1use pest::iterators::Pair;
6use pest::Parser;
7
8pub use either;
9use either::Either;
10use std::collections::HashSet;
11use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::iter::FromIterator;
14use std::path::Path;
15
16#[cfg(feature = "to_tokens")]
17use proc_macro2::*;
18#[cfg(feature = "to_tokens")]
19use quote::{quote, ToTokens, TokenStreamExt};
20
21mod parser {
22 use pest_derive::Parser;
23
24 #[derive(Parser)]
25 #[grammar = "parser/dot.pest"]
26 pub struct DotParser;
27}
28
29use self::parser::DotParser;
30use self::parser::Rule;
31
32pub type PestError = pest::error::Error<Rule>;
34pub type IOError = std::io::Error;
36
37#[derive(Debug, Clone)]
41pub enum ParseError<'a> {
42 ExpectRule {
45 expect: Vec<Rule>,
47 found: Rule,
49 },
50 MissingPair {
52 parent: Pair<'a, Rule>,
54 expect: Vec<Rule>,
56 },
57}
58
59impl<'a> ParseError<'a> {
60 fn expect_rule(expect: Vec<Rule>, found: Rule) -> Self {
61 ParseError::ExpectRule { expect, found }
62 }
63
64 fn missing_pair(parent: Pair<'a, Rule>, expect: Vec<Rule>) -> Self {
65 ParseError::MissingPair { parent, expect }
66 }
67}
68
69impl Display for ParseError<'_> {
70 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
71 match self {
72 ParseError::ExpectRule { expect, found } => {
73 let expected = expect
74 .iter()
75 .fold(String::new(), |acc, r| format!("{}\"{:?}\", ", acc, r));
76 write!(
77 f,
78 "Expect one rule of {}but \"{:?}\" found.",
79 expected, found
80 )?;
81 }
82 ParseError::MissingPair { parent, expect } => {
83 let mut expected = expect
84 .iter()
85 .fold(String::new(), |acc, r| format!("{}\"{:?}\", ", acc, r));
86 expected.pop();
88 expected.pop();
89 write!(
90 f,
91 "The Pair:\n{}\nterminates early. Expected one of {}.",
92 parent.as_str(),
93 expected
94 )?;
95 }
96 }
97 Ok(())
98 }
99}
100
101impl Error for ParseError<'_> {}
102
103#[derive(Debug)]
105pub enum GraphFromFileError<'a> {
106 FileError(IOError),
108 PestParseError(PestError),
110 ParseError(ParseError<'a>),
113}
114
115impl Display for GraphFromFileError<'_> {
116 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
117 match self {
118 Self::FileError(e) => write!(f, "{}", e),
119 Self::PestParseError(e) => write!(f, "{}", e),
120 Self::ParseError(e) => write!(f, "{}", e),
121 }
122 }
123}
124
125impl Error for GraphFromFileError<'_> {}
126
127impl From<IOError> for GraphFromFileError<'_> {
128 fn from(e: IOError) -> Self {
129 Self::FileError(e)
130 }
131}
132
133impl From<PestError> for GraphFromFileError<'_> {
134 fn from(e: PestError) -> Self {
135 Self::PestParseError(e)
136 }
137}
138
139impl<'a> From<ParseError<'a>> for GraphFromFileError<'a> {
140 fn from(e: ParseError<'a>) -> Self {
141 Self::ParseError(e)
142 }
143}
144
145#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
146pub struct Graph<A> {
152 pub strict: bool,
156 pub is_digraph: bool,
158 pub name: Option<String>,
160 pub stmts: StmtList<A>,
162}
163
164#[cfg(feature = "to_tokens")]
165impl ToTokens for Graph<(String, String)> {
166 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
167 let name = match &self.name {
168 Some(name) => {
169 quote! { std::option::Option::Some( std::string::ToString::to_string(#name) ) }
170 }
171 None => quote! { std::option::Option::None },
172 };
173 let strict = self.strict;
174 let is_digraph = self.is_digraph;
175 let stmts = &self.stmts;
176 let tokens = quote! {
177 dot_parser::ast::Graph::<(&'static str, &'static str)> {
178 strict: #strict,
179 is_digraph: #is_digraph,
180 name: #name,
181 stmts: #stmts
182 }
183 };
184 ts.append_all(tokens);
185 }
186}
187
188impl<'a, A> TryFrom<Pair<'a, Rule>> for Graph<A>
189where
190 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
191{
192 type Error = ParseError<'a>;
193 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
194 let mut inner = p.clone().into_inner();
195 let mut strict = false;
196 let mut name = None;
197 let mut pair = inner.next().ok_or(ParseError::missing_pair(
198 p.clone(),
199 vec![Rule::strict, Rule::digraph, Rule::graph],
200 ))?;
201 if let Rule::strict = pair.as_rule() {
202 strict = true;
203 pair = inner.next().ok_or(ParseError::missing_pair(
204 p.clone(),
205 vec![Rule::digraph, Rule::graph],
206 ))?;
207 }
208 let is_digraph = match pair.as_rule() {
209 Rule::digraph => true,
210 Rule::graph => false,
211 r => {
212 return Err(ParseError::expect_rule(vec![Rule::digraph, Rule::graph], r));
213 }
214 };
215 pair = inner.next().ok_or(ParseError::missing_pair(
216 p.clone(),
217 vec![Rule::ident, Rule::stmt_list],
218 ))?;
219 if let Rule::ident = pair.as_rule() {
220 name = Some(String::from(pair.as_str()));
221 pair = inner
222 .next()
223 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::stmt_list]))?;
224 }
225 let stmts = StmtList::try_from(pair)?;
226 Ok(Graph {
227 strict,
228 is_digraph,
229 name,
230 stmts,
231 })
232 }
233}
234
235impl Graph<(String, String)> {
236 #[allow(clippy::result_large_err)]
238 pub fn from_file<'a, P>(p: P) -> Result<Self, GraphFromFileError<'a>>
242 where
243 P: AsRef<Path>,
244 {
245 let s = std::fs::read_to_string(p)?;
246 let mut pairs = DotParser::parse(Rule::dotgraph, &s)?;
247 let pair = pairs.next().expect("The toplevel `Pairs` is empty.");
248 match Graph::try_from(pair) {
249 Ok(g) => Ok(g),
250 Err(e) => panic!("{}", e),
251 }
252 }
253}
254
255impl<'a> TryFrom<&'a str> for Graph<(ID<'a>, ID<'a>)> {
256 type Error = PestError;
257 fn try_from(s: &'a str) -> Result<Self, PestError> {
258 let mut pairs = DotParser::parse(Rule::dotgraph, s)?;
259 match pairs.next() {
260 None => {
261 panic!("The toplevel `Pairs` is empty.")
262 }
263 Some(pair) => {
264 match Graph::try_from(pair) {
265 Ok(g) => Ok(g),
266 Err(e) => {
267 panic!("{}", e);
268 },
269 }
270 },
271 }
272 }
273}
274
275impl<A> Graph<A> {
276 pub fn filter_map<B>(self, f: &dyn Fn(A) -> Option<B>) -> Graph<B> {
284 let new_stmts: StmtList<B> = self.stmts.filter_map_attr(f);
285 Graph {
286 strict: self.strict,
287 is_digraph: self.is_digraph,
288 name: self.name,
289 stmts: new_stmts,
290 }
291 }
292
293 pub fn get_node_ids(self) -> HashSet<NodeID> {
295 let clone = self.stmts;
296 clone.get_node_ids()
297 }
298}
299
300pub struct Graphs<A> {
302 pub graphs: Vec<Graph<A>>,
304}
305
306impl<'a, A> TryFrom<Pair<'a, Rule>> for Graphs<A>
307where
308 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
309{
310 type Error = ParseError<'a>;
311 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
312 let mut inner = p.clone().into_inner().into_iter();
313 let first_graph_pair = inner
314 .next()
315 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::dotfile]))?;
316 let mut graphs = vec![Graph::try_from(first_graph_pair)?];
317 for p in inner {
318 graphs.push(Graph::try_from(p)?);
319 }
320 Ok(Graphs { graphs })
321 }
322}
323
324impl<'a> TryFrom<&'a str> for Graphs<(ID<'a>, ID<'a>)> {
325 type Error = PestError;
326 fn try_from(s: &'a str) -> Result<Self, PestError> {
327 DotParser::parse(Rule::dotfile, s).map(|mut p| match p.next() {
328 None => {
329 panic!("The toplevel `Pairs` is empty.")
330 }
331 Some(pair) => match Graphs::try_from(pair) {
332 Ok(g) => g,
333 Err(e) => panic!("{}", e),
334 },
335 })
336 }
337}
338
339impl Graphs<(String, String)> {
340 #[allow(clippy::result_large_err)]
342 pub fn from_file<'a, P>(p: P) -> Result<Self, GraphFromFileError<'a>>
346 where
347 P: AsRef<Path>,
348 {
349 let s = std::fs::read_to_string(p)?;
350 let mut pairs = DotParser::parse(Rule::dotfile, &s)?;
351 let pair = pairs.next().expect("The toplevel `Pairs` is empty.");
352 match Graphs::try_from(pair) {
353 Ok(g) => Ok(g),
354 Err(e) => panic!("{}", e),
355 }
356 }
357}
358
359#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
362pub struct StmtList<A> {
363 pub stmts: Vec<Stmt<A>>,
365}
366
367#[cfg(feature = "to_tokens")]
368impl ToTokens for StmtList<(String, String)> {
369 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
370 let stmts = &self.stmts;
371 let tokens = quote! {
372 dot_parser::ast::StmtList {
373 stmts: std::vec![ #( #stmts ),* ],
374 }
375 };
376 ts.append_all(tokens);
377 }
378}
379
380impl<'a, A> TryFrom<Pair<'a, Rule>> for StmtList<A>
381where
382 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
383{
384 type Error = ParseError<'a>;
385 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
386 let inner = p.into_inner();
387 let mut stmts = Vec::new();
388 for stmt in inner {
389 stmts.push(Stmt::try_from(stmt)?);
390 }
391 Ok(StmtList { stmts })
392 }
393}
394
395impl<'a, A> StmtList<A> {
396 fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> StmtList<B> {
397 self.stmts
398 .into_iter()
399 .map(|stmt| stmt.filter_map_attr(f))
400 .collect()
401 }
402
403 fn get_node_ids(&self) -> HashSet<NodeID> {
404 let mut hs = HashSet::new();
405 for stmt in self {
406 hs = hs.union(&stmt.get_node_ids()).cloned().collect();
407 }
408 hs
409 }
410}
411
412impl<A> IntoIterator for StmtList<A> {
413 type Item = Stmt<A>;
414 type IntoIter = std::vec::IntoIter<Self::Item>;
415
416 fn into_iter(self) -> Self::IntoIter {
417 self.stmts.into_iter()
418 }
419}
420
421impl<'a, A> IntoIterator for &'a StmtList<A> {
422 type Item = &'a Stmt<A>;
423 type IntoIter = std::slice::Iter<'a, Stmt<A>>;
424
425 fn into_iter(self) -> Self::IntoIter {
426 self.stmts.iter()
427 }
428}
429
430impl<A> FromIterator<Stmt<A>> for StmtList<A> {
431 fn from_iter<T>(iter: T) -> Self
432 where
433 T: IntoIterator<Item = Stmt<A>>,
434 {
435 Self {
436 stmts: iter.into_iter().collect(),
437 }
438 }
439}
440
441#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
444pub enum Stmt<A> {
445 NodeStmt(NodeStmt<A>),
447 EdgeStmt(EdgeStmt<A>),
449 AttrStmt(AttrStmt<A>),
451 IDEq(String, String),
453 Subgraph(Subgraph<A>),
455}
456
457#[cfg(feature = "to_tokens")]
458impl ToTokens for Stmt<(String, String)> {
459 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
460 let tokens = match &self {
461 Self::NodeStmt(stmt) => {
462 quote! {
463 dot_parser::ast::Stmt::NodeStmt( #stmt )
464 }
465 }
466 Self::EdgeStmt(stmt) => {
467 quote! {
468 dot_parser::ast::Stmt::EdgeStmt( #stmt )
469 }
470 }
471 Self::AttrStmt(stmt) => {
472 quote! {
473 dot_parser::ast::Stmt::AttrStmt( #stmt )
474 }
475 }
476 Self::IDEq(s1, s2) => {
477 quote! {
478 dot_parser::ast::Stmt::IDEq( std::string::ToString::to_string(#s1), std::string::ToString::to_string(#s2) )
479 }
480 }
481 Self::Subgraph(sub) => {
482 quote! {
483 dot_parser::ast::Stmt::Subgraph( #sub )
484 }
485 }
486 };
487 ts.append_all(tokens);
488 }
489}
490
491impl<'a, A> TryFrom<Pair<'a, Rule>> for Stmt<A>
492where
493 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
494{
495 type Error = ParseError<'a>;
496 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
497 let inner = p
498 .clone()
499 .into_inner()
500 .next()
501 .ok_or(ParseError::missing_pair(
502 p.clone(),
503 std::vec![
504 Rule::node_stmt,
505 Rule::edge_stmt,
506 Rule::attr_stmt,
507 Rule::id_eq,
508 Rule::subgraph,
509 ],
510 ))?;
511 match inner.as_rule() {
512 Rule::node_stmt => NodeStmt::try_from(inner).map(Stmt::NodeStmt),
513 Rule::edge_stmt => EdgeStmt::try_from(inner).map(Stmt::EdgeStmt),
514 Rule::attr_stmt => AttrStmt::try_from(inner).map(Stmt::AttrStmt),
515 Rule::id_eq => {
516 let mut inners = inner.into_inner();
517 let id1 = inners
518 .next()
519 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?
520 .as_str()
521 .into();
522 let id2 = inners
523 .next()
524 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?
525 .as_str()
526 .into();
527 Ok(Stmt::IDEq(id1, id2))
528 }
529 Rule::subgraph => Subgraph::try_from(inner).map(Stmt::Subgraph),
530 other => {
531 let error = ParseError::expect_rule(
532 vec![
533 Rule::node_stmt,
534 Rule::edge_stmt,
535 Rule::attr_stmt,
536 Rule::id_eq,
537 Rule::subgraph,
538 ],
539 other,
540 );
541 Err(error)
542 }
543 }
544 }
545}
546
547impl<'a, A> Stmt<A> {
548 fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> Stmt<B> {
551 match self {
552 Stmt::NodeStmt(node) => Stmt::NodeStmt(node.filter_map_attr(f)),
553 Stmt::EdgeStmt(edge) => Stmt::EdgeStmt(edge.filter_map_attr(f)),
554 Stmt::AttrStmt(attr) => Stmt::AttrStmt(attr.filter_map_attr(f)),
555 Stmt::IDEq(a, b) => Stmt::IDEq(a, b),
556 Stmt::Subgraph(sub) => Stmt::Subgraph(sub.filter_map_attr(f)),
557 }
558 }
559
560 pub fn is_node_stmt(&self) -> bool {
562 matches!(self, Stmt::NodeStmt(_))
563 }
564
565 pub fn get_node_ref(&self) -> Option<&NodeStmt<A>> {
568 if let Stmt::NodeStmt(node) = self {
569 Some(node)
570 } else {
571 None
572 }
573 }
574
575 pub fn get_node(self) -> Option<NodeStmt<A>> {
578 if let Stmt::NodeStmt(node) = self {
579 Some(node)
580 } else {
581 None
582 }
583 }
584
585 pub fn is_edge_stmt(&self) -> bool {
587 matches!(self, Stmt::EdgeStmt(_))
588 }
589
590 pub fn get_edge_ref(&self) -> Option<&EdgeStmt<A>> {
593 if let Stmt::EdgeStmt(edge) = self {
594 Some(edge)
595 } else {
596 None
597 }
598 }
599
600 pub fn get_edge(self) -> Option<EdgeStmt<A>> {
603 if let Stmt::EdgeStmt(edge) = self {
604 Some(edge)
605 } else {
606 None
607 }
608 }
609
610 pub fn is_attr_stmt(&self) -> bool {
612 matches!(self, Stmt::AttrStmt(_))
613 }
614
615 pub fn get_attr_ref(&self) -> Option<&AttrStmt<A>> {
618 if let Stmt::AttrStmt(attr) = self {
619 Some(attr)
620 } else {
621 None
622 }
623 }
624
625 pub fn get_attr(self) -> Option<AttrStmt<A>> {
628 if let Stmt::AttrStmt(attr) = self {
629 Some(attr)
630 } else {
631 None
632 }
633 }
634
635 pub fn is_ideq_stmt(&self) -> bool {
637 matches!(self, Stmt::IDEq(..))
638 }
639
640 pub fn get_ideq_ref(&self) -> Option<(&str, &str)> {
643 if let Stmt::IDEq(id1, id2) = self {
644 Some((id1, id2))
645 } else {
646 None
647 }
648 }
649
650 pub fn is_subgraph(&self) -> bool {
652 matches!(self, Stmt::Subgraph(..))
653 }
654
655 fn get_node_ids(&self) -> HashSet<NodeID> {
657 match self {
658 Stmt::Subgraph(g) => g.get_node_ids(),
659 Stmt::EdgeStmt(e) => e.get_node_ids(),
660 Stmt::NodeStmt(n) => HashSet::from_iter([n.get_node_id().clone()]),
661 _ => HashSet::new(),
662 }
663 }
664}
665
666#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
669pub enum AttrStmt<A> {
670 Graph(AttrList<A>),
672 Node(AttrList<A>),
674 Edge(AttrList<A>),
676}
677
678#[cfg(feature = "to_tokens")]
679impl ToTokens for AttrStmt<(String, String)> {
680 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
681 let new_tokens = match self {
682 AttrStmt::Graph(attrs) => quote! { dot_parser::ast::AttrStmt::Graph(#attrs) },
683 AttrStmt::Node(attrs) => quote! { dot_parser::ast::AttrStmt::Node(#attrs) },
684 AttrStmt::Edge(attrs) => quote! { dot_parser::ast::AttrStmt::Edge(#attrs) },
685 };
686 ts.append_all(new_tokens);
687 }
688}
689
690impl<'a, A> TryFrom<Pair<'a, Rule>> for AttrStmt<A>
691where
692 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
693{
694 type Error = ParseError<'a>;
695 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
696 let mut inners = p.clone().into_inner();
697 let kind = inners
698 .next()
699 .ok_or(ParseError::missing_pair(
700 p.clone(),
701 vec![Rule::graph, Rule::node, Rule::edge],
702 ))?
703 .as_rule();
704 let attr_list_pair = inners
705 .next()
706 .ok_or(ParseError::missing_pair(p, vec![Rule::attr_list]))?;
707 let attr = AttrList::try_from(attr_list_pair)?;
708 match kind {
709 Rule::graph => Ok(AttrStmt::Graph(attr)),
710 Rule::node => Ok(AttrStmt::Node(attr)),
711 Rule::edge => Ok(AttrStmt::Edge(attr)),
712 r => Err(ParseError::expect_rule(
713 vec![Rule::graph, Rule::node, Rule::edge],
714 r,
715 )),
716 }
717 }
718}
719
720impl<A> AttrStmt<A> {
721 pub(crate) fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> AttrStmt<B> {
722 match self {
723 AttrStmt::Graph(attr) => AttrStmt::Graph(attr.filter_map_attr(f)),
724 AttrStmt::Node(attr) => AttrStmt::Node(attr.filter_map_attr(f)),
725 AttrStmt::Edge(attr) => AttrStmt::Edge(attr.filter_map_attr(f)),
726 }
727 }
728}
729
730#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
736pub struct AttrList<A> {
737 pub elems: Vec<AList<A>>,
739}
740
741#[cfg(feature = "to_tokens")]
742impl ToTokens for AttrList<(String, String)> {
743 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
744 let elems = &self.elems;
745 let tokens = quote! {
746 dot_parser::ast::AttrList {
747 elems: std::vec![ #( #elems ),* ]
748 }
749 };
750 ts.append_all(tokens);
751 }
752}
753
754impl<'a, A> TryFrom<Pair<'a, Rule>> for AttrList<A>
755where
756 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
757{
758 type Error = ParseError<'a>;
759 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
760 let mut v: Vec<AList<A>> = Vec::new();
761 let mut inners = p.clone().into_inner();
762 let alist_pair = inners
763 .next()
764 .ok_or(ParseError::missing_pair(p, vec![Rule::a_list]))?;
765 let alist = AList::try_from(alist_pair)?;
766 let mut tail = inners
767 .next()
768 .map(|p| {
769 AttrList::try_from(p)
770 .map(|alist| alist.elems)
771 .unwrap_or_default()
772 })
773 .unwrap_or_default();
774 v.push(alist);
775 v.append(&mut tail);
776
777 Ok(AttrList { elems: v })
778 }
779}
780
781impl<A> AttrList<A> {
782 pub(crate) fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> AttrList<B> {
783 AttrList {
784 elems: self
785 .into_iter()
786 .map(|alist| alist.filter_map_attr(f))
787 .collect(),
788 }
789 }
790
791 pub fn flatten(self) -> AList<A> {
794 self.into()
795 }
796
797 pub fn flatten_ref(&self) -> AList<&A> {
800 self.into()
801 }
802}
803
804impl<A> FromIterator<AList<A>> for AttrList<A> {
805 fn from_iter<T>(iter: T) -> Self
806 where
807 T: IntoIterator<Item = AList<A>>,
808 {
809 Self {
810 elems: iter.into_iter().map(|u| u.into_iter().collect()).collect(),
811 }
812 }
813}
814
815impl<A> IntoIterator for AttrList<A> {
816 type Item = AList<A>;
817 type IntoIter = std::vec::IntoIter<Self::Item>;
818
819 fn into_iter(self) -> Self::IntoIter {
820 self.elems.into_iter()
821 }
822}
823
824impl<'a, A> IntoIterator for &'a AttrList<A> {
825 type Item = &'a AList<A>;
826 type IntoIter = std::slice::Iter<'a, AList<A>>;
827
828 fn into_iter(self) -> Self::IntoIter {
829 self.elems.iter()
830 }
831}
832
833#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
836pub struct AList<A> {
837 pub elems: Vec<A>,
839}
840
841#[cfg(feature = "to_tokens")]
842impl ToTokens for AList<(String, String)> {
843 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
844 let elems = &self.elems;
845 let elems = elems.iter().map(|(s1, s2)| quote! { (#s1, #s2) });
846 let tokens = quote! {
847 dot_parser::ast::AList {
848 elems: std::vec![ #( #elems ),* ]
849 }
850 };
851 ts.append_all(tokens);
852 }
853}
854
855#[cfg(feature = "display")]
856impl<A> Display for AList<A>
857where
858 A: Display,
859{
860 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
861 for attr in &self.elems {
862 write!(f, "{}; ", attr)?;
863 }
864 Ok(())
865 }
866}
867
868impl<'a> TryFrom<Pair<'a, Rule>> for AList<(ID<'a>, ID<'a>)> {
869 type Error = ParseError<'a>;
870 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
871 let mut v = Vec::new();
872 let mut inners = p.clone().into_inner();
873 let p_id1 = inners
874 .next()
875 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?;
876 let id1 = ID::try_from(p_id1)?;
877 let p_id2 = inners
878 .next()
879 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?;
880 let id2 = ID::try_from(p_id2)?;
881 let mut tail = inners
882 .next()
883 .map(|p| {
884 AList::try_from(p)
885 .map(|alist| alist.elems)
886 .unwrap_or_default()
887 })
888 .unwrap_or_default();
889 v.push((id1, id2));
890 v.append(&mut tail);
891
892 Ok(AList { elems: v })
893 }
894}
895
896impl<'a> TryFrom<Pair<'a, Rule>> for AList<(String, String)> {
897 type Error = ParseError<'a>;
898 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
899 let mut v: Vec<(String, String)> = Vec::new();
900 let mut inners = p.clone().into_inner();
901 let id1 = inners
902 .next()
903 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?
904 .as_str()
905 .into();
906 let id2 = inners
907 .next()
908 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::ident]))?
909 .as_str()
910 .into();
911 let mut tail = inners
912 .next()
913 .map(|p| {
914 AList::try_from(p)
915 .map(|alist| alist.elems)
916 .unwrap_or_default()
917 })
918 .unwrap_or_default();
919 v.push((id1, id2));
920 v.append(&mut tail);
921
922 Ok(AList { elems: v })
923 }
924}
925
926impl<A> AList<A> {
927 pub(crate) fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> AList<B> {
928 AList {
929 elems: self.into_iter().filter_map(f).collect(),
930 }
931 }
932
933 pub(crate) fn empty() -> Self {
934 AList { elems: Vec::new() }
935 }
936
937 #[cfg(feature = "display")]
938 pub(crate) fn is_empty(&self) -> bool {
940 self.elems.is_empty()
941 }
942}
943
944impl<A> FromIterator<A> for AList<A> {
945 fn from_iter<T>(iter: T) -> Self
946 where
947 T: IntoIterator<Item = A>,
948 {
949 Self {
950 elems: iter.into_iter().collect(),
951 }
952 }
953}
954
955impl<A> IntoIterator for AList<A> {
956 type Item = A;
957 type IntoIter = std::vec::IntoIter<Self::Item>;
958
959 fn into_iter(self) -> Self::IntoIter {
960 self.elems.into_iter()
961 }
962}
963
964impl<'a, A> IntoIterator for &'a AList<A> {
965 type Item = &'a A;
966 type IntoIter = std::slice::Iter<'a, A>;
967
968 fn into_iter(self) -> Self::IntoIter {
969 self.elems.iter()
970 }
971}
972
973impl<A> From<AttrList<A>> for AList<A> {
974 fn from(attr: AttrList<A>) -> Self {
975 attr.into_iter().flatten().collect()
976 }
977}
978
979impl<'a, A> From<&'a AttrList<A>> for AList<&'a A> {
980 fn from(attr: &'a AttrList<A>) -> Self {
981 attr.into_iter().flatten().collect()
982 }
983}
984
985#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
988pub struct EdgeStmt<A> {
989 pub from: Either<NodeID, Subgraph<A>>,
991 pub next: EdgeRHS<A>,
993 pub attr: Option<AttrList<A>>,
995}
996
997#[cfg(feature = "to_tokens")]
998impl ToTokens for EdgeStmt<(String, String)> {
999 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1000 let from = match &self.from {
1001 Either::Left(id) => {
1002 quote! { dot_parser::ast::either::Either::Left( #id ) }
1003 }
1004 Either::Right(sub) => {
1005 quote! { dot_parser::ast::either::Either::Right( #sub ) }
1006 }
1007 };
1008 let attr = match &self.attr {
1009 Some(attr) => {
1010 quote! { std::option::Option::Some( #attr ) }
1011 }
1012 None => {
1013 quote! { std::option::Option::None }
1014 }
1015 };
1016 let next = &self.next;
1017 let tokens = quote! {
1018 dot_parser::ast::EdgeStmt {
1019 from: #from,
1020 next: #next,
1021 attr: #attr,
1022 }
1023 };
1024 ts.append_all(tokens);
1025 }
1026}
1027
1028impl<'a, A> TryFrom<Pair<'a, Rule>> for EdgeStmt<A>
1029where
1030 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
1031{
1032 type Error = ParseError<'a>;
1033 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1034 let mut inners = p.clone().into_inner();
1035 let from_pair = inners.next().ok_or(ParseError::missing_pair(
1036 p.clone(),
1037 vec![Rule::node_id, Rule::subgraph],
1038 ))?;
1039 let rule = from_pair.as_rule();
1040 let from = match rule {
1041 Rule::node_id => Either::Left(NodeID::try_from(from_pair)?),
1042 Rule::subgraph => Either::Right(Subgraph::try_from(from_pair)?),
1043 r => {
1044 return Err(ParseError::expect_rule(
1045 vec![Rule::node_id, Rule::subgraph],
1046 r,
1047 ));
1048 }
1049 };
1050 let next = inners
1051 .next()
1052 .map(EdgeRHS::try_from)
1053 .transpose()?
1054 .ok_or(ParseError::missing_pair(p, vec![Rule::edge_rhs]))?;
1055 let attr = inners.next().map(AttrList::try_from).transpose()?;
1056
1057 Ok(EdgeStmt { from, next, attr })
1058 }
1059}
1060
1061impl<'a, A> EdgeStmt<A> {
1062 fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> EdgeStmt<B> {
1063 let new_from = match self.from {
1064 Either::Left(node_id) => Either::Left(node_id),
1065 Either::Right(subgraph) => Either::Right(subgraph.filter_map_attr(f)),
1066 };
1067
1068 let new_next = self.next.filter_map_attr(f);
1069
1070 EdgeStmt {
1071 from: new_from,
1072 next: new_next,
1073 attr: self.attr.map(|a| a.filter_map_attr(f)),
1074 }
1075 }
1076
1077 pub fn flatten(self) -> Vec<EdgeStmt<A>>
1079 where
1080 A: Clone,
1081 {
1082 let mut from = self.from;
1083 let mut to = self.next;
1084 let attr = self.attr;
1085
1086 let mut v = Vec::new();
1087
1088 loop {
1089 let next_step = EdgeStmt {
1090 from: from.clone(),
1091 next: EdgeRHS {
1092 to: to.to.clone(),
1093 next: None,
1094 },
1095 attr: attr.clone(),
1096 };
1097 v.push(next_step);
1098 match to.next {
1099 None => return v,
1100 Some(rhs) => {
1101 from = to.to;
1102 to = *rhs;
1103 }
1104 }
1105 }
1106 }
1107
1108 fn get_node_ids(&self) -> HashSet<NodeID> {
1109 let mut nexts = self.next.get_node_ids();
1110 match &self.from {
1111 Either::Left(node_id) => {
1112 nexts.insert(node_id.clone());
1113 }
1114 Either::Right(subgraph) => {
1115 return nexts.union(&subgraph.get_node_ids()).cloned().collect();
1116 }
1117 };
1118 nexts
1119 }
1120}
1121
1122#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1127pub struct EdgeRHS<A> {
1128 pub to: Either<NodeID, Subgraph<A>>,
1130 pub next: Option<Box<EdgeRHS<A>>>,
1132}
1133
1134#[cfg(feature = "to_tokens")]
1135impl ToTokens for EdgeRHS<(String, String)> {
1136 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1137 let to = match &self.to {
1138 Either::Left(id) => quote! { dot_parser::ast::either::Either::Left( #id ) },
1139 Either::Right(sub) => quote! { dot_parser::ast::either::Either::Right( #sub ) },
1140 };
1141 let next = match &self.next {
1142 Some(next) => quote! {
1143 std::option::Option::Some(
1144 std::boxed::Box::new( #next )
1145 )
1146 },
1147 None => quote! {
1148 std::option::Option::None
1149 },
1150 };
1151 let tokens = quote! {
1152 dot_parser::ast::EdgeRHS {
1153 to: #to,
1154 next: #next,
1155 }
1156 };
1157 ts.append_all(tokens);
1158 }
1159}
1160
1161impl<'a, A> TryFrom<Pair<'a, Rule>> for EdgeRHS<A>
1162where
1163 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
1164{
1165 type Error = ParseError<'a>;
1166 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1167 let mut inners = p.clone().into_inner();
1168 let to_pair = inners.next().ok_or(ParseError::missing_pair(
1169 p,
1170 vec![Rule::node_id, Rule::subgraph],
1171 ))?;
1172 let to = match to_pair.as_rule() {
1173 Rule::node_id => Either::Left(NodeID::try_from(to_pair)?),
1174 Rule::subgraph => {
1175 Either::Right(Subgraph::try_from(to_pair)?)
1177 }
1178 r => {
1179 return Err(ParseError::expect_rule(
1180 vec![Rule::node_id, Rule::subgraph],
1181 r,
1182 ));
1183 }
1184 };
1185 let next = inners
1186 .next()
1187 .map(EdgeRHS::try_from)
1188 .transpose()?
1189 .map(Box::new);
1190 Ok(EdgeRHS { to, next })
1191 }
1192}
1193
1194impl<'a, A> EdgeRHS<A> {
1195 fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> EdgeRHS<B> {
1196 let to = match self.to {
1197 Either::Left(node_id) => Either::Left(node_id),
1198 Either::Right(subgraph) => Either::Right(subgraph.filter_map_attr(f)),
1199 };
1200
1201 let next = self.next.map(|e| Box::new(e.filter_map_attr(f)));
1202
1203 EdgeRHS { to, next }
1204 }
1205
1206 fn get_node_ids(&self) -> HashSet<NodeID> {
1207 let mut nexts: HashSet<NodeID> = self
1208 .next
1209 .as_ref()
1210 .map(|n| n.get_node_ids())
1211 .unwrap_or_default();
1212 match &self.to {
1213 Either::Left(node_id) => {
1214 nexts.insert(node_id.clone());
1215 }
1216 Either::Right(subgraph) => {
1217 return nexts.union(&subgraph.get_node_ids()).cloned().collect();
1218 }
1219 };
1220 nexts
1221 }
1222}
1223
1224#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1227pub struct NodeStmt<A> {
1228 pub node: NodeID,
1230 pub attr: Option<AttrList<A>>,
1232}
1233
1234#[cfg(feature = "to_tokens")]
1235impl ToTokens for NodeStmt<(String, String)> {
1236 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1237 let node = &self.node;
1238 let attrs = match &self.attr {
1239 Some(a) => quote! { std::option::Option::Some(#a) },
1240 None => quote! { std::option::Option::None },
1241 };
1242 let tokens = quote! {
1243 dot_parser::ast::NodeStmt {
1244 node: #node,
1245 attr: #attrs
1246 }
1247 };
1248 ts.append_all(tokens);
1249 }
1250}
1251
1252impl<'a, A> TryFrom<Pair<'a, Rule>> for NodeStmt<A>
1253where
1254 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
1255{
1256 type Error = ParseError<'a>;
1257 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1258 let mut inners = p.clone().into_inner();
1259 let node = inners
1260 .next()
1261 .map(NodeID::try_from)
1262 .transpose()?
1263 .ok_or(ParseError::missing_pair(p, vec![Rule::node_id]))?;
1264 let attr = inners.next().map(AttrList::try_from).transpose()?;
1265 Ok(NodeStmt { node, attr })
1266 }
1267}
1268
1269impl<A> NodeStmt<A> {
1270 pub(crate) fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> NodeStmt<B> {
1271 NodeStmt {
1272 node: self.node,
1273 attr: self.attr.map(|a| a.filter_map_attr(f)),
1274 }
1275 }
1276
1277 pub fn name(&self) -> &str {
1280 &self.node.id
1281 }
1282
1283 pub(crate) fn get_node_id(&self) -> &NodeID {
1284 &self.node
1285 }
1286}
1287
1288#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1291pub struct NodeID {
1292 pub id: String,
1294 pub port: Option<Port>,
1296}
1297
1298#[cfg(feature = "to_tokens")]
1299impl ToTokens for NodeID {
1300 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1301 let id = &self.id;
1302 let port = match &self.port {
1303 Some(port) => quote! { std::option::Option::Some(#port) },
1304 None => quote! { std::option::Option::None },
1305 };
1306 let tokens = quote! {
1307 dot_parser::ast::NodeID {
1308 id: std::string::ToString::to_string(#id),
1309 port: #port,
1310 }
1311 };
1312 ts.append_all(tokens);
1313 }
1314}
1315
1316impl<'a> TryFrom<Pair<'a, Rule>> for NodeID {
1317 type Error = ParseError<'a>;
1318 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1319 let mut inners = p.clone().into_inner();
1320 let id = inners
1321 .next()
1322 .ok_or(ParseError::missing_pair(p, vec![Rule::node_id]))?
1323 .as_str()
1324 .to_string();
1325 let port = inners.next().map(Port::try_from).transpose()?;
1326 Ok(NodeID { id, port })
1327 }
1328}
1329
1330#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
1332pub enum Port {
1333 ID(String, Option<CompassPt>),
1335 Compass(CompassPt),
1337}
1338
1339#[cfg(feature = "to_tokens")]
1340impl ToTokens for Port {
1341 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1342 let tokens = match self {
1343 Port::ID(s, cpss) => match cpss {
1344 Some(cpss) => {
1345 quote! { dot_parser::ast::Port::ID(std::string::ToString::to_string(#s), std::option::Option::Some(#cpss)) }
1346 }
1347 None => {
1348 quote! { dot_parser::ast::Port::ID(std::string::ToString::to_string(#s), std::option::Option::None) }
1349 }
1350 },
1351 Port::Compass(cpss) => {
1352 quote! { dot_parser::ast::Port::Compass(#cpss) }
1353 }
1354 };
1355 ts.append_all(tokens);
1356 }
1357}
1358
1359impl<'a> TryFrom<Pair<'a, Rule>> for Port {
1360 type Error = ParseError<'a>;
1361 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1362 let mut inners = p.clone().into_inner();
1363 let inner = inners.next().ok_or(ParseError::missing_pair(
1364 p,
1365 vec![Rule::compass_pt, Rule::ident],
1366 ))?;
1367 match inner.as_rule() {
1368 Rule::compass_pt => Ok(Port::Compass(CompassPt::try_from(inner)?)),
1369 Rule::ident => {
1370 let opt_comp = inners.next().map(CompassPt::try_from).transpose()?;
1371 Ok(Port::ID(inner.as_str().to_string(), opt_comp))
1372 }
1373 r => Err(ParseError::expect_rule(
1374 vec![Rule::compass_pt, Rule::ident],
1375 r,
1376 )),
1377 }
1378 }
1379}
1380
1381#[cfg(feature = "display")]
1382impl Display for Port {
1383 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1384 match self {
1385 Port::ID(name, Some(cpss)) => {
1386 write!(f, ": {name} : {cpss}")
1387 }
1388 Port::ID(name, None) => {
1389 write!(f, ": {name}")
1390 }
1391 Port::Compass(cpss) => {
1392 write!(f, ": {}", cpss)
1393 }
1394 }
1395 }
1396}
1397
1398#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1400pub struct Subgraph<A> {
1401 pub id: Option<String>,
1403 pub stmts: StmtList<A>,
1405}
1406
1407#[cfg(feature = "to_tokens")]
1408impl ToTokens for Subgraph<(String, String)> {
1409 fn to_tokens(&self, ts: &mut proc_macro2::TokenStream) {
1410 let id = match &self.id {
1411 Some(s) => quote! { std::option::Option::Some(#s) },
1412 None => quote! { std::option::Option::None },
1413 };
1414 let stmts = &self.stmts;
1415 let tokens = quote! {
1416 dot_parser::ast::Subgraph {
1417 id: #id,
1418 stmts: #stmts,
1419 }
1420 };
1421 ts.append_all(tokens);
1422 }
1423}
1424
1425impl<'a, A> TryFrom<Pair<'a, Rule>> for Subgraph<A>
1426where
1427 AList<A>: TryFrom<Pair<'a, Rule>, Error = ParseError<'a>>,
1428{
1429 type Error = ParseError<'a>;
1430 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1431 let mut inners = p.clone().into_inner();
1432 let mut inner = inners.next().ok_or(ParseError::missing_pair(
1433 p.clone(),
1434 vec![Rule::ident, Rule::stmt_list],
1435 ))?;
1436 let id = if let Rule::ident = inner.as_rule() {
1437 let id_str = inner.as_str().to_string();
1438 inner = inners
1439 .next()
1440 .ok_or(ParseError::missing_pair(p.clone(), vec![Rule::stmt_list]))?;
1441 Some(id_str)
1442 } else {
1443 None
1444 };
1445 let stmts = StmtList::try_from(inner)?;
1446 Ok(Subgraph { id, stmts })
1447 }
1448}
1449
1450impl<A> Subgraph<A> {
1451 fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> Subgraph<B> {
1452 Subgraph {
1453 id: self.id,
1454 stmts: self
1455 .stmts
1456 .into_iter()
1457 .map(|stmt| stmt.filter_map_attr(f))
1458 .collect(),
1459 }
1460 }
1461
1462 pub(crate) fn into_graph(self, strict: bool, is_digraph: bool) -> Graph<A> {
1464 Graph {
1465 strict,
1466 is_digraph,
1467 name: self.id.map(String::from),
1468 stmts: self.stmts,
1469 }
1470 }
1471
1472 fn get_node_ids(&self) -> HashSet<NodeID> {
1473 self.stmts.get_node_ids()
1474 }
1475}
1476
1477#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
1479pub enum CompassPt {
1480 N,
1482 NE,
1484 E,
1486 SE,
1488 S,
1490 SW,
1492 W,
1494 NW,
1496 C,
1498 Underscore,
1500}
1501
1502#[cfg(feature = "display")]
1503impl Display for CompassPt {
1504 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1505 match self {
1506 CompassPt::N => write!(f, "n"),
1507 CompassPt::NE => write!(f, "ne"),
1508 CompassPt::E => write!(f, "e"),
1509 CompassPt::SE => write!(f, "se"),
1510 CompassPt::S => write!(f, "s"),
1511 CompassPt::SW => write!(f, "sw"),
1512 CompassPt::W => write!(f, "w"),
1513 CompassPt::NW => write!(f, "nw"),
1514 CompassPt::C => write!(f, "c"),
1515 CompassPt::Underscore => write!(f, "_"),
1516 }
1517 }
1518}
1519
1520impl<'a> TryFrom<Pair<'a, Rule>> for CompassPt {
1521 type Error = ParseError<'a>;
1522 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1523 match p
1524 .clone()
1525 .into_inner()
1526 .next()
1527 .ok_or(ParseError::missing_pair(
1528 p,
1529 vec![
1530 Rule::n,
1531 Rule::ne,
1532 Rule::e,
1533 Rule::se,
1534 Rule::s,
1535 Rule::sw,
1536 Rule::w,
1537 Rule::nw,
1538 Rule::c,
1539 Rule::underscore,
1540 ],
1541 ))?
1542 .as_rule()
1543 {
1544 Rule::n => Ok(CompassPt::N),
1545 Rule::ne => Ok(CompassPt::NE),
1546 Rule::e => Ok(CompassPt::E),
1547 Rule::se => Ok(CompassPt::SE),
1548 Rule::s => Ok(CompassPt::S),
1549 Rule::sw => Ok(CompassPt::SW),
1550 Rule::w => Ok(CompassPt::W),
1551 Rule::nw => Ok(CompassPt::NW),
1552 Rule::c => Ok(CompassPt::C),
1553 Rule::underscore => Ok(CompassPt::Underscore),
1554 r => Err(ParseError::expect_rule(
1555 vec![
1556 Rule::n,
1557 Rule::ne,
1558 Rule::e,
1559 Rule::se,
1560 Rule::s,
1561 Rule::sw,
1562 Rule::w,
1563 Rule::nw,
1564 Rule::c,
1565 Rule::underscore,
1566 ],
1567 r,
1568 )),
1569 }
1570 }
1571}
1572
1573#[cfg(feature = "to_tokens")]
1574impl ToTokens for CompassPt {
1575 fn to_tokens(&self, tokens: &mut TokenStream) {
1576 let new_tokens = match self {
1577 CompassPt::N => quote! { dot_parser::ast::CompassPt::N},
1578 CompassPt::NE => quote! { dot_parser::ast::CompassPt::NE},
1579 CompassPt::E => quote! { dot_parser::ast::CompassPt::E},
1580 CompassPt::SE => quote! { dot_parser::ast::CompassPt::SE},
1581 CompassPt::S => quote! { dot_parser::ast::CompassPt::S},
1582 CompassPt::SW => quote! { dot_parser::ast::CompassPt::SW},
1583 CompassPt::W => quote! { dot_parser::ast::CompassPt::W},
1584 CompassPt::NW => quote! { dot_parser::ast::CompassPt::NW},
1585 CompassPt::C => quote! { dot_parser::ast::CompassPt::C},
1586 CompassPt::Underscore => quote! { dot_parser::ast::CompassPt::Underscore},
1587 };
1588 tokens.append_all(new_tokens);
1589 }
1590}
1591
1592#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
1605pub struct ID<'a>(&'a str);
1606
1607impl<'a> TryFrom<Pair<'a, Rule>> for ID<'a> {
1608 type Error = ParseError<'a>;
1609 fn try_from(p: Pair<'a, Rule>) -> Result<Self, ParseError<'a>> {
1610 let inner = p
1611 .clone()
1612 .into_inner()
1613 .next()
1614 .ok_or(ParseError::missing_pair(
1615 p.clone(),
1616 vec![Rule::ident1, Rule::numeral, Rule::quote, Rule::html],
1617 ))?;
1618 let id = match inner.as_rule() {
1619 Rule::ident1 => ID(inner.as_str()),
1620 Rule::numeral => ID(inner.as_str()),
1621 Rule::quote => {
1622 let mut inner = inner.into_inner();
1623 inner.next()
1624 .ok_or(ParseError::missing_pair(
1625 p.clone(),
1626 vec![Rule::quotemark],
1627 ))?;
1628 let text =
1629 inner.next()
1630 .ok_or(ParseError::missing_pair(
1631 p.clone(),
1632 vec![Rule::quote_escaped],
1633 ))?;
1634 ID(text.as_str())
1635 },
1636 Rule::html => ID(inner.as_str()),
1637 _ => Err(ParseError::expect_rule(
1638 vec![Rule::ident1, Rule::numeral, Rule::quote, Rule::html],
1639 p.as_rule(),
1640 ))?,
1641 };
1642 Ok(id)
1643 }
1644}
1645
1646#[cfg(feature = "display")]
1647impl Display for ID<'_> {
1648 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1649 write!(f, "{}", self.0)
1650 }
1651}
1652
1653impl Into<String> for ID<'_> {
1654 fn into(self) -> String {
1655 self.0.to_string()
1656 }
1657}