1use crate::{Base, Figure, Move, Player, Point, PointRange, PointText, SgfToolError, Token};
2use std::string::String;
3
4use strum::EnumMessage;
5
6pub trait Builder {
7 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError>;
8}
9
10impl Builder for Point<'_> {
11 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
12 buffer.push_str(self.0);
13 Ok(())
14 }
15}
16
17impl Builder for Move<'_> {
18 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
19 match &self {
20 Move::Move(point) => point.build(buffer)?,
21 Move::Pass => buffer.push_str(""),
22 };
23
24 Ok(())
25 }
26}
27
28impl Builder for PointRange<'_> {
29 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
30 self.0.build(buffer)?;
31 buffer.push(':');
32 self.1.build(buffer)?;
33 Ok(())
34 }
35}
36
37impl Builder for Figure<'_> {
38 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
39 buffer.push_str(&self.0.to_string());
40 buffer.push(':');
41 buffer.push_str(self.1);
42 Ok(())
43 }
44}
45
46impl Builder for PointText<'_> {
47 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
48 self.0.build(buffer)?;
49 buffer.push(':');
50 buffer.push_str(self.1);
51 Ok(())
52 }
53}
54
55impl Builder for Player {
56 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
57 buffer.push(match self {
58 Player::Black => 'B',
59 Player::White => 'W',
60 });
61 Ok(())
62 }
63}
64
65impl Builder for Base<'_> {
66 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
67 buffer.push('(');
68 for node in self.tokens.iter() {
69 node.build(buffer)?;
70 }
71 buffer.push(')');
72 Ok(())
73 }
74}
75
76impl Builder for &'_ str {
77 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
78 buffer.push_str(self);
79 Ok(())
80 }
81}
82
83impl<T: Builder> Builder for Vec<T> {
84 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
85 for point in self.iter() {
86 buffer.push('[');
87 point.build(buffer)?;
88 buffer.push(']');
89 }
90 Ok(())
91 }
92}
93
94impl Builder for Option<Figure<'_>> {
95 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
96 if let Some(item) = self {
97 item.build(buffer)?;
98 }
99
100 Ok(())
101 }
102}
103
104impl Builder for usize {
105 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
106 buffer.push_str(&self.to_string());
107 Ok(())
108 }
109}
110
111impl Builder for f32 {
112 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
113 buffer.push_str(&self.to_string());
114 Ok(())
115 }
116}
117
118impl Builder for Token<'_> {
119 fn build(&self, buffer: &mut String) -> Result<(), SgfToolError> {
120 let node_type = self.get_message();
121
122 match self {
123 Token::Unknown(item) => add_node(buffer, node_type, item),
124 Token::Application(application) => add_node(buffer, node_type, application),
125 Token::Comment(comment) => add_node(buffer, node_type, comment),
126 Token::Copyright(copyright) => add_node(buffer, node_type, copyright),
127 Token::BlackName(name) => add_node(buffer, node_type, name),
128 Token::WhiteName(name) => add_node(buffer, node_type, name),
129 Token::BlackTeam(name) => add_node(buffer, node_type, name),
130 Token::WhiteTeam(name) => add_node(buffer, node_type, name),
131 Token::BoardSize(size, _) => add_node(buffer, node_type, size),
132 Token::Variation(variant) => variant.build(buffer),
133 Token::FileFormat(format) => add_node(buffer, node_type, format),
134 Token::GameType(game) => add_node(buffer, node_type, game),
135 Token::Charset(charset) => add_node(buffer, node_type, charset),
136 Token::VariationShown(show) => add_node(buffer, node_type, show),
137 Token::WhoseTurn(turn) => add_node(buffer, node_type, turn),
138 Token::BlackStones(stones) => add_multiple_nodes(buffer, node_type, stones),
139 Token::WhiteStones(stones) => add_multiple_nodes(buffer, node_type, stones),
140 Token::BlackMove(point) => add_node(buffer, node_type, point),
141 Token::WhiteMove(point) => add_node(buffer, node_type, point),
142 Token::BlackPlayerRank(rank) => add_node(buffer, node_type, rank),
143 Token::WhitePlayerRank(rank) => add_node(buffer, node_type, rank),
144 Token::Source(source) => add_node(buffer, node_type, source),
145 Token::GameName(name) => add_node(buffer, node_type, name),
146 Token::NodeName(name) => add_node(buffer, node_type, name),
147 Token::Rule(rule) => add_node(buffer, node_type, rule),
148 Token::Komi(komi) => add_node(buffer, node_type, komi),
149 Token::PersonWhoProvidesAnnotations(person) => add_node(buffer, node_type, person),
150 Token::DrawArrow(point_range) => add_node(buffer, node_type, point_range),
151 Token::DrawCircle(points) => add_multiple_nodes(buffer, node_type, points),
152 Token::DrawSquare(item) => add_multiple_nodes(buffer, node_type, item),
153 Token::DrawTriangle(item) => add_multiple_nodes(buffer, node_type, item),
154 Token::GreyOut(item) => add_multiple_nodes(buffer, node_type, item),
155 Token::MarkX(item) => add_multiple_nodes(buffer, node_type, item),
156 Token::Handicap(item) => add_node(buffer, node_type, item),
157 Token::Result(item) => add_node(buffer, node_type, item),
158 Token::Figure(item) => add_node(buffer, node_type, item),
159 Token::Printing(item) => add_node(buffer, node_type, item),
160 Token::TimeLimit(item) => add_node(buffer, node_type, item),
161 Token::Date(item) => add_node(buffer, node_type, item),
162 Token::Event(item) => add_node(buffer, node_type, item),
163 Token::PointText(item) => add_multiple_nodes(buffer, node_type, item),
164 Token::Round(item) => add_node(buffer, node_type, item),
165 Token::SGFCreator(item) => add_node(buffer, node_type, item),
166 Token::ViewOnly(item) => add_multiple_nodes(buffer, node_type, item),
167 Token::MoveNumber(item) => add_node(buffer, node_type, item),
168 }
169 }
170}
171
172fn add_node<T: Builder + ?Sized>(
173 buffer: &mut String,
174 node_type: Option<&'static str>,
175 node_value: &T,
176) -> Result<(), SgfToolError> {
177 if let Some(message) = node_type {
178 buffer.push(';');
179 buffer.push_str(message);
180 buffer.push('[');
181 node_value.build(buffer)?;
182 buffer.push(']');
183 }
184 Ok(())
185}
186
187fn add_multiple_nodes<T: Builder + ?Sized>(
188 buffer: &mut String,
189 node_type: Option<&'static str>,
190 node_value: &T,
191) -> Result<(), SgfToolError> {
192 if let Some(message) = node_type {
193 buffer.push(';');
194 buffer.push_str(message);
195 node_value.build(buffer)?;
196 }
197 Ok(())
198}
199
200pub fn build(object: Base<'_>) -> Result<String, SgfToolError> {
201 let mut buffer = String::new();
202 object.build(&mut buffer)?;
203 Ok(buffer)
204}