This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1use std::{collections::HashMap, fmt::Display}; 2 3use itertools::Itertools; 4 5use crate::{Color, ColorMapping, Point, Region}; 6 7#[derive(Debug, Clone)] 8pub struct Element { 9 pub tag: String, 10 pub attributes: HashMap<String, String>, 11 pub styles: HashMap<String, String>, 12 pub children: Vec<Node>, 13} 14 15#[derive(Debug, Clone)] 16pub enum Node { 17 Element(Element), 18 Text(String), 19 SVG(String), 20} 21 22impl Into<Node> for Element { 23 fn into(self) -> Node { 24 self.node() 25 } 26} 27 28pub fn tag(tag: &str) -> Element { 29 Element::new(tag) 30} 31 32impl Element { 33 pub fn node(self) -> Node { 34 Node::Element(self) 35 } 36 37 pub fn new(tag: &str) -> Self { 38 Element { 39 tag: tag.to_string(), 40 attributes: HashMap::new(), 41 styles: HashMap::new(), 42 children: Vec::new(), 43 } 44 } 45 46 pub fn attr(self, key: &str, value: impl Display) -> Self { 47 // assert!( 48 // key != "style", 49 // "Use `style` method instead of `attr` for style attributes." 50 // ); 51 let mut attributes = self.attributes.clone(); 52 attributes.insert(key.to_string(), value.to_string()); 53 Element { attributes, ..self } 54 } 55 56 /// Sets x and y 57 pub fn coords(self, p: impl Into<(f32, f32)>) -> Self { 58 let (x, y) = p.into(); 59 self.attr("x", x).attr("y", y) 60 } 61 62 pub fn fill(self, c: Color, colormap: &ColorMapping) -> Self { 63 self.attr("fill", c.render(colormap)) 64 } 65 66 /// Sets cx and cy 67 pub fn center_position(self, p: impl Into<Point>, cell_size: usize) -> Self { 68 let (x, y) = p.into().coords(cell_size); 69 self.attr("cx", x).attr("cy", y) 70 } 71 72 /// Sets x1, y1 and x2, y2 73 pub fn position_pair( 74 self, 75 p1: impl Into<Point>, 76 p2: impl Into<Point>, 77 cell_size: usize, 78 ) -> Self { 79 let (x1, y1) = p1.into().coords(cell_size); 80 let (x2, y2) = p2.into().coords(cell_size); 81 self.attr("x1", x1) 82 .attr("y1", y1) 83 .attr("x2", x2) 84 .attr("y2", y2) 85 } 86 87 /// Sets x and y 88 pub fn position(self, p: impl Into<Point>, cell_size: usize) -> Self { 89 self.coords(p.into().coords(cell_size)) 90 } 91 92 /// Sets width and height 93 pub fn dimensions(self, p: impl Into<(usize, usize)>) -> Self { 94 let (w, h) = p.into(); 95 self.attr("width", w).attr("height", h) 96 } 97 98 /// Sets width and height 99 pub fn size(self, r: impl Into<Region>, cell_size: usize) -> Self { 100 self.dimensions(r.into().size(cell_size)) 101 } 102 103 /// Sets x, y, width and height according to the region 104 pub fn region(self, r: impl Into<Region>, cell_size: usize) -> Self { 105 let region: Region = r.into(); 106 self.position(region.start, cell_size) 107 .size(region, cell_size) 108 } 109 110 pub fn style(self, key: &str, value: &str) -> Self { 111 let mut styles = self.styles.clone(); 112 styles.insert(key.to_string(), value.to_string()); 113 Element { styles, ..self } 114 } 115 116 pub fn dataset(self, key: &str, value: &str) -> Self { 117 let mut attributes = self.attributes.clone(); 118 attributes.insert(format!("data-{key}"), value.to_string()); 119 Element { attributes, ..self } 120 } 121 122 pub fn class(self, class: &str) -> Self { 123 self.attr("class", class) 124 } 125 126 pub fn add(&mut self, child: impl Into<Node>) -> &mut Self { 127 self.children.push(child.into()); 128 self 129 } 130 131 pub fn with_attributes(self, attributes: HashMap<String, String>) -> Self { 132 Element { attributes, ..self } 133 } 134 135 pub fn wrapping( 136 self, 137 children: impl IntoIterator<Item = impl Into<Node>>, 138 ) -> Self { 139 Element { 140 children: children.into_iter().map(|n| n.into()).collect(), 141 ..self 142 } 143 } 144 145 pub fn wrap(self, tag: &str, attrs: HashMap<String, String>) -> Self { 146 Element { 147 tag: tag.to_string(), 148 styles: HashMap::new(), 149 attributes: attrs, 150 children: vec![Node::Element(self)], 151 } 152 } 153} 154 155pub enum PathInstruction { 156 MoveTo((f32, f32)), 157 LineTo((f32, f32)), 158 HorizontalLineTo(f32), 159 VerticalLineTo(f32), 160 CurveTo((f32, f32), (f32, f32), (f32, f32)), 161 SmoothCurveTo((f32, f32), (f32, f32)), 162 QuadraticCurveTo((f32, f32), (f32, f32)), 163 SmoothQuadraticCurveTo((f32, f32)), 164 ArcTo((f32, f32), f32, bool, bool, (f32, f32)), 165 ClosePath, 166} 167 168pub struct Path(Vec<PathInstruction>); 169 170impl Path { 171 pub fn new() -> Self { 172 Path(Vec::new()) 173 } 174 175 pub fn node(self) -> Node { 176 self.element().node() 177 } 178 179 pub fn element(self) -> Element { 180 tag("path").attr("d", self.to_string()) 181 } 182 183 pub fn move_to( 184 &mut self, 185 p: impl Into<Point>, 186 cell_size: usize, 187 ) -> &mut Self { 188 self.0 189 .push(PathInstruction::MoveTo(p.into().coords(cell_size))); 190 self 191 } 192 193 pub fn line_to( 194 &mut self, 195 p: impl Into<Point>, 196 cell_size: usize, 197 ) -> &mut Self { 198 self.0 199 .push(PathInstruction::LineTo(p.into().coords(cell_size))); 200 self 201 } 202 203 pub fn quadratic_curve_to( 204 &mut self, 205 control: impl Into<(f32, f32)>, 206 end: impl Into<Point>, 207 cell_size: usize, 208 ) -> &mut Self { 209 self.0.push(PathInstruction::QuadraticCurveTo( 210 control.into(), 211 end.into().coords(cell_size), 212 )); 213 self 214 } 215 216 pub fn close(&mut self) -> &mut Self { 217 self.0.push(PathInstruction::ClosePath); 218 self 219 } 220} 221 222impl Display for Path { 223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 224 f.write_str( 225 &self 226 .0 227 .iter() 228 .map(|i| i.to_string()) 229 .collect::<Vec<_>>() 230 .join(" "), 231 ) 232 } 233} 234 235impl Display for PathInstruction { 236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 237 match self { 238 Self::MoveTo((x, y)) => write!(f, "M {} {}", x, y), 239 Self::LineTo((x, y)) => write!(f, "L {} {}", x, y), 240 Self::HorizontalLineTo(x) => write!(f, "H {}", x), 241 Self::VerticalLineTo(y) => write!(f, "V {}", y), 242 Self::CurveTo((x1, y1), (x2, y2), (x3, y3)) => { 243 write!(f, "C {} {} {} {} {} {}", x1, y1, x2, y2, x3, y3) 244 } 245 Self::SmoothCurveTo((x2, y2), (x3, y3)) => { 246 write!(f, "S {} {} {} {}", x2, y2, x3, y3) 247 } 248 Self::QuadraticCurveTo((x1, y1), (x2, y2)) => { 249 write!(f, "Q {} {} {} {}", x1, y1, x2, y2) 250 } 251 Self::SmoothQuadraticCurveTo((x2, y2)) => { 252 write!(f, "T {} {}", x2, y2) 253 } 254 Self::ArcTo( 255 (rx, ry), 256 angle, 257 large_arc_flag, 258 sweep_flag, 259 (x2, y2), 260 ) => { 261 write!( 262 f, 263 "A {rx} {ry} {angle} {large_arc_flag} {sweep_flag} {x2} {y2}" 264 ) 265 } 266 Self::ClosePath => write!(f, "Z"), 267 } 268 } 269} 270 271fn space_if(add_space: bool) -> &'static str { 272 if add_space { 273 " " 274 } else { 275 "" 276 } 277} 278 279impl Display for Node { 280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 281 match self { 282 Node::Text(text) => write!(f, "{}", quick_xml::escape::escape(text)), 283 Node::SVG(svg) => write!(f, "{}", svg), 284 Node::Element(Element { 285 tag, 286 attributes, 287 styles, 288 children, 289 }) => { 290 write!(f, "<{tag} ")?; 291 292 let non_style_attributes: Vec<_> = attributes 293 .iter() 294 .filter(|(k, _)| *k != "style") 295 .sorted_by_key(|(k, _)| *k) 296 .collect(); 297 298 for (i, (key, value)) in non_style_attributes.iter().enumerate() { 299 write!( 300 f, 301 r#"{spacing}{key}="{value}""#, 302 spacing = space_if(i > 0), 303 key = key, 304 value = value 305 .replace("&", "&amp;") 306 .replace('"', "&quot;") 307 .replace("'", "&apos;") 308 )?; 309 } 310 311 if attributes.contains_key("style") || !styles.is_empty() { 312 write!( 313 f, 314 r#"{spacing}style="{value}""#, 315 spacing = space_if(non_style_attributes.len() > 0), 316 value = styles 317 .iter() 318 .map(|(k, v)| format!("{k}: {v};")) 319 .chain::<Option<String>>( 320 attributes.get("style").map(|s| s.to_string()), 321 ) 322 .collect::<Vec<_>>() 323 .join(" ") 324 )?; 325 } 326 327 if children.is_empty() { 328 write!(f, "/>\n")?; 329 } else { 330 write!(f, ">\n")?; 331 332 for child in children { 333 write!(f, "{}", child)?; 334 } 335 336 write!(f, "</{tag}>")?; 337 } 338 339 Ok(()) 340 } 341 } 342 } 343}