sgf_tool/
builder.rs

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}