1mod arrow_type;
4mod cluster_mode;
5mod color;
6mod compass_point;
7mod direction;
8mod image;
9mod label;
10mod ordering;
11mod output_mode;
12mod pack_mode;
13mod page_direction;
14mod point;
15mod port_position;
16mod rankdir;
17mod ratio;
18mod rectangle;
19mod shape;
20mod spline_type;
21mod splines;
22mod style;
23mod viewport;
24
25pub use crate::attributes::arrow_type::ArrowType;
26pub use crate::attributes::cluster_mode::ClusterMode;
27pub use crate::attributes::color::{Color, ColorList, IntoWeightedColor, WeightedColor};
28pub use crate::attributes::compass_point::CompassPoint;
29pub use crate::attributes::direction::Direction;
30pub use crate::attributes::image::{ImagePosition, ImageScale};
31pub use crate::attributes::label::{LabelJustification, LabelLocation};
32pub use crate::attributes::ordering::Ordering;
33pub use crate::attributes::output_mode::OutputMode;
34pub use crate::attributes::pack_mode::PackMode;
35pub use crate::attributes::page_direction::PageDirection;
36pub use crate::attributes::point::Point;
37pub use crate::attributes::port_position::PortPosition;
38pub use crate::attributes::rankdir::RankDir;
39pub use crate::attributes::ratio::Ratio;
40pub use crate::attributes::rectangle::Rectangle;
41pub use crate::attributes::shape::Shape;
42pub use crate::attributes::spline_type::SplineType;
43pub use crate::attributes::splines::Splines;
44pub use crate::attributes::style::{EdgeStyle, GraphStyle, NodeStyle, Styles};
45#[doc(hidden)]
46pub use crate::attributes::AttributeText::{AttrStr, EscStr, HtmlStr, QuotedStr};
47use crate::dot::DotString;
48use crate::validation::{ValidationError, ValidationResult};
49use indexmap::map::IndexMap;
50use std::borrow::Cow;
51use std::collections::HashMap;
52use Cow::Borrowed;
53use crate::attributes::viewport::ViewPort;
54
55#[derive(Clone, PartialEq, Eq, Debug)]
57pub enum AttributeText<'a> {
58 AttrStr(Cow<'a, str>),
60
61 EscStr(Cow<'a, str>),
72
73 HtmlStr(Cow<'a, str>),
79
80 QuotedStr(Cow<'a, str>),
85}
86
87impl<'a> AttributeText<'a> {
88 pub fn attr<S: Into<Cow<'a, str>>>(s: S) -> AttributeText<'a> {
89 AttrStr(s.into())
90 }
91
92 pub fn escaped<S: Into<Cow<'a, str>>>(s: S) -> AttributeText<'a> {
93 EscStr(s.into())
94 }
95
96 pub fn html<S: Into<Cow<'a, str>>>(s: S) -> AttributeText<'a> {
97 HtmlStr(s.into())
98 }
99
100 pub fn quoted<S: Into<Cow<'a, str>>>(s: S) -> AttributeText<'a> {
101 QuotedStr(s.into())
102 }
103
104 fn escape_char<F>(c: char, mut f: F)
105 where
106 F: FnMut(char),
107 {
108 match c {
109 '\\' => f(c),
112 _ => {
113 for c in c.escape_default() {
114 f(c)
115 }
116 }
117 }
118 }
119
120 fn escape_str(s: &str) -> String {
121 let mut out = String::with_capacity(s.len());
122 for c in s.chars() {
123 AttributeText::escape_char(c, |c| out.push(c));
124 }
125 out
126 }
127
128 pub fn dot_string(&self) -> String {
131 match *self {
132 AttrStr(ref s) => format!("{}", s),
133 EscStr(ref s) => format!("\"{}\"", AttributeText::escape_str(&s)),
134 HtmlStr(ref s) => format!("<{}>", s),
135 QuotedStr(ref s) => format!("\"{}\"", escape_double_quotes(&s.to_string())),
136 }
137 }
138}
139
140impl<'a> From<ArrowType> for AttributeText<'a> {
141 fn from(arrow_type: ArrowType) -> Self {
142 AttributeText::attr(arrow_type.dot_string())
143 }
144}
145
146impl<'a> From<bool> for AttributeText<'a> {
147 fn from(v: bool) -> Self {
148 AttributeText::attr(v.to_string())
149 }
150}
151
152impl<'a> From<ClusterMode> for AttributeText<'a> {
153 fn from(mode: ClusterMode) -> Self {
154 AttributeText::quoted(mode.dot_string())
155 }
156}
157
158impl<'a> From<Color<'a>> for AttributeText<'a> {
159 fn from(color: Color<'a>) -> Self {
160 AttributeText::quoted(color.dot_string())
161 }
162}
163
164impl<'a> From<ColorList<'a>> for AttributeText<'a> {
165 fn from(color_list: ColorList<'a>) -> Self {
166 AttributeText::quoted(color_list.dot_string())
167 }
168}
169
170impl<'a> From<CompassPoint> for AttributeText<'a> {
171 fn from(compass: CompassPoint) -> Self {
172 AttributeText::quoted(compass.dot_string())
173 }
174}
175
176impl<'a> From<Direction> for AttributeText<'a> {
177 fn from(direction: Direction) -> Self {
178 AttributeText::attr(direction.dot_string())
179 }
180}
181
182impl<'a> From<EdgeStyle> for AttributeText<'a> {
183 fn from(style: EdgeStyle) -> Self {
184 AttributeText::attr(style.dot_string())
185 }
186}
187
188impl<'a> From<f32> for AttributeText<'a> {
189 fn from(v: f32) -> Self {
190 AttributeText::attr(v.to_string())
191 }
192}
193
194impl<'a> From<GraphStyle> for AttributeText<'a> {
195 fn from(style: GraphStyle) -> Self {
196 AttributeText::attr(style.dot_string())
197 }
198}
199
200impl<'a> From<ImagePosition> for AttributeText<'a> {
201 fn from(pos: ImagePosition) -> Self {
202 AttributeText::quoted(pos.dot_string())
203 }
204}
205
206impl<'a> From<ImageScale> for AttributeText<'a> {
207 fn from(scale: ImageScale) -> Self {
208 AttributeText::quoted(scale.dot_string())
209 }
210}
211
212impl<'a> From<LabelJustification> for AttributeText<'a> {
213 fn from(label_justification: LabelJustification) -> Self {
214 AttributeText::attr(label_justification.dot_string())
215 }
216}
217
218impl<'a> From<LabelLocation> for AttributeText<'a> {
219 fn from(label_location: LabelLocation) -> Self {
220 AttributeText::attr(label_location.dot_string())
221 }
222}
223
224impl<'a> From<NodeStyle> for AttributeText<'a> {
225 fn from(style: NodeStyle) -> Self {
226 AttributeText::attr(style.dot_string())
227 }
228}
229
230impl<'a> From<Ordering> for AttributeText<'a> {
231 fn from(ordering: Ordering) -> Self {
232 AttributeText::quoted(ordering.dot_string())
233 }
234}
235
236impl<'a> From<OutputMode> for AttributeText<'a> {
237 fn from(mode: OutputMode) -> Self {
238 AttributeText::quoted(mode.dot_string())
239 }
240}
241
242impl<'a> From<PackMode> for AttributeText<'a> {
243 fn from(mode: PackMode) -> Self {
244 AttributeText::quoted(mode.dot_string())
245 }
246}
247
248impl<'a> From<PageDirection> for AttributeText<'a> {
249 fn from(page_direction: PageDirection) -> Self {
250 AttributeText::attr(page_direction.dot_string())
251 }
252}
253
254impl<'a> From<Point> for AttributeText<'a> {
255 fn from(point: Point) -> Self {
256 AttributeText::quoted(point.dot_string())
257 }
258}
259
260impl<'a> From<PortPosition> for AttributeText<'a> {
261 fn from(port_position: PortPosition) -> Self {
262 AttributeText::quoted(port_position.dot_string())
263 }
264}
265
266impl<'a> From<RankDir> for AttributeText<'a> {
267 fn from(rank_dir: RankDir) -> Self {
268 AttributeText::attr(rank_dir.dot_string())
269 }
270}
271
272impl<'a> From<Ratio> for AttributeText<'a> {
273 fn from(ratio: Ratio) -> Self {
274 match ratio {
275 Ratio::Aspect(_aspect) => AttributeText::attr(ratio.dot_string()),
276 _ => AttributeText::quoted(ratio.dot_string()),
277 }
278 }
279}
280
281impl<'a> From<Rectangle> for AttributeText<'a> {
282 fn from(rectangle: Rectangle) -> Self {
283 AttributeText::quoted(rectangle.dot_string())
284 }
285}
286
287impl<'a> From<Shape> for AttributeText<'a> {
288 fn from(shape: Shape) -> Self {
289 AttributeText::attr(shape.dot_string())
290 }
291}
292
293impl<'a> From<Splines> for AttributeText<'a> {
294 fn from(splines: Splines) -> Self {
295 AttributeText::quoted(splines.dot_string())
296 }
297}
298
299impl<'a> From<SplineType> for AttributeText<'a> {
300 fn from(spline_type: SplineType) -> Self {
301 AttributeText::quoted(spline_type.dot_string())
302 }
303}
304
305impl<'a> From<Styles> for AttributeText<'a> {
306 fn from(styles: Styles) -> Self {
307 match styles {
308 Styles::Edge(s) => AttributeText::from(s),
309 Styles::Node(s) => AttributeText::from(s),
310 Styles::Graph(s) => AttributeText::from(s),
311 }
312 }
313}
314
315impl<'a> From<ViewPort> for AttributeText<'a> {
316 fn from(viewport: ViewPort) -> Self {
317 AttributeText::quoted(viewport.dot_string())
318 }
319}
320
321
322impl<'a> From<u32> for AttributeText<'a> {
323 fn from(v: u32) -> Self {
324 AttributeText::attr(v.to_string())
325 }
326}
327
328impl<'a> From<String> for AttributeText<'a> {
329 fn from(string: String) -> Self {
330 if is_alphanum(&string) {
332 AttributeText::attr(string)
333 } else {
334 AttributeText::quoted(string)
335 }
336 }
337}
338
339impl<'a> From<&str> for AttributeText<'a> {
340 fn from(string: &str) -> Self {
341 if is_alphanum(&String::from(string)) {
343 AttributeText::attr(String::from(string))
344 } else {
345 AttributeText::quoted(String::from(string))
346 }
347 }
348}
349
350impl<'a> From<AttributeText<'a>> for String {
351 fn from(attribute_text: AttributeText) -> Self {
352 attribute_text.dot_string()
353 }
354}
355
356#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Debug, Clone)]
357pub enum AttributeType {
358 Graph,
359 Node,
360 Edge,
361}
362
363pub trait GraphAttributes<'a> {
364 fn background(&mut self, background: String) -> &mut Self {
365 self.add_attribute("_background", AttributeText::attr(background))
366 }
367
368 fn background_color(&mut self, background_color: Color<'a>) -> &mut Self {
370 self.add_attribute("bgcolor", AttributeText::from(background_color))
371 }
372
373 fn background_colorlist(&mut self, background_colors: ColorList<'a>) -> &mut Self {
379 self.add_attribute("bgcolor", AttributeText::from(background_colors))
380 }
381
382 fn bounding_box(&mut self, bounding_box: String) -> &mut Self {
386 self.add_attribute("bb", AttributeText::quoted(bounding_box))
387 }
388
389 fn center(&mut self, center: bool) -> &mut Self {
391 self.add_attribute("center", AttributeText::from(center))
392 }
393
394 fn charset(&mut self, charset: String) -> &mut Self {
396 self.add_attribute("charset", AttributeText::quoted(charset))
397 }
398
399 fn class(&mut self, class: String) -> &mut Self {
403 Attributes::class(self.get_attributes_mut(), class);
404 self
405 }
406
407 fn cluster_rank(&mut self, cluster_rank: ClusterMode) -> &mut Self {
415 self.add_attribute("clusterrank", AttributeText::from(cluster_rank))
416 }
417
418 fn color(&mut self, color: Color<'a>) -> &mut Self {
422 Attributes::color(self.get_attributes_mut(), color);
423 self
424 }
425
426 fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
431 Attributes::color_scheme(self.get_attributes_mut(), color_scheme);
432 self
433 }
434
435 fn comment(&mut self, comment: String) -> &mut Self {
437 Attributes::comment(self.get_attributes_mut(), comment);
438 self
439 }
440
441 fn compound(&mut self, compound: String) -> &mut Self {
442 self.add_attribute("compound", AttributeText::quoted(compound))
443 }
444
445 fn concentrate(&mut self, concentrate: String) -> &mut Self {
446 self.add_attribute("concentrate", AttributeText::quoted(concentrate))
447 }
448
449 fn dpi(&mut self, dpi: f32) -> &mut Self {
452 self.add_attribute("dpi", AttributeText::from(dpi))
453 }
454
455 fn fill_color(&mut self, fill_color: Color<'a>) -> &mut Self {
457 Attributes::fill_color(self.get_attributes_mut(), fill_color);
458 self
459 }
460
461 fn fill_color_with_colorlist(&mut self, fill_colors: ColorList<'a>) -> &mut Self {
464 Attributes::fill_color_with_colorlist(self.get_attributes_mut(), fill_colors);
465 self
466 }
467
468 fn fill_color_with_iter<I>(&mut self, fill_colors: I) -> &mut Self
473 where
474 I: IntoIterator,
475 I::Item: IntoWeightedColor<'a>,
476 {
477 Attributes::fill_color_with_iter(self.get_attributes_mut(), fill_colors);
478 self
479 }
480
481 fn font_color(&mut self, font_color: Color<'a>) -> &mut Self {
483 Attributes::font_color(self.get_attributes_mut(), font_color);
484 self
485 }
486
487 fn font_name(&mut self, font_name: String) -> &mut Self {
489 Attributes::font_name(self.get_attributes_mut(), font_name);
490 self
491 }
492
493 fn font_names(&mut self, font_names: String) -> &mut Self {
494 self.add_attribute("fontnames", AttributeText::quoted(font_names))
495 }
496
497 fn font_path(&mut self, font_path: String) -> &mut Self {
498 self.add_attribute("fontpath", AttributeText::quoted(font_path))
499 }
500
501 fn font_size(&mut self, font_size: f32) -> &mut Self {
504 if font_size < 1.0 {
505 self.add_validation_error("fontsize", "Must be greater than or equal to 1.0")
506 }
507 Attributes::font_size(self.get_attributes_mut(), font_size);
508 self
509 }
510
511 fn force_label(&mut self, force_label: bool) -> &mut Self {
512 self.add_attribute("forcelabel", AttributeText::from(force_label))
513 }
514
515 fn gradient_angle(&mut self, gradient_angle: u32) -> &mut Self {
517 Attributes::gradient_angle(self.get_attributes_mut(), gradient_angle);
518 self
519 }
520
521 fn image_path(&mut self, image_path: String) -> &mut Self {
522 self.add_attribute("imagepath", AttributeText::escaped(image_path))
523 }
524
525 fn label<S: Into<String>>(&mut self, label: S) -> &mut Self {
527 Attributes::label(self.get_attributes_mut(), label);
528 self
529 }
530
531 fn label_justification(
535 &mut self,
536 label_justification: LabelJustification,
537 ) -> &mut Self {
538 self.add_attribute("labeljust", AttributeText::from(label_justification))
539 }
540
541 fn label_location(&mut self, label_location: LabelLocation) -> &mut Self {
553 Attributes::label_location(self.get_attributes_mut(), label_location);
554 self
555 }
556
557 fn landscape(&mut self, landscape: bool) -> &mut Self {
558 self.add_attribute("landscape", AttributeText::from(landscape))
559 }
560
561 fn layer_list_sep(&mut self, layer_list_sep: String) -> &mut Self {
564 self.add_attribute("layerlistsep", AttributeText::attr(layer_list_sep))
565 }
566
567 fn layers(&mut self, layers: String) -> &mut Self {
571 Attributes::layer(self.get_attributes_mut(), layers);
572 self
573 }
574
575 fn layer_select(&mut self, layer_select: String) -> &mut Self {
577 self.add_attribute("layerselect", AttributeText::attr(layer_select))
578 }
579
580 fn layer_sep(&mut self, layer_sep: String) -> &mut Self {
583 self.add_attribute("layersep", AttributeText::attr(layer_sep))
584 }
585
586 fn lheight(&mut self, lheight: f32) -> &mut Self {
588 self.add_attribute("lheight", AttributeText::from(lheight))
589 }
590
591 fn label_position(&mut self, lp: Point) -> &mut Self {
594 Attributes::label_position(self.get_attributes_mut(), lp);
595 self
596 }
597
598 fn lwidth(&mut self, lwidth: f32) -> &mut Self {
600 self.add_attribute("lwidth", AttributeText::from(lwidth))
601 }
602
603 fn margin(&mut self, margin: f32) -> &mut Self {
607 self.margin_point(Point::new_2d(margin, margin))
608 }
609
610 fn margin_point(&mut self, margin: Point) -> &mut Self {
619 Attributes::margin(self.get_attributes_mut(), margin);
620 self
621 }
622
623 fn mclimit(&mut self, mclimit: f32) -> &mut Self {
628 self.add_attribute("mclimit", AttributeText::from(mclimit))
629 }
630
631 fn mindist(&mut self, mindist: u32) -> &mut Self {
633 self.add_attribute("mindist", AttributeText::from(mindist))
634 }
635
636 fn newrank(&mut self, newrank: bool) -> &mut Self {
646 self.add_attribute("newrank", AttributeText::from(newrank))
647 }
648
649 fn nodesep(&mut self, nodesep: f32) -> &mut Self {
652 if nodesep < 0.02 {
653 self.add_validation_error("nodesep", "Must be greater than or equal to 0.02")
654 }
655 self.add_attribute("nodesep", AttributeText::from(nodesep))
656 }
657
658 fn no_justify(&mut self, no_justify: bool) -> &mut Self {
669 Attributes::no_justify(self.get_attributes_mut(), no_justify);
670 self
671 }
672
673 fn nslimit(&mut self, nslimit: f32) -> &mut Self {
677 self.add_attribute("nslimit", AttributeText::from(nslimit))
678 }
679
680 fn ordering(&mut self, ordering: Ordering) -> &mut Self {
691 Attributes::ordering(self.get_attributes_mut(), ordering);
692 self
693 }
694
695 fn orientation(&mut self, orientation: f32) -> &mut Self {
699 if orientation < 0.0 || orientation > 360.0 {
700 self.add_validation_error("orientation", "Must be between 0 and 360")
701 }
702 Attributes::orientation(self.get_attributes_mut(), orientation);
703 self
704 }
705
706 fn output_order(&mut self, output_order: OutputMode) -> &mut Self {
709 self.add_attribute("outputorder", AttributeText::from(output_order))
710 }
711
712 fn pack(&mut self, pack: bool) -> &mut Self {
717 self.add_attribute("pack", AttributeText::from(pack))
718 }
719
720 fn pack_int(&mut self, pack: u32) -> &mut Self {
726 self.add_attribute("pack", AttributeText::from(pack))
727 }
728
729 fn pack_mode(&mut self, pack_mode: PackMode) -> &mut Self {
732 self.add_attribute("packmode", AttributeText::from(pack_mode))
733 }
734
735 fn pad(&mut self, pad: f32) -> &mut Self {
740 self.pad_point(Point::new_2d(pad, pad))
741 }
742
743 fn pad_point(&mut self, pad: Point) -> &mut Self {
748 self.add_attribute("pad", AttributeText::from(pad))
749 }
750
751 fn page(&mut self, page: f32) -> &mut Self {
754 self.add_attribute("page", AttributeText::from(page))
755 }
756
757 fn page_point(&mut self, page: Point) -> &mut Self {
759 self.add_attribute("page", AttributeText::from(page))
760 }
761
762 fn page_dir(&mut self, page_dir: PageDirection) -> &mut Self {
766 self.add_attribute("pagedir", AttributeText::from(page_dir))
767 }
768
769 fn quantum(&mut self, quantum: f32) -> &mut Self {
772 if quantum < 0.0 {
773 self.add_validation_error("quantum", "Must be greater than or equal to 0")
774 }
775 self.add_attribute("quantum", AttributeText::from(quantum))
776 }
777
778 fn rank_dir(&mut self, rank_dir: RankDir) -> &mut Self {
784 self.add_attribute("rankdir", AttributeText::from(rank_dir))
785 }
786
787 fn rank_sep(&mut self, rank_sep: String) -> &mut Self {
793 self.add_attribute("ranksep", AttributeText::attr(rank_sep))
794 }
795
796 fn ratio(&mut self, ratio: Ratio) -> &mut Self {
799 self.add_attribute("ratio", AttributeText::from(ratio))
800 }
801
802 fn remincross(&mut self, remincross: bool) -> &mut Self {
804 self.add_attribute("remincross", AttributeText::from(remincross))
805 }
806
807 fn rotate(&mut self, rotate: u32) -> &mut Self {
809 self.add_attribute("rotate", AttributeText::from(rotate))
810 }
811
812 fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
817 Attributes::show_boxes(self.get_attributes_mut(), show_boxes);
818 self
819 }
820
821 fn size(&mut self, size: u32, desired_min: bool) -> &mut Self {
830 self.size_point(Point {
831 x: size as f32,
832 y: size as f32,
833 z: None,
834 force_pos: desired_min,
835 })
836 }
837
838 fn size_point(&mut self, size: Point) -> &mut Self {
845 self.add_attribute("size", AttributeText::from(size))
846 }
847
848 fn sortv(&mut self, sortv: u32) -> &mut Self {
852 Attributes::sortv(self.get_attributes_mut(), sortv);
853 self
854 }
855
856 fn splines(&mut self, splines: Splines) -> &mut Self {
858 self.add_attribute("splines", AttributeText::from(splines))
859 }
860
861 fn style(&mut self, style: GraphStyle) -> &mut Self {
863 Attributes::style(self.get_attributes_mut(), Styles::Graph(style));
864 self
865 }
866
867 fn stylesheet(&mut self, stylesheet: String) -> &mut Self {
870 self.add_attribute("stylesheet", AttributeText::attr(stylesheet))
871 }
872
873 fn target(&mut self, target: String) -> &mut Self {
875 Attributes::target(self.get_attributes_mut(), target);
876 self
877 }
878
879 fn true_color(&mut self, true_color: bool) -> &mut Self {
884 self.add_attribute("truecolor", AttributeText::from(true_color))
885 }
886
887 fn url(&mut self, url: String) -> &mut Self {
889 Attributes::url(self.get_attributes_mut(), url);
890 self
891 }
892
893 fn viewport(&mut self, viewport: ViewPort) -> &mut Self {
905 self.add_attribute("viewport", AttributeText::from(viewport))
906 }
907
908 fn add_attribute<S: Into<String>>(
910 &mut self,
911 key: S,
912 value: AttributeText<'a>,
913 ) -> &mut Self;
914
915 fn add_attributes(
917 &'a mut self,
918 attributes: HashMap<String, AttributeText<'a>>,
919 ) -> &mut Self;
920
921 fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>>;
922
923 fn add_validation_error(&mut self, field: &'static str, message: &'static str);
924}
925
926impl<'a> GraphAttributes<'a> for GraphAttributeStatementBuilder<'a> {
927 fn add_attribute<S: Into<String>>(
928 &mut self,
929 key: S,
930 value: AttributeText<'a>,
931 ) -> &mut Self {
932 self.attributes.insert(key.into(), value);
933 self
934 }
935
936 fn add_attributes(
938 &'a mut self,
939 attributes: HashMap<String, AttributeText<'a>>,
940 ) -> &mut Self {
941 self.attributes.extend(attributes);
942 self
943 }
944
945 fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>> {
946 &mut self.attributes
947 }
948
949 fn add_validation_error(&mut self, field: &'static str, message: &'static str) {
950 self.errors.push(ValidationError {
951 field: Borrowed(field),
952 message: Borrowed(message),
953 })
954 }
955}
956
957pub struct GraphAttributeStatementBuilder<'a> {
959 pub attributes: IndexMap<String, AttributeText<'a>>,
960 errors: Vec<ValidationError>,
961}
962
963impl<'a> GraphAttributeStatementBuilder<'a> {
964 pub fn new() -> Self {
965 Self {
966 attributes: IndexMap::new(),
967 errors: Vec::new(),
968 }
969 }
970
971 pub fn build(&self) -> ValidationResult<IndexMap<String, AttributeText<'a>>> {
972 if !self.errors.is_empty() {
973 return Err(self.errors.clone());
974 }
975 Ok(self.build_ignore_validation())
976 }
977
978 pub fn build_ignore_validation(&self) -> IndexMap<String, AttributeText<'a>> {
979 self.attributes.clone()
980 }
981}
982
983pub(crate) struct Attributes;
984impl Attributes {
985 pub fn class(attributes: &mut IndexMap<String, AttributeText>, class: String) {
986 Self::add_attribute(attributes, "class", AttributeText::quoted(class))
987 }
988
989 pub fn color<'a>(
990 attributes: &mut IndexMap<String, AttributeText<'a>>,
991 color: Color<'a>,
992 ) {
993 Self::add_attribute(attributes, "color", AttributeText::from(color))
994 }
995
996 pub fn color_with_colorlist<'a>(
997 attributes: &mut IndexMap<String, AttributeText<'a>>,
998 color: ColorList<'a>,
999 ) {
1000 Self::add_attribute(attributes, "color", AttributeText::from(color))
1001 }
1002
1003 pub fn color_scheme(
1004 attributes: &mut IndexMap<String, AttributeText>,
1005 color_scheme: String,
1006 ) {
1007 Self::add_attribute(
1008 attributes,
1009 "colorscheme",
1010 AttributeText::quoted(color_scheme),
1011 )
1012 }
1013
1014 pub fn comment(attributes: &mut IndexMap<String, AttributeText>, comment: String) {
1015 Self::add_attribute(attributes, "comment", AttributeText::quoted(comment))
1016 }
1017
1018 pub fn fill_color<'a>(
1019 attributes: &mut IndexMap<String, AttributeText<'a>>,
1020 fill_color: Color<'a>,
1021 ) {
1022 Self::add_attribute(attributes, "fillcolor", AttributeText::from(fill_color))
1023 }
1024
1025 pub fn fill_color_with_colorlist<'a>(
1026 attributes: &mut IndexMap<String, AttributeText<'a>>,
1027 fill_colors: ColorList<'a>,
1028 ) {
1029 Self::add_attribute(attributes, "fillcolor", AttributeText::from(fill_colors))
1030 }
1031
1032 pub fn fill_color_with_iter<'a, I>(
1033 attributes: &mut IndexMap<String, AttributeText<'a>>,
1034 fill_colors: I,
1035 ) where
1036 I: IntoIterator,
1037 I::Item: IntoWeightedColor<'a>,
1038 {
1039 let colors: Vec<WeightedColor> = fill_colors
1040 .into_iter()
1041 .map(|e| e.into_weighted_color())
1042 .collect();
1043
1044 let color_list = ColorList { colors };
1045
1046 Self::add_attribute(attributes, "fillcolor", AttributeText::from(color_list))
1047 }
1048
1049 pub fn font_color<'a>(
1050 attributes: &mut IndexMap<String, AttributeText<'a>>,
1051 font_color: Color<'a>,
1052 ) {
1053 Self::add_attribute(attributes, "fontcolor", AttributeText::from(font_color))
1054 }
1055
1056 pub fn font_name(
1057 attributes: &mut IndexMap<String, AttributeText>,
1058 font_name: String,
1059 ) {
1060 Self::add_attribute(attributes, "fontname", AttributeText::quoted(font_name))
1061 }
1062
1063 pub fn font_size(attributes: &mut IndexMap<String, AttributeText>, font_size: f32) {
1064 Self::add_attribute(attributes, "fontsize", AttributeText::from(font_size))
1065 }
1066
1067 pub fn gradient_angle(
1068 attributes: &mut IndexMap<String, AttributeText>,
1069 gradient_angle: u32,
1070 ) {
1071 Self::add_attribute(
1072 attributes,
1073 "gradientangle",
1074 AttributeText::from(gradient_angle),
1075 )
1076 }
1077
1078 pub fn label<S: Into<String>>(attributes: &mut IndexMap<String, AttributeText>, text: S) {
1079 Self::add_attribute(attributes, "label", AttributeText::quoted(text.into()));
1080 }
1081
1082 pub fn label_location(
1083 attributes: &mut IndexMap<String, AttributeText>,
1084 label_location: LabelLocation,
1085 ) {
1086 Self::add_attribute(attributes, "labelloc", AttributeText::from(label_location))
1087 }
1088
1089 pub fn layer(attributes: &mut IndexMap<String, AttributeText>, layer: String) {
1092 Self::add_attribute(attributes, "layer", AttributeText::attr(layer))
1093 }
1094
1095 pub fn label_position(attributes: &mut IndexMap<String, AttributeText>, lp: Point) {
1096 Self::add_attribute(attributes, "lp", AttributeText::from(lp))
1097 }
1098
1099 pub fn margin(attributes: &mut IndexMap<String, AttributeText>, margin: Point) {
1100 Self::add_attribute(attributes, "margin", AttributeText::from(margin))
1101 }
1102
1103 pub fn no_justify(
1104 attributes: &mut IndexMap<String, AttributeText>,
1105 no_justify: bool,
1106 ) {
1107 Self::add_attribute(attributes, "nojustify", AttributeText::from(no_justify))
1108 }
1109
1110 pub fn ordering(
1111 attributes: &mut IndexMap<String, AttributeText>,
1112 ordering: Ordering,
1113 ) {
1114 Self::add_attribute(attributes, "ordering", AttributeText::from(ordering))
1115 }
1116
1117 pub fn orientation(
1118 attributes: &mut IndexMap<String, AttributeText>,
1119 orientation: f32,
1120 ) {
1121 Self::add_attribute(attributes, "orientation", AttributeText::from(orientation))
1122 }
1123
1124 pub fn pen_width(attributes: &mut IndexMap<String, AttributeText>, pen_width: f32) {
1125 Self::add_attribute(attributes, "penwidth", AttributeText::from(pen_width))
1126 }
1127
1128 pub fn pos(attributes: &mut IndexMap<String, AttributeText>, pos: Point) {
1130 Self::add_attribute(attributes, "pos", AttributeText::from(pos))
1131 }
1132
1133 pub fn show_boxes(
1134 attributes: &mut IndexMap<String, AttributeText>,
1135 show_boxes: u32,
1136 ) {
1137 Self::add_attribute(attributes, "showboxes", AttributeText::from(show_boxes))
1138 }
1139
1140 pub fn sortv(attributes: &mut IndexMap<String, AttributeText>, sortv: u32) {
1141 Self::add_attribute(attributes, "sortv", AttributeText::from(sortv))
1142 }
1143
1144 pub fn style(attributes: &mut IndexMap<String, AttributeText>, style: Styles) {
1145 Self::add_attribute(attributes, "style", AttributeText::from(style))
1146 }
1147
1148 pub fn target(attributes: &mut IndexMap<String, AttributeText>, target: String) {
1149 Self::add_attribute(attributes, "target", AttributeText::escaped(target))
1150 }
1151
1152 pub fn tooltip(attributes: &mut IndexMap<String, AttributeText>, tooltip: String) {
1153 Self::add_attribute(attributes, "tooltip", AttributeText::escaped(tooltip))
1154 }
1155
1156 pub fn url(attributes: &mut IndexMap<String, AttributeText>, url: String) {
1157 Self::add_attribute(attributes, "url", AttributeText::escaped(url))
1158 }
1159
1160 pub fn xlabel(attributes: &mut IndexMap<String, AttributeText>, width: String) {
1161 Self::add_attribute(attributes, "xlabel", AttributeText::escaped(width))
1162 }
1163
1164 pub fn xlp(attributes: &mut IndexMap<String, AttributeText>, xlp: Point) {
1165 Self::add_attribute(attributes, "xlp", AttributeText::from(xlp))
1166 }
1167
1168 pub fn add_attribute<'a, S: Into<String>>(
1169 attributes: &mut IndexMap<String, AttributeText<'a>>,
1170 key: S,
1171 value: AttributeText<'a>,
1172 ) {
1173 attributes.insert(key.into(), value);
1174 }
1175}
1176
1177pub trait NodeAttributes<'a> {
1178 fn area(&mut self, area: f32) -> &mut Self {
1181 if area <= 0.0 {
1182 self.add_validation_error("area", "Must be greater than 0")
1183 }
1184 self.add_attribute("area", AttributeText::from(area))
1185 }
1186
1187 fn class(&mut self, class: String) -> &mut Self {
1191 Attributes::class(self.get_attributes_mut(), class);
1192 self
1193 }
1194
1195 fn color(&mut self, color: Color<'a>) -> &mut Self {
1197 Attributes::color(self.get_attributes_mut(), color);
1198 self
1199 }
1200
1201 fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
1206 Attributes::color_scheme(self.get_attributes_mut(), color_scheme);
1207 self
1208 }
1209
1210 fn comment(&mut self, comment: String) -> &mut Self {
1212 Attributes::comment(self.get_attributes_mut(), comment);
1213 self
1214 }
1215
1216 fn distortion(&mut self, distortion: f32) -> &mut Self {
1219 self.add_attribute("distortion", AttributeText::from(distortion))
1220 }
1221
1222 fn fill_color(&mut self, fill_color: Color<'a>) -> &mut Self {
1224 Attributes::fill_color(self.get_attributes_mut(), fill_color);
1225 self
1226 }
1227
1228 fn fill_color_with_colorlist(&mut self, fill_colors: ColorList<'a>) -> &mut Self {
1231 Attributes::fill_color_with_colorlist(self.get_attributes_mut(), fill_colors);
1232 self
1233 }
1234
1235 fn fill_color_with_iter<I>(&mut self, fill_colors: I) -> &mut Self
1239 where
1240 I: IntoIterator,
1241 I::Item: IntoWeightedColor<'a>,
1242 {
1243 Attributes::fill_color_with_iter(self.get_attributes_mut(), fill_colors);
1244 self
1245 }
1246
1247 fn fixed_size(&mut self, fixed_size: bool) -> &mut Self {
1253 self.add_attribute("fixedsize", AttributeText::from(fixed_size))
1254 }
1255
1256 fn font_color(&mut self, font_color: Color<'a>) -> &mut Self {
1258 Attributes::font_color(self.get_attributes_mut(), font_color);
1259 self
1260 }
1261
1262 fn font_name(&mut self, font_name: String) -> &mut Self {
1264 Attributes::font_name(self.get_attributes_mut(), font_name);
1265 self
1266 }
1267
1268 fn font_size(&mut self, font_size: f32) -> &mut Self {
1271 Attributes::font_size(self.get_attributes_mut(), font_size);
1272 self
1273 }
1274
1275 fn gradient_angle(&mut self, gradient_angle: u32) -> &mut Self {
1277 Attributes::gradient_angle(self.get_attributes_mut(), gradient_angle);
1278 self
1279 }
1280
1281 fn group(&mut self, group: String) -> &mut Self {
1284 self.add_attribute("group", AttributeText::attr(group))
1285 }
1286
1287 fn height(&mut self, height: f32) -> &mut Self {
1290 if height < 0.02 {
1291 self.add_validation_error("height", "Must be greater than or equal to 0.02")
1292 }
1293 self.add_attribute("height", AttributeText::from(height))
1294 }
1295
1296 fn image(&mut self, image: String) -> &mut Self {
1301 self.add_attribute("image", AttributeText::quoted(image))
1302 }
1303
1304 fn image_pos(&mut self, image_pos: ImagePosition) -> &mut Self {
1307 self.add_attribute("imagepos", AttributeText::from(image_pos))
1308 }
1309
1310 fn image_scale_bool(&mut self, image_scale: bool) -> &mut Self {
1312 self.add_attribute("imagescale", AttributeText::from(image_scale))
1313 }
1314
1315 fn image_scale(&mut self, image_scale: ImageScale) -> &mut Self {
1317 self.add_attribute("imagescale", AttributeText::from(image_scale))
1318 }
1319
1320 fn label<S: Into<Cow<'a, str>>>(&mut self, text: S) -> &mut Self {
1322 self.add_attribute("label", AttributeText::quoted(text))
1323 }
1324
1325 fn label_location(&mut self, label_location: LabelLocation) -> &mut Self {
1337 Attributes::label_location(self.get_attributes_mut(), label_location);
1338 self
1339 }
1340
1341 fn layer(&mut self, layer: String) -> &mut Self {
1343 Attributes::layer(self.get_attributes_mut(), layer);
1344 self
1345 }
1346
1347 fn margin(&mut self, margin: f32) -> &mut Self {
1351 self.margin_point(Point::new_2d(margin, margin))
1352 }
1353
1354 fn margin_point(&mut self, margin: Point) -> &mut Self {
1365 Attributes::margin(self.get_attributes_mut(), margin);
1366 self
1367 }
1368
1369 fn no_justify(&mut self, no_justify: bool) -> &mut Self {
1381 Attributes::no_justify(self.get_attributes_mut(), no_justify);
1382 self
1383 }
1384
1385 fn ordering(&mut self, ordering: Ordering) -> &mut Self {
1396 Attributes::ordering(self.get_attributes_mut(), ordering);
1397 self
1398 }
1399
1400 fn orientation(&mut self, orientation: f32) -> &mut Self {
1406 if orientation < 0.0 || orientation > 360.0 {
1407 self.add_validation_error("orientation", "Must be between 0 and 360")
1408 }
1409 Attributes::orientation(self.get_attributes_mut(), orientation);
1410 self
1411 }
1412
1413 fn pen_width(&mut self, pen_width: f32) -> &mut Self {
1417 Attributes::pen_width(self.get_attributes_mut(), pen_width);
1418 self
1419 }
1420
1421 fn peripheries(&mut self, peripheries: u32) -> &mut Self {
1423 self.add_attribute("penwidth", AttributeText::from(peripheries))
1424 }
1425
1426 fn pos(&mut self, pos: Point) -> &mut Self {
1429 Attributes::pos(self.get_attributes_mut(), pos);
1430 self
1431 }
1432
1433 fn rects(&mut self, rect: Rectangle) -> &mut Self {
1437 self.add_attribute("rects", AttributeText::from(rect))
1438 }
1439
1440 fn regular(&mut self, regular: bool) -> &mut Self {
1443 self.add_attribute("regular", AttributeText::from(regular))
1444 }
1445
1446 fn sample_points(&mut self, sample_points: u32) -> &mut Self {
1448 self.add_attribute("samplepoints", AttributeText::from(sample_points))
1449 }
1450
1451 fn shape(&mut self, shape: Shape) -> &mut Self {
1453 self.add_attribute("shape", AttributeText::from(shape))
1454 }
1455
1456 fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
1461 Attributes::show_boxes(self.get_attributes_mut(), show_boxes);
1462 self
1463 }
1464
1465 fn sides(&mut self, sides: u32) -> &mut Self {
1467 self.add_attribute("sides", AttributeText::from(sides))
1468 }
1469
1470 fn skew(&mut self, skew: f32) -> &mut Self {
1474 if skew < -100.0 {
1475 self.add_validation_error("skew", "Must be greater than or equal to -100")
1476 }
1477 self.add_attribute("skew", AttributeText::from(skew))
1478 }
1479
1480 fn sortv(&mut self, sortv: u32) -> &mut Self {
1484 Attributes::sortv(self.get_attributes_mut(), sortv);
1485 self
1486 }
1487
1488 fn style(&mut self, style: NodeStyle) -> &mut Self {
1490 Attributes::style(self.get_attributes_mut(), Styles::Node(style));
1491 self
1492 }
1493
1494 fn target(&mut self, target: String) -> &mut Self {
1497 Attributes::target(self.get_attributes_mut(), target);
1498 self
1499 }
1500
1501 fn tooltip(&mut self, tooltip: String) -> &mut Self {
1508 Attributes::tooltip(self.get_attributes_mut(), tooltip);
1509 self
1510 }
1511
1512 fn url(&mut self, url: String) -> &mut Self {
1514 Attributes::url(self.get_attributes_mut(), url);
1515 self
1516 }
1517
1518 fn vertices(&mut self, vertices: String) -> &mut Self {
1521 self.add_attribute("vertices", AttributeText::quoted(vertices))
1522 }
1523
1524 fn width(&mut self, width: f32) -> &mut Self {
1530 self.add_attribute("width", AttributeText::from(width))
1531 }
1532
1533 fn xlabel(&mut self, xlabel: String) -> &mut Self {
1540 Attributes::xlabel(self.get_attributes_mut(), xlabel);
1541 self
1542 }
1543
1544 fn xlp(&mut self, xlp: Point) -> &mut Self {
1547 Attributes::xlp(self.get_attributes_mut(), xlp);
1548 self
1549 }
1550
1551 fn add_attribute<S: Into<String>>(
1553 &mut self,
1554 key: S,
1555 value: AttributeText<'a>,
1556 ) -> &mut Self;
1557
1558 fn add_attributes(
1560 &'a mut self,
1561 attributes: HashMap<String, AttributeText<'a>>,
1562 ) -> &mut Self;
1563
1564 fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>>;
1565
1566 fn add_validation_error(&mut self, field: &'static str, message: &'static str);
1567}
1568
1569pub trait EdgeAttributes<'a> {
1570 fn arrow_head(&mut self, arrowhead: ArrowType) -> &mut Self {
1573 self.add_attribute("arrowhead", AttributeText::from(arrowhead))
1574 }
1575
1576 fn arrow_size(&mut self, arrow_size: f32) -> &mut Self {
1579 if arrow_size < 0.0 {
1580 self.add_validation_error("arrowsize", "Must be greater than or equal to 0")
1581 }
1582 self.add_attribute("arrowsize", AttributeText::from(arrow_size))
1583 }
1584
1585 fn arrow_tail(&mut self, arrow_tail: ArrowType) -> &mut Self {
1588 self.add_attribute("arrowtail", AttributeText::from(arrow_tail))
1589 }
1590
1591 fn class(&mut self, class: String) -> &mut Self {
1595 Attributes::class(self.get_attributes_mut(), class);
1596 self
1597 }
1598
1599 fn color(&mut self, color: Color<'a>) -> &mut Self {
1603 Attributes::color(self.get_attributes_mut(), color);
1604 self
1605 }
1606
1607 fn color_with_colorlist(&mut self, color: ColorList<'a>) -> &mut Self {
1616 Attributes::color_with_colorlist(self.get_attributes_mut(), color);
1617 self
1618 }
1619
1620 fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
1625 Attributes::color_scheme(self.get_attributes_mut(), color_scheme);
1626 self
1627 }
1628
1629 fn comment(&mut self, comment: String) -> &mut Self {
1631 self.add_attribute("comment", AttributeText::attr(comment));
1632 self
1633 }
1634
1635 fn constraint(&mut self, constraint: bool) -> &mut Self {
1637 self.add_attribute("constraint", AttributeText::from(constraint))
1638 }
1639
1640 fn decorate(&mut self, decorate: bool) -> &mut Self {
1643 self.add_attribute("decorate", AttributeText::from(decorate))
1644 }
1645
1646 fn dir(&mut self, dir: Direction) -> &mut Self {
1651 self.add_attribute("dir", AttributeText::from(dir))
1652 }
1653
1654 fn edge_target(&mut self, edge_target: String) -> &mut Self {
1659 self.add_attribute("edgetarget", AttributeText::escaped(edge_target))
1660 }
1661
1662 fn edge_tooltip(&mut self, edge_tooltip: String) -> &mut Self {
1665 self.add_attribute("edgetooltip", AttributeText::escaped(edge_tooltip))
1666 }
1667
1668 fn edge_url(&mut self, edge_url: String) -> &mut Self {
1673 self.add_attribute("edgeurl", AttributeText::escaped(edge_url))
1674 }
1675
1676 fn fill_color(&mut self, fill_color: Color<'a>) -> &mut Self {
1679 Attributes::fill_color(self.get_attributes_mut(), fill_color);
1680 self
1681 }
1682
1683 fn fill_color_with_colorlist(&mut self, fill_color: ColorList<'a>) -> &mut Self {
1690 Attributes::fill_color_with_colorlist(self.get_attributes_mut(), fill_color);
1691 self
1692 }
1693
1694 fn font_color(&mut self, font_color: Color<'a>) -> &mut Self {
1696 Attributes::font_color(self.get_attributes_mut(), font_color);
1697 self
1698 }
1699
1700 fn font_name(&mut self, font_name: String) -> &mut Self {
1702 Attributes::font_name(self.get_attributes_mut(), font_name);
1703 self
1704 }
1705
1706 fn font_size(&mut self, font_size: f32) -> &mut Self {
1709 Attributes::font_size(self.get_attributes_mut(), font_size);
1710 self
1711 }
1712
1713 fn head_lp(&mut self, head_lp: Point) -> &mut Self {
1715 self.add_attribute("head_lp", AttributeText::from(head_lp))
1716 }
1717
1718 fn head_clip(&mut self, head_clip: bool) -> &mut Self {
1722 self.add_attribute("headclip", AttributeText::from(head_clip))
1723 }
1724
1725 fn head_label(&mut self, head_label: String) -> &mut Self {
1727 self.add_attribute("headlabel", AttributeText::quoted(head_label))
1728 }
1729
1730 fn head_port(&mut self, head_port: PortPosition) -> &mut Self {
1734 self.add_attribute("headport", AttributeText::from(head_port))
1735 }
1736
1737 fn head_target(&mut self, head_target: String) -> &mut Self {
1742 self.add_attribute("headtarget", AttributeText::escaped(head_target))
1743 }
1744
1745 fn head_tooltip(&mut self, head_tooltip: String) -> &mut Self {
1748 self.add_attribute("headtooltip", AttributeText::escaped(head_tooltip))
1749 }
1750
1751 fn head_url(&mut self, head_url: String) -> &mut Self {
1754 self.add_attribute("headURL", AttributeText::escaped(head_url))
1755 }
1756
1757 fn label(&mut self, label: String) -> &mut Self {
1759 Attributes::label(self.get_attributes_mut(), label);
1760 self
1761 }
1762
1763 fn label_angle(&mut self, label_angle: f32) -> &mut Self {
1771 if label_angle < -180.0 {
1772 self.add_validation_error(
1773 "labelangle",
1774 "Must be greater than or equal to -180",
1775 )
1776 }
1777 self.add_attribute("labelangle", AttributeText::from(label_angle))
1778 }
1779
1780 fn label_distance(&mut self, label_distance: f32) -> &mut Self {
1784 self.add_attribute("labeldistance", AttributeText::from(label_distance))
1785 }
1786
1787 fn label_float(&mut self, label_float: bool) -> &mut Self {
1790 self.add_attribute("labelfloat", AttributeText::from(label_float))
1791 }
1792
1793 fn label_font_color(&mut self, label_font_color: Color<'a>) -> &mut Self {
1795 self.add_attribute("labelfontcolor", AttributeText::from(label_font_color))
1796 }
1797
1798 fn label_font_name(&mut self, label_font_name: String) -> &mut Self {
1801 self.add_attribute("labelfontname", AttributeText::attr(label_font_name))
1802 }
1803
1804 fn label_font_size(&mut self, label_font_size: f32) -> &mut Self {
1808 if label_font_size < 1.0 {
1809 self.add_validation_error(
1810 "labelfontsize",
1811 "Must be greater than or equal to 1",
1812 )
1813 }
1814 self.add_attribute("labelfontsize", AttributeText::from(label_font_size))
1815 }
1816
1817 fn label_target(&mut self, label_target: String) -> &mut Self {
1820 self.add_attribute("labeltarget", AttributeText::escaped(label_target))
1821 }
1822
1823 fn label_tooltip(&mut self, label_tooltip: String) -> &mut Self {
1826 self.add_attribute("labeltooltip", AttributeText::escaped(label_tooltip))
1827 }
1828
1829 fn label_url(&mut self, label_url: String) -> &mut Self {
1832 self.add_attribute("labelurl", AttributeText::escaped(label_url))
1833 }
1834
1835 fn layer(&mut self, layer: String) -> &mut Self {
1836 Attributes::layer(self.get_attributes_mut(), layer);
1837 self
1838 }
1839
1840 fn lhead(&mut self, lhead: String) -> &mut Self {
1841 self.add_attribute("lhead", AttributeText::quoted(lhead))
1842 }
1843
1844 fn label_position(&mut self, lp: Point) -> &mut Self {
1847 Attributes::label_position(self.get_attributes_mut(), lp);
1848 self
1849 }
1850
1851 fn ltail(&mut self, ltail: String) -> &mut Self {
1855 self.add_attribute("ltail", AttributeText::quoted(ltail))
1856 }
1857
1858 fn min_len(&mut self, min_len: u32) -> &mut Self {
1860 self.add_attribute("minlen", AttributeText::from(min_len))
1861 }
1862
1863 fn no_justify(&mut self, no_justify: bool) -> &mut Self {
1864 self.add_attribute("nojustify", AttributeText::from(no_justify))
1865 }
1866
1867 fn pen_width(&mut self, pen_width: f32) -> &mut Self {
1868 Attributes::pen_width(self.get_attributes_mut(), pen_width);
1869 self
1870 }
1871
1872 fn pos(&mut self, pos: Point) -> &mut Self {
1875 Attributes::pos(self.get_attributes_mut(), pos);
1876 self
1877 }
1878
1879 fn same_head(&mut self, same_head: String) -> &mut Self {
1881 self.add_attribute("samehead", AttributeText::quoted(same_head))
1882 }
1883
1884 fn same_tail(&mut self, same_tail: String) -> &mut Self {
1886 self.add_attribute("sametail", AttributeText::quoted(same_tail))
1887 }
1888
1889 fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
1894 Attributes::show_boxes(self.get_attributes_mut(), show_boxes);
1895 self
1896 }
1897
1898 fn style(&mut self, style: EdgeStyle) -> &mut Self {
1900 Attributes::style(self.get_attributes_mut(), Styles::Edge(style));
1901 self
1902 }
1903
1904 fn tail_lp(&mut self, tail_lp: Point) -> &mut Self {
1907 self.add_attribute("tail_lp", AttributeText::from(tail_lp))
1908 }
1909
1910 fn tail_clip(&mut self, tail_clip: bool) -> &mut Self {
1913 self.add_attribute("tailclip", AttributeText::from(tail_clip))
1914 }
1915
1916 fn tail_label(&mut self, tail_label: String) -> &mut Self {
1918 self.add_attribute("taillabel", AttributeText::quoted(tail_label))
1919 }
1920
1921 fn tail_port(&mut self, tail_port: PortPosition) -> &mut Self {
1923 self.add_attribute("tailport", AttributeText::from(tail_port))
1924 }
1925
1926 fn tail_target(&mut self, tail_target: String) -> &mut Self {
1928 self.add_attribute("tailtarget", AttributeText::escaped(tail_target))
1929 }
1930
1931 fn tail_tooltip(&mut self, tail_tooltip: String) -> &mut Self {
1933 self.add_attribute("tailtooltip", AttributeText::escaped(tail_tooltip))
1934 }
1935
1936 fn tail_url(&mut self, tail_url: String) -> &mut Self {
1939 self.add_attribute("tailURL", AttributeText::escaped(tail_url))
1940 }
1941
1942 fn target(&mut self, target: String) -> &mut Self {
1944 self.add_attribute("target", AttributeText::escaped(target))
1945 }
1946
1947 fn tooltip(&mut self, tooltip: String) -> &mut Self {
1953 Attributes::tooltip(self.get_attributes_mut(), tooltip);
1954 self
1955 }
1956
1957 fn url(&mut self, url: String) -> &mut Self {
1959 Attributes::url(self.get_attributes_mut(), url);
1960 self
1961 }
1962
1963 fn weight(&mut self, weight: u32) -> &mut Self {
1967 self.add_attribute("weight", AttributeText::from(weight))
1968 }
1969
1970 fn xlabel(&mut self, xlabel: String) -> &mut Self {
1977 Attributes::xlabel(self.get_attributes_mut(), xlabel);
1978 self
1979 }
1980
1981 fn xlp(&mut self, xlp: Point) -> &mut Self {
1984 Attributes::xlp(self.get_attributes_mut(), xlp);
1985 self
1986 }
1987
1988 fn add_attribute<S: Into<String>>(
1989 &mut self,
1990 key: S,
1991 value: AttributeText<'a>,
1992 ) -> &mut Self;
1993
1994 fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>>;
1995
1996 fn add_validation_error(&mut self, field: &'static str, message: &'static str);
1997}
1998
1999fn escape_double_quotes(val: &String) -> String {
2004 format!("{}", val.chars().map(
2005 |c| if c == '"' {
2006 format!("\\{}", c)
2007 } else {
2008 format!("{}", c)
2009 }).collect::<String>()
2010 )
2011}
2012
2013fn is_alphanum(val: &String) -> bool {
2014 for byte in val.bytes() {
2015 if !((byte >= b'a' && byte <= b'z') || (byte >= b'A' && byte <= b'Z') ||
2016 (byte >= b'0' && byte <= b'9') || byte == b'_' || byte >= 128)
2017 {
2018 return false;
2019 }
2020 }
2021 true
2022}
2023
2024pub(crate) fn fmt_attributes(attributes: &IndexMap<String, AttributeText>) -> String {
2025 let mut dot_string = String::from("");
2026 if !attributes.is_empty() {
2027 dot_string.push_str(" [");
2028 let mut iter = attributes.iter();
2029 let first = iter.next().unwrap();
2030 dot_string.push_str(format!("{}={}", first.0, first.1.dot_string()).as_str());
2031 for (key, value) in iter {
2032 dot_string.push_str(", ");
2033 dot_string.push_str(format!("{}={}", key, value.dot_string()).as_str());
2034 }
2035 dot_string.push_str("]");
2036 }
2037
2038 dot_string
2039}
2040
2041#[cfg(test)]
2042mod test {
2043 use crate::attributes::{
2044 fmt_attributes, AttributeText, Color, GraphAttributeStatementBuilder,
2045 GraphAttributes,
2046 };
2047 use indexmap::map::IndexMap;
2048
2049 #[test]
2050 fn graph_attribute_colorlist_vec_dot_string() {
2051 let graph_attributes = GraphAttributeStatementBuilder::new()
2052 .fill_color_with_iter(&[
2053 (Color::Named("yellow"), Some(0.3)),
2054 (Color::Named("blue"), None),
2055 ])
2056 .build()
2057 .unwrap();
2058 println!("{}", graph_attributes.get("fillcolor").unwrap().dot_string());
2059 assert_eq!(
2060 graph_attributes.get("fillcolor").unwrap().dot_string(),
2061 "\"yellow;0.3:blue\""
2062 );
2063 }
2064
2065 #[test]
2066 fn fmt_attributes_empty_attributes_should_return_empty_string() {
2067 assert_eq!(fmt_attributes(&IndexMap::new()), "");
2068 }
2069
2070 #[test]
2071 fn fmt_attributes_with_single_attribute() {
2072 let mut attributes = IndexMap::new();
2073 attributes.insert("color".to_string(), AttributeText::attr("red"));
2074
2075 assert_eq!(fmt_attributes(&attributes), " [color=red]");
2076 }
2077
2078 #[test]
2079 fn fmt_attributes_with_attributes() {
2080 let mut attributes = IndexMap::new();
2081 attributes.insert("color".to_string(), AttributeText::attr("red"));
2082 attributes.insert("size".to_string(), AttributeText::attr("2"));
2083
2084 assert_eq!(fmt_attributes(&attributes), " [color=red, size=2]");
2085 }
2086}