tangled
alpha
login
or
join now
kokirigla.de
/
nara
0
fork
atom
online Minecraft written book viewer
0
fork
atom
overview
issues
pulls
pipelines
feat(text): improve a lot
kokirigla.de
3 weeks ago
b0a5c7d1
33e72147
verified
This commit was signed with the committer's
known signature
.
kokirigla.de
SSH Key Fingerprint:
SHA256:BlSEtD3ZoKT3iKveofI8gba+lZ9CEolKRM1Pzy3pAwg=
+201
-61
2 changed files
expand all
collapse all
unified
split
nara_text
src
color.rs
lib.rs
+165
nara_text/src/color.rs
reviewed
···
1
1
+
use serde::{Deserialize, Serialize};
2
2
+
3
3
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4
4
+
#[serde(rename_all = "snake_case")]
5
5
+
pub enum NamedColor {
6
6
+
Black,
7
7
+
DarkBlue,
8
8
+
DarkGreen,
9
9
+
DarkAqua,
10
10
+
DarkRed,
11
11
+
DarkPurple,
12
12
+
Gold,
13
13
+
Gray,
14
14
+
DarkGray,
15
15
+
Blue,
16
16
+
Green,
17
17
+
Aqua,
18
18
+
Red,
19
19
+
LightPurple,
20
20
+
Yellow,
21
21
+
White,
22
22
+
}
23
23
+
24
24
+
impl NamedColor {
25
25
+
pub fn as_hex(&self) -> [u8; 3] {
26
26
+
match self {
27
27
+
NamedColor::Black => [0x00, 0x00, 0x00],
28
28
+
NamedColor::DarkBlue => [0x00, 0x00, 0xAA],
29
29
+
NamedColor::DarkGreen => [0x00, 0xAA, 0x00],
30
30
+
NamedColor::DarkAqua => [0x00, 0xAA, 0xAA],
31
31
+
NamedColor::DarkRed => [0xAA, 0x00, 0x00],
32
32
+
NamedColor::DarkPurple => [0xAA, 0x00, 0xAA],
33
33
+
NamedColor::Gold => [0xFF, 0xAA, 0x00],
34
34
+
NamedColor::Gray => [0xAA, 0xAA, 0xAA],
35
35
+
NamedColor::DarkGray => [0x55, 0x55, 0x55],
36
36
+
NamedColor::Blue => [0x55, 0x55, 0xFF],
37
37
+
NamedColor::Green => [0x55, 0xFF, 0x55],
38
38
+
NamedColor::Aqua => [0x55, 0xFF, 0xFF],
39
39
+
NamedColor::Red => [0xFF, 0x55, 0x55],
40
40
+
NamedColor::LightPurple => [0xFF, 0x55, 0xFF],
41
41
+
NamedColor::Yellow => [0xFF, 0xFF, 0x55],
42
42
+
NamedColor::White => [0xFF, 0xFF, 0xFF],
43
43
+
}
44
44
+
}
45
45
+
46
46
+
pub fn as_hex_string(&self) -> String {
47
47
+
hex_bytes_to_string(self.as_hex())
48
48
+
}
49
49
+
50
50
+
pub fn from_hex(hex: [u8; 3]) -> Option<Self> {
51
51
+
match hex {
52
52
+
[0x00, 0x00, 0x00] => Some(NamedColor::Black),
53
53
+
[0x00, 0x00, 0xAA] => Some(NamedColor::DarkBlue),
54
54
+
[0x00, 0xAA, 0x00] => Some(NamedColor::DarkGreen),
55
55
+
[0x00, 0xAA, 0xAA] => Some(NamedColor::DarkAqua),
56
56
+
[0xAA, 0x00, 0x00] => Some(NamedColor::DarkRed),
57
57
+
[0xAA, 0x00, 0xAA] => Some(NamedColor::DarkPurple),
58
58
+
[0xFF, 0xAA, 0x00] => Some(NamedColor::Gold),
59
59
+
[0xAA, 0xAA, 0xAA] => Some(NamedColor::Gray),
60
60
+
[0x55, 0x55, 0x55] => Some(NamedColor::DarkGray),
61
61
+
[0x55, 0x55, 0xFF] => Some(NamedColor::Blue),
62
62
+
[0x55, 0xFF, 0x55] => Some(NamedColor::Green),
63
63
+
[0x55, 0xFF, 0xFF] => Some(NamedColor::Aqua),
64
64
+
[0xFF, 0x55, 0x55] => Some(NamedColor::Red),
65
65
+
[0xFF, 0x55, 0xFF] => Some(NamedColor::LightPurple),
66
66
+
[0xFF, 0xFF, 0x55] => Some(NamedColor::Yellow),
67
67
+
[0xFF, 0xFF, 0xFF] => Some(NamedColor::White),
68
68
+
_ => None,
69
69
+
}
70
70
+
}
71
71
+
72
72
+
pub fn from_name(s: &str) -> Option<Self> {
73
73
+
let normalized = s.trim().to_ascii_lowercase().replace([' ', '-'], "_");
74
74
+
75
75
+
match normalized.as_str() {
76
76
+
"black" => Some(NamedColor::Black),
77
77
+
"dark_blue" => Some(NamedColor::DarkBlue),
78
78
+
"dark_green" => Some(NamedColor::DarkGreen),
79
79
+
"dark_aqua" => Some(NamedColor::DarkAqua),
80
80
+
"dark_red" => Some(NamedColor::DarkRed),
81
81
+
"dark_purple" => Some(NamedColor::DarkPurple),
82
82
+
"gold" => Some(NamedColor::Gold),
83
83
+
"gray" | "grey" => Some(NamedColor::Gray),
84
84
+
"dark_gray" | "dark_grey" => Some(NamedColor::DarkGray),
85
85
+
"blue" => Some(NamedColor::Blue),
86
86
+
"green" => Some(NamedColor::Green),
87
87
+
"aqua" => Some(NamedColor::Aqua),
88
88
+
"red" => Some(NamedColor::Red),
89
89
+
"light_purple" => Some(NamedColor::LightPurple),
90
90
+
"yellow" => Some(NamedColor::Yellow),
91
91
+
"white" => Some(NamedColor::White),
92
92
+
_ => None,
93
93
+
}
94
94
+
}
95
95
+
}
96
96
+
97
97
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98
98
+
#[serde(try_from = "String", into = "String")]
99
99
+
pub enum Color {
100
100
+
Named(NamedColor),
101
101
+
Hex(String),
102
102
+
}
103
103
+
104
104
+
impl Color {
105
105
+
pub fn as_hex(&self) -> Result<[u8; 3], String> {
106
106
+
match self {
107
107
+
Color::Named(named) => Ok(named.as_hex()),
108
108
+
Color::Hex(hex) => hex_string_to_bytes(hex),
109
109
+
}
110
110
+
}
111
111
+
112
112
+
pub fn as_hex_string(&self) -> String {
113
113
+
match self {
114
114
+
Color::Named(named) => named.as_hex_string(),
115
115
+
Color::Hex(hex) => match hex_string_to_bytes(hex) {
116
116
+
Ok(bytes) => hex_bytes_to_string(bytes),
117
117
+
Err(_) => hex.clone(),
118
118
+
},
119
119
+
}
120
120
+
}
121
121
+
}
122
122
+
123
123
+
impl TryFrom<String> for Color {
124
124
+
type Error = String;
125
125
+
126
126
+
fn try_from(value: String) -> Result<Self, Self::Error> {
127
127
+
if let Some(named) = NamedColor::from_name(&value) {
128
128
+
return Ok(Color::Named(named));
129
129
+
}
130
130
+
131
131
+
let bytes = hex_string_to_bytes(&value)?;
132
132
+
if let Some(named) = NamedColor::from_hex(bytes) {
133
133
+
Ok(Color::Named(named))
134
134
+
} else {
135
135
+
Ok(Color::Hex(hex_bytes_to_string(bytes)))
136
136
+
}
137
137
+
}
138
138
+
}
139
139
+
140
140
+
impl From<Color> for String {
141
141
+
fn from(value: Color) -> Self {
142
142
+
value.as_hex_string()
143
143
+
}
144
144
+
}
145
145
+
146
146
+
fn hex_string_to_bytes(s: &str) -> Result<[u8; 3], String> {
147
147
+
let s = s.trim().strip_prefix('#').unwrap_or(s.trim());
148
148
+
149
149
+
if s.len() != 6 {
150
150
+
return Err(format!("expected 6 hex characters, got {}", s.len()));
151
151
+
}
152
152
+
153
153
+
let r = u8::from_str_radix(&s[0..2], 16)
154
154
+
.map_err(|_| "invalid red channel".to_string())?;
155
155
+
let g = u8::from_str_radix(&s[2..4], 16)
156
156
+
.map_err(|_| "invalid green channel".to_string())?;
157
157
+
let b = u8::from_str_radix(&s[4..6], 16)
158
158
+
.map_err(|_| "invalid blue channel".to_string())?;
159
159
+
160
160
+
Ok([r, g, b])
161
161
+
}
162
162
+
163
163
+
fn hex_bytes_to_string(bytes: [u8; 3]) -> String {
164
164
+
format!("#{:02X}{:02X}{:02X}", bytes[0], bytes[1], bytes[2])
165
165
+
}
+36
-61
nara_text/src/lib.rs
reviewed
···
1
1
-
#[derive(Debug)]
1
1
+
use serde::{Deserialize, Serialize};
2
2
+
3
3
+
pub mod color;
4
4
+
5
5
+
#[derive(Debug, Serialize, Deserialize)]
6
6
+
#[serde(untagged)]
2
7
pub enum Component {
3
8
String(String),
4
9
Object(ComponentObject),
5
10
Array(Vec<Component>),
6
11
}
7
12
8
8
-
#[derive(Debug, Clone, Copy)]
9
9
-
pub enum NamedColor {
10
10
-
Black,
11
11
-
DarkBlue,
12
12
-
DarkGreen,
13
13
-
DarkAqua,
14
14
-
DarkRed,
15
15
-
DarkPurple,
16
16
-
Gold,
17
17
-
Gray,
18
18
-
DarkGray,
19
19
-
Blue,
20
20
-
Green,
21
21
-
Aqua,
22
22
-
Red,
23
23
-
LightPurple,
24
24
-
Yellow,
25
25
-
White,
13
13
+
#[serde_with::skip_serializing_none]
14
14
+
#[derive(Debug, Serialize, Deserialize)]
15
15
+
pub struct ComponentObject {
16
16
+
#[serde(flatten)]
17
17
+
text: Option<TextComponent>,
18
18
+
#[serde(flatten)]
19
19
+
translation: Option<TranslationComponent>,
20
20
+
#[serde(flatten)]
21
21
+
formatting: ComponentFormatting,
22
22
+
#[serde(default, rename = "extra", skip_serializing_if = "Vec::is_empty")]
23
23
+
children: Vec<Component>,
26
24
}
27
25
28
28
-
impl NamedColor {
29
29
-
fn as_hex_string(&self) -> String {
30
30
-
match self {
31
31
-
NamedColor::Black => "#000000".to_string(),
32
32
-
NamedColor::DarkBlue => "#0000AA".to_string(),
33
33
-
NamedColor::DarkGreen => "#00AA00".to_string(),
34
34
-
NamedColor::DarkAqua => "#00AAAA".to_string(),
35
35
-
NamedColor::DarkRed => "#AA0000".to_string(),
36
36
-
NamedColor::DarkPurple => "#AA00AA".to_string(),
37
37
-
NamedColor::Gold => "#FFAA00".to_string(),
38
38
-
NamedColor::Gray => "#AAAAAA".to_string(),
39
39
-
NamedColor::DarkGray => "#555555".to_string(),
40
40
-
NamedColor::Blue => "#5555FF".to_string(),
41
41
-
NamedColor::Green => "#55FF55".to_string(),
42
42
-
NamedColor::Aqua => "#55FFFF".to_string(),
43
43
-
NamedColor::Red => "#FF5555".to_string(),
44
44
-
NamedColor::LightPurple => "#FF55FF".to_string(),
45
45
-
NamedColor::Yellow => "#FFFF55".to_string(),
46
46
-
NamedColor::White => "#FFFFFF".to_string(),
47
47
-
}
48
48
-
}
49
49
-
}
50
50
-
51
51
-
#[derive(Debug, Clone)]
52
52
-
pub enum Color {
53
53
-
Named(NamedColor),
54
54
-
Hex(String),
55
55
-
}
56
56
-
57
57
-
impl Color {
58
58
-
fn as_hex_string(&self) -> String {
59
59
-
match self {
60
60
-
Color::Named(named_color) => todo!(),
61
61
-
Color::Hex(hex) => hex.clone(),
62
62
-
}
63
63
-
}
64
64
-
65
65
-
#[derive(Debug)]
66
66
-
pub struct ComponentObject {
67
67
-
extra: Option<Vec<Component>>,
68
68
-
color: Option<Color>,
69
69
-
font: Option<String>,
26
26
+
#[serde_with::skip_serializing_none]
27
27
+
#[derive(Debug, Serialize, Deserialize)]
28
28
+
pub struct ComponentFormatting {
29
29
+
color: Option<color::Color>,
30
30
+
font: Option<String>, // todo(kokiriglade): an Identifier struct? or is that too over-enginereed?
70
31
bold: Option<bool>,
71
32
italic: Option<bool>,
72
33
underlined: Option<bool>,
73
34
strikethrough: Option<bool>,
74
35
obfuscated: Option<bool>,
75
36
}
37
37
+
38
38
+
#[derive(Debug, Serialize, Deserialize)]
39
39
+
pub struct TextComponent {
40
40
+
text: String,
41
41
+
}
42
42
+
43
43
+
#[serde_with::skip_serializing_none]
44
44
+
#[derive(Debug, Serialize, Deserialize)]
45
45
+
pub struct TranslationComponent {
46
46
+
translate: String,
47
47
+
fallback: Option<String>,
48
48
+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
49
49
+
with: Vec<Component>,
50
50
+
}