sgf_parse/props/
values.rs1use std::str::FromStr;
2
3use super::SgfPropError;
4
5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
7pub enum Color {
8 Black,
9 White,
10}
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq)]
14pub enum Double {
15 One,
16 Two,
17}
18
19#[derive(Clone, Debug, Eq, PartialEq, Hash)]
32pub struct SimpleText {
33 pub text: String,
34}
35
36#[derive(Clone, Debug, Eq, PartialEq, Hash)]
48pub struct Text {
49 pub text: String,
50}
51
52#[derive(Copy, Clone, Debug, Eq, PartialEq)]
54pub enum PropertyType {
55 Move,
56 Setup,
57 Root,
58 GameInfo,
59 Inherit,
60}
61
62impl FromStr for Double {
63 type Err = SgfPropError;
64
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 if s == "1" {
67 Ok(Self::One)
68 } else if s == "2" {
69 Ok(Self::Two)
70 } else {
71 Err(SgfPropError {})
72 }
73 }
74}
75
76impl FromStr for Color {
77 type Err = SgfPropError;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 if s == "B" {
81 Ok(Self::Black)
82 } else if s == "W" {
83 Ok(Self::White)
84 } else {
85 Err(SgfPropError {})
86 }
87 }
88}
89
90impl std::convert::From<&str> for SimpleText {
91 fn from(s: &str) -> Self {
92 Self { text: s.to_owned() }
93 }
94}
95
96impl std::convert::From<&str> for Text {
97 fn from(s: &str) -> Self {
98 Self { text: s.to_owned() }
99 }
100}
101
102impl FromStr for SimpleText {
103 type Err = SgfPropError;
104
105 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 Ok(s.into())
107 }
108}
109
110impl FromStr for Text {
111 type Err = SgfPropError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 Ok(s.into())
115 }
116}
117
118impl std::fmt::Display for SimpleText {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 let text = format_text(&self.text)
121 .replace("\r\n", " ")
122 .replace("\n\r", " ")
123 .replace(['\n', '\r'], " ");
124 f.write_str(&text)
125 }
126}
127
128impl std::fmt::Display for Text {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 let text = format_text(&self.text);
131 f.write_str(&text)
132 }
133}
134
135fn format_text(s: &str) -> String {
136 let mut output = vec![];
138 let chars: Vec<char> = s.chars().collect();
139 let mut i = 0;
140 while i < chars.len() {
141 let c = chars[i];
142 if c == '\\' && i + 1 < chars.len() {
143 i += 1;
144
145 if chars[i] == '\n' {
147 if i + 1 < chars.len() && chars[i + 1] == '\r' {
148 i += 1;
149 }
150 } else if chars[i] == '\r' {
151 if i + 1 < chars.len() && chars[i + 1] == '\n' {
152 i += 1;
153 }
154 } else {
155 output.push(chars[i]);
157 }
158 } else if c.is_whitespace() && c != '\r' && c != '\n' {
159 if i + 1 < chars.len() {
160 let next = chars[i + 1];
161 if (c == '\n' && next == '\r') || (c == '\r' && next == '\n') {
163 i += 1;
164 }
165 }
166 output.push(' ');
168 } else {
169 output.push(c);
170 }
171 i += 1;
172 }
173
174 output.into_iter().collect()
175}
176
177#[cfg(test)]
178mod test {
179 #[test]
180 pub fn format_text() {
181 let text = super::Text {
182 text: "Comment with\trandom whitespace\nescaped \\] and \\\\ and a soft \\\nlinebreak"
183 .to_string(),
184 };
185 let expected = "Comment with random whitespace\nescaped ] and \\ and a soft linebreak";
186
187 assert_eq!(format!("{}", text), expected);
188 }
189
190 #[test]
191 pub fn format_simple_text() {
192 let text = super::SimpleText { text:
193 "Comment with\trandom\r\nwhitespace\n\rescaped \\] and \\\\ and\na soft \\\nlinebreak"
194 .to_string()
195 };
196 let expected = "Comment with random whitespace escaped ] and \\ and a soft linebreak";
197
198 assert_eq!(format!("{}", text), expected);
199 }
200}