1use std::fmt::Write;
3
4use include_doc::function_body;
5use silkenweb_signals_ext::value::Value;
6
7use crate::attribute::{AsAttribute, Attribute};
8
9#[derive(Copy, Clone)]
11pub enum Offset {
12 Abs,
13 Rel,
14}
15
16#[doc = function_body!("tests/doc/elements/svg.rs", path_data, [])]
22#[derive(Default, Clone)]
25pub struct Data(String);
26
27impl Data {
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn new_with_capacity(capacity: usize) -> Self {
33 Self(String::with_capacity(capacity))
34 }
35
36 pub fn move_to(self, offset: Offset, x: f64, y: f64) -> Self {
40 self.cmd2(offset, 'm', [(x, y)])
41 }
42
43 pub fn lines_to(self, offset: Offset, ends: impl IntoIterator<Item = (f64, f64)>) -> Self {
47 self.cmd2(offset, 'l', ends)
48 }
49
50 pub fn horizontal_lines_to(self, offset: Offset, ends: impl IntoIterator<Item = f64>) -> Self {
54 self.cmd1(offset, 'h', ends)
55 }
56
57 pub fn vertical_lines_to(self, offset: Offset, ends: impl IntoIterator<Item = f64>) -> Self {
61 self.cmd1(offset, 'v', ends)
62 }
63
64 pub fn cubic_bezier_curves(
68 self,
69 offset: Offset,
70 args: impl IntoIterator<Item = (f64, f64, f64, f64, f64, f64)>,
71 ) -> Self {
72 self.cmd6(offset, 'c', args)
73 }
74
75 pub fn smooth_cubic_bezier_curves(
79 self,
80 offset: Offset,
81 args: impl IntoIterator<Item = (f64, f64, f64, f64)>,
82 ) -> Self {
83 self.cmd4(offset, 's', args)
84 }
85
86 pub fn quadradic_bezier_curves(
90 self,
91 offset: Offset,
92 args: impl IntoIterator<Item = (f64, f64, f64, f64)>,
93 ) -> Self {
94 self.cmd4(offset, 'q', args)
95 }
96
97 pub fn smooth_quadradic_bezier_curves(
101 self,
102 offset: Offset,
103 end_points: impl IntoIterator<Item = (f64, f64)>,
104 ) -> Self {
105 self.cmd2(offset, 't', end_points)
106 }
107
108 pub fn elliptical_arc_curves(
112 self,
113 offset: Offset,
114 args: impl IntoIterator<Item = (f64, f64, f64, f64, f64, f64, f64)>,
115 ) -> Self {
116 self.cmd7(offset, 'a', args)
117 }
118
119 fn cmd1(self, offset: Offset, cmd: char, cmds: impl IntoIterator<Item = f64>) -> Self {
120 self.cmd(offset, cmd, cmds.into_iter().map(|x| [x]))
121 }
122
123 fn cmd2(self, offset: Offset, cmd: char, cmds: impl IntoIterator<Item = (f64, f64)>) -> Self {
124 self.cmd(offset, cmd, cmds.into_iter().map(|(x0, x1)| [x0, x1]))
125 }
126
127 fn cmd4(
128 self,
129 offset: Offset,
130 cmd: char,
131 cmds: impl IntoIterator<Item = (f64, f64, f64, f64)>,
132 ) -> Self {
133 self.cmd(
134 offset,
135 cmd,
136 cmds.into_iter().map(|(x0, x1, x2, x3)| [x0, x1, x2, x3]),
137 )
138 }
139
140 fn cmd6(
141 self,
142 offset: Offset,
143 cmd: char,
144 cmds: impl IntoIterator<Item = (f64, f64, f64, f64, f64, f64)>,
145 ) -> Self {
146 self.cmd(
147 offset,
148 cmd,
149 cmds.into_iter()
150 .map(|(x0, x1, x2, x3, x4, x5)| [x0, x1, x2, x3, x4, x5]),
151 )
152 }
153
154 fn cmd7(
155 self,
156 offset: Offset,
157 cmd: char,
158 cmds: impl IntoIterator<Item = (f64, f64, f64, f64, f64, f64, f64)>,
159 ) -> Self {
160 self.cmd(
161 offset,
162 cmd,
163 cmds.into_iter()
164 .map(|(x0, x1, x2, x3, x4, x5, x6)| [x0, x1, x2, x3, x4, x5, x6]),
165 )
166 }
167
168 fn cmd<const COUNT: usize>(
169 mut self,
170 offset: Offset,
171 cmd: char,
172 cmds: impl IntoIterator<Item = [f64; COUNT]>,
173 ) -> Self {
174 let cmd = match offset {
175 Offset::Abs => cmd.to_ascii_uppercase(),
176 Offset::Rel => cmd.to_ascii_lowercase(),
177 };
178 let mut cmds = cmds.into_iter().peekable();
179
180 if cmds.peek().is_some() {
181 if !self.0.is_empty() {
182 self.0.push(' ');
183 }
184
185 self.0.push(cmd);
186
187 for args in cmds {
188 let mut args = args.into_iter();
189 write!(&mut self.0, " {}", args.next().unwrap()).unwrap();
190
191 for arg in args {
192 write!(&mut self.0, ",{arg}").unwrap();
193 }
194 }
195 }
196
197 self
198 }
199}
200
201impl AsAttribute<Data> for Data {}
202impl AsAttribute<Data> for String {}
203impl AsAttribute<Data> for &str {}
204
205impl Attribute for Data {
206 type Text<'a> = &'a str;
207
208 fn text(&self) -> Option<Self::Text<'_>> {
209 Some(self.0.as_str())
210 }
211}
212
213impl Value for Data {}