Skip to main content

links_notation/
lib.rs

1pub mod binary;
2pub mod comments;
3pub mod cst;
4pub mod format_config;
5pub mod parser;
6pub mod parser_config;
7mod quotes;
8pub mod reference_literal;
9pub use reference_literal::{decode_reference_literal, encode_reference_literal};
10pub mod stream_parser;
11pub mod syntax;
12
13use comments::strip_comments;
14pub use cst::{CstKind, CstNode, parse_lino_to_cst, parse_lino_to_cst_with_config};
15use format_config::FormatConfig;
16pub use parser_config::ParserConfig;
17use std::borrow::Cow;
18pub use stream_parser::{
19    ErrorLocation, StreamIterator, StreamParseError, StreamParser, StreamPosition, StreamReference,
20};
21pub use syntax::{SyntaxReference, reference_quote};
22
23// Re-export the lino! macro when the macro feature is enabled
24#[cfg(feature = "macro")]
25pub use links_notation_macro::lino;
26use std::error::Error as StdError;
27use std::fmt;
28
29/// The version of this crate, taken from `Cargo.toml` at compile time.
30///
31/// A tool that reports which parser produced a result should read it from here
32/// rather than from its own package, which is how the benchmark report came to
33/// claim the version of the benchmark instead of the version of the parser.
34///
35/// # Examples
36/// ```
37/// assert!(!links_notation::VERSION.is_empty());
38/// ```
39pub const VERSION: &str = env!("CARGO_PKG_VERSION");
40
41/// Error type for Lino parsing
42#[derive(Debug)]
43pub enum ParseError {
44    /// Input string is empty or contains only whitespace
45    EmptyInput,
46    /// The document does not parse, and this is where it stopped
47    SyntaxError(SyntaxError),
48    /// The document nests links deeper than [`ParserConfig::max_depth`], and
49    /// this is where the level that is one too deep opens
50    NestingTooDeep(NestingTooDeep),
51    /// Internal parser error
52    InternalError(String),
53}
54
55impl fmt::Display for ParseError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            ParseError::EmptyInput => write!(f, "Empty input"),
59            ParseError::SyntaxError(error) => write!(f, "Syntax error at {}", error),
60            ParseError::NestingTooDeep(error) => write!(f, "Nesting too deep at {}", error),
61            ParseError::InternalError(msg) => write!(f, "Internal error: {}", msg),
62        }
63    }
64}
65
66impl StdError for ParseError {}
67
68/// The number of characters of the offending line an error message quotes.
69///
70/// A message has to fit in a log line, and the whole point of quoting one line
71/// of context is that the message does not grow with the size of the document.
72const QUOTED_LINE_WIDTH: usize = 80;
73
74/// What a message writes in place of the part of a long line it left out.
75const ELLIPSIS: &str = "...";
76
77/// A syntax error, with the position in the document it was found at.
78///
79/// The position is the furthest one the parser reached, which is the character
80/// the document stops making sense at rather than the point the last
81/// alternative gave up on.
82///
83/// # Examples
84/// ```
85/// use links_notation::{parse_lino, ParseError};
86///
87/// let error = parse_lino("ci_gate x\nstage: rust: nextest\n").unwrap_err();
88/// let ParseError::SyntaxError(error) = error else { panic!("expected a syntax error") };
89/// assert_eq!((error.line, error.column), (2, 12));
90/// assert_eq!(error.found, Some(':'));
91/// ```
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct SyntaxError {
94    /// Byte offset of the offending position from the start of the document.
95    pub offset: usize,
96    /// Line the offending position is on, counted from 1.
97    pub line: usize,
98    /// Column the offending position is at, in characters, counted from 1.
99    pub column: usize,
100    /// What could have continued the document at this position. Empty when the
101    /// parser stopped somewhere it names no expectation for.
102    pub expected: Vec<String>,
103    /// The character found instead, or `None` at the end of the document.
104    pub found: Option<char>,
105    /// The offending line, as written, without its line ending.
106    pub line_text: String,
107}
108
109impl SyntaxError {
110    /// The one-line summary: where the parser stopped, what could have stood
111    /// there and what does.
112    ///
113    /// # Examples
114    /// ```
115    /// use links_notation::{parse_lino, ParseError};
116    ///
117    /// let ParseError::SyntaxError(error) = parse_lino("a: b: c").unwrap_err() else {
118    ///     panic!("expected a syntax error")
119    /// };
120    /// assert_eq!(
121    ///     error.summary(),
122    ///     r#"line 1, column 5: expected "(", a reference or end of line, found ":""#
123    /// );
124    /// ```
125    pub fn summary(&self) -> String {
126        let found = match self.found {
127            Some(character) => format!("\"{}\"", character.escape_debug()),
128            None => "end of input".to_string(),
129        };
130        match join_alternatives(&self.expected) {
131            Some(expected) => format!(
132                "line {}, column {}: expected {}, found {}",
133                self.line, self.column, expected, found
134            ),
135            None => format!(
136                "line {}, column {}: unexpected {}",
137                self.line, self.column, found
138            ),
139        }
140    }
141
142    /// The offending line with a caret under the offending column, quoted the
143    /// way `rustc` quotes source.
144    ///
145    /// A long line is shown as a window around the caret, so the message stays
146    /// the same size whether the document has ten lines or fifteen hundred.
147    ///
148    /// # Examples
149    /// ```
150    /// use links_notation::{parse_lino, ParseError};
151    ///
152    /// let ParseError::SyntaxError(error) = parse_lino("a: b: c").unwrap_err() else {
153    ///     panic!("expected a syntax error")
154    /// };
155    /// assert_eq!(error.snippet(), "1 | a: b: c\n  |     ^");
156    /// ```
157    pub fn snippet(&self) -> String {
158        snippet(self.line, &self.line_text, self.column)
159    }
160}
161
162impl fmt::Display for SyntaxError {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        write!(f, "{}\n{}", self.summary(), self.snippet())
165    }
166}
167
168impl StdError for SyntaxError {}
169
170/// A document that nests links deeper than the parser allows, with the position
171/// of the level that is one too deep.
172///
173/// Every parenthesized group and every indentation level is one level of
174/// nesting. The parser recurses once per level, so the limit is what turns a
175/// document that would overflow the stack, and abort the process, into an error.
176///
177/// # Examples
178/// ```
179/// use links_notation::{parse_lino_with_config, ParseError, ParserConfig};
180///
181/// let config = ParserConfig::new().with_max_depth(1);
182/// assert!(parse_lino_with_config("(a (b))", &config).is_err());
183/// let Err(ParseError::NestingTooDeep(error)) = parse_lino_with_config("((a))", &config) else {
184///     panic!("expected the nesting to be too deep")
185/// };
186/// assert_eq!((error.max_depth, error.line, error.column), (1, 1, 2));
187/// assert_eq!(
188///     error.to_string(),
189///     "line 1, column 2: nesting depth exceeds the maximum of 1\n1 | ((a))\n  |  ^"
190/// );
191/// ```
192#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct NestingTooDeep {
194    /// The deepest nesting the parser was configured to accept.
195    pub max_depth: usize,
196    /// Byte offset of the offending position from the start of the document.
197    pub offset: usize,
198    /// Line the offending position is on, counted from 1.
199    pub line: usize,
200    /// Column the offending position is at, in characters, counted from 1.
201    pub column: usize,
202    /// The offending line, as written, without its line ending.
203    pub line_text: String,
204}
205
206impl NestingTooDeep {
207    /// The one-line summary: where the nesting got too deep and how deep it may go.
208    pub fn summary(&self) -> String {
209        format!(
210            "line {}, column {}: nesting depth exceeds the maximum of {}",
211            self.line, self.column, self.max_depth
212        )
213    }
214
215    /// The offending line with a caret under the offending column.
216    pub fn snippet(&self) -> String {
217        snippet(self.line, &self.line_text, self.column)
218    }
219}
220
221impl fmt::Display for NestingTooDeep {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        write!(f, "{}\n{}", self.summary(), self.snippet())
224    }
225}
226
227impl StdError for NestingTooDeep {}
228
229/// Quotes line `number` with a caret under `column`, the way `rustc` quotes
230/// source.
231fn snippet(number: usize, line_text: &str, column: usize) -> String {
232    let (quoted, column) = quote_line(line_text, column);
233    let number = number.to_string();
234    let gutter = " ".repeat(number.len());
235    format!(
236        "{} | {}\n{} | {}^",
237        number,
238        quoted,
239        gutter,
240        " ".repeat(column - 1)
241    )
242}
243
244/// Writes alternatives the way prose does: `a`, `a or b`, `a, b or c`.
245fn join_alternatives(alternatives: &[String]) -> Option<String> {
246    match alternatives {
247        [] => None,
248        [only] => Some(only.clone()),
249        [rest @ .., last] => Some(format!("{} or {}", rest.join(", "), last)),
250    }
251}
252
253/// Cuts `line` down to a window around `column`, and says which column the
254/// offending character sits at in that window. Both columns count from 1.
255fn quote_line(line: &str, column: usize) -> (String, usize) {
256    let characters: Vec<char> = line.chars().collect();
257    if characters.len() <= QUOTED_LINE_WIDTH {
258        return (line.to_string(), column);
259    }
260
261    let target = column - 1;
262    let last_start = characters.len() - QUOTED_LINE_WIDTH;
263    let start = target.saturating_sub(QUOTED_LINE_WIDTH / 2).min(last_start);
264    let end = start + QUOTED_LINE_WIDTH;
265
266    let mut quoted = String::new();
267    if start > 0 {
268        quoted.push_str(ELLIPSIS);
269    }
270    quoted.extend(&characters[start..end]);
271    if end < characters.len() {
272        quoted.push_str(ELLIPSIS);
273    }
274
275    let shift = if start > 0 {
276        ELLIPSIS.chars().count()
277    } else {
278        0
279    };
280    (quoted, target - start + shift + 1)
281}
282
283/// Turns the position the parser stopped at into a line, a column and the line
284/// itself, so the message can point at the defect instead of quoting the rest
285/// of the document.
286fn locate(document: &str, failure: parser::ParseFailure) -> SyntaxError {
287    let offset = failure.offset.min(document.len());
288    let mut line = 1;
289    let mut line_start = 0;
290    let bytes = document.as_bytes();
291    let mut cursor = 0;
292    while cursor < offset {
293        match bytes[cursor] {
294            b'\r' => {
295                line += 1;
296                cursor += 1;
297                if cursor < offset && bytes[cursor] == b'\n' {
298                    cursor += 1;
299                }
300                line_start = cursor;
301            }
302            b'\n' => {
303                line += 1;
304                cursor += 1;
305                line_start = cursor;
306            }
307            _ => cursor += 1,
308        }
309    }
310    let column = document[line_start..offset].chars().count() + 1;
311    let line_end = document[line_start..]
312        .find(['\r', '\n'])
313        .map_or(document.len(), |position| line_start + position);
314    let line_text = &document[line_start..line_end];
315
316    SyntaxError {
317        offset,
318        line,
319        column,
320        expected: failure.expected.iter().map(|s| s.to_string()).collect(),
321        found: document[offset..].chars().next(),
322        line_text: line_text.to_string(),
323    }
324}
325
326/// Turns a failed parse into the error the caller sees.
327fn parse_error(document: &str, failure: parser::ParseFailure) -> ParseError {
328    let max_depth = failure.max_depth_exceeded;
329    let located = locate(document, failure);
330    match max_depth {
331        Some(max_depth) => ParseError::NestingTooDeep(NestingTooDeep {
332            max_depth,
333            offset: located.offset,
334            line: located.line,
335            column: located.column,
336            line_text: located.line_text,
337        }),
338        None => ParseError::SyntaxError(located),
339    }
340}
341
342#[derive(Debug, Clone, PartialEq)]
343pub enum LiNo<T> {
344    Link { id: Option<T>, values: Vec<Self> },
345    Ref(T),
346}
347
348impl<T> LiNo<T> {
349    pub fn is_ref(&self) -> bool {
350        matches!(self, LiNo::Ref(_))
351    }
352
353    pub fn is_link(&self) -> bool {
354        matches!(self, LiNo::Link { .. })
355    }
356
357    /// Creates a new link with the given ID and values.
358    ///
359    /// This method allows creating links with any number of values,
360    /// providing an alternative to tuple conversion for cases where
361    /// more than 12 values are needed.
362    ///
363    /// # Examples
364    /// ```
365    /// use links_notation::LiNo;
366    ///
367    /// // Create a link with many values
368    /// let values: Vec<LiNo<String>> = (1..=20)
369    ///     .map(|i| LiNo::Ref(format!("v{}", i)))
370    ///     .collect();
371    /// let link = LiNo::new(Some("id".to_string()), values);
372    /// ```
373    pub fn new(id: Option<T>, values: Vec<Self>) -> Self {
374        LiNo::Link { id, values }
375    }
376
377    /// Creates a new anonymous link (no ID) with the given values.
378    ///
379    /// # Examples
380    /// ```
381    /// use links_notation::LiNo;
382    ///
383    /// let values = vec![LiNo::Ref("a".to_string()), LiNo::Ref("b".to_string())];
384    /// let link = LiNo::anonymous(values);
385    /// assert_eq!(format!("{}", link), "(a b)");
386    /// ```
387    pub fn anonymous(values: Vec<Self>) -> Self {
388        LiNo::Link { id: None, values }
389    }
390
391    /// Creates a new reference.
392    ///
393    /// # Examples
394    /// ```
395    /// use links_notation::LiNo;
396    ///
397    /// let r: LiNo<String> = LiNo::reference("hello".to_string());
398    /// assert_eq!(format!("{}", r), "hello");
399    /// ```
400    pub fn reference(value: T) -> Self {
401        LiNo::Ref(value)
402    }
403}
404
405/// Builder for creating LiNo links with arbitrary number of values.
406///
407/// This builder provides a fluent API for constructing links when the tuple
408/// conversion (limited to 12 elements) is insufficient.
409///
410/// # Examples
411/// ```
412/// use links_notation::{LiNo, LiNoBuilder};
413///
414/// // Build a link with many string values
415/// let link: LiNo<String> = LiNoBuilder::new()
416///     .id("myLink")
417///     .value("v1")
418///     .value("v2")
419///     .value("v3")
420///     .build();
421/// assert_eq!(format!("{}", link), "(myLink: v1 v2 v3)");
422///
423/// // Build a link with LiNo values
424/// let nested: LiNo<String> = ("inner", "a", "b").into();
425/// let link: LiNo<String> = LiNoBuilder::new()
426///     .id("outer")
427///     .lino(nested)
428///     .value("c")
429///     .build();
430/// assert_eq!(format!("{}", link), "(outer: (inner: a b) c)");
431///
432/// // Build anonymous link
433/// let link: LiNo<String> = LiNoBuilder::new()
434///     .value("a")
435///     .value("b")
436///     .build();
437/// assert_eq!(format!("{}", link), "(a b)");
438/// ```
439#[derive(Debug, Clone, Default)]
440pub struct LiNoBuilder {
441    id: Option<String>,
442    values: Vec<LiNo<String>>,
443}
444
445impl LiNoBuilder {
446    /// Creates a new empty LiNoBuilder.
447    pub fn new() -> Self {
448        Self::default()
449    }
450
451    /// Sets the ID of the link.
452    ///
453    /// If called multiple times, the last value wins.
454    pub fn id(mut self, id: &str) -> Self {
455        self.id = Some(id.to_string());
456        self
457    }
458
459    /// Adds a string value to the link (converted to a Ref).
460    pub fn value(mut self, value: &str) -> Self {
461        self.values.push(LiNo::Ref(value.to_string()));
462        self
463    }
464
465    /// Adds a LiNo value to the link.
466    pub fn lino(mut self, value: LiNo<String>) -> Self {
467        self.values.push(value);
468        self
469    }
470
471    /// Adds multiple string values to the link.
472    pub fn values<I, S>(mut self, values: I) -> Self
473    where
474        I: IntoIterator<Item = S>,
475        S: AsRef<str>,
476    {
477        for v in values {
478            self.values.push(LiNo::Ref(v.as_ref().to_string()));
479        }
480        self
481    }
482
483    /// Adds multiple LiNo values to the link.
484    pub fn linos<I>(mut self, values: I) -> Self
485    where
486        I: IntoIterator<Item = LiNo<String>>,
487    {
488        self.values.extend(values);
489        self
490    }
491
492    /// Builds the final LiNo link.
493    pub fn build(self) -> LiNo<String> {
494        LiNo::Link {
495            id: self.id,
496            values: self.values,
497        }
498    }
499}
500
501/// Type alias for backward compatibility (deprecated).
502#[deprecated(since = "0.3.0", note = "Use LiNoBuilder instead")]
503pub type LinkBuilder = LiNoBuilder;
504
505impl<T: ToString + Clone> LiNo<T> {
506    /// Format the link using FormatConfig configuration.
507    ///
508    /// # Arguments
509    /// * `config` - The FormatConfig to use for formatting
510    ///
511    /// # Returns
512    /// Formatted string representation
513    pub fn format_with_config(&self, config: &FormatConfig) -> String {
514        match self {
515            LiNo::Ref(value) => {
516                let escaped = escape_reference(&value.to_string());
517                if config.less_parentheses {
518                    escaped
519                } else {
520                    format!("({})", escaped)
521                }
522            }
523            LiNo::Link { id, values } => {
524                // Empty link
525                if id.is_none() && values.is_empty() {
526                    return if config.less_parentheses {
527                        String::new()
528                    } else {
529                        "()".to_string()
530                    };
531                }
532
533                // Link with only ID, no values
534                if values.is_empty() {
535                    if let Some(id_val) = id {
536                        let escaped_id = escape_reference(&id_val.to_string());
537                        return if config.less_parentheses && !needs_parentheses(&id_val.to_string())
538                        {
539                            escaped_id
540                        } else {
541                            format!("({})", escaped_id)
542                        };
543                    }
544                    return if config.less_parentheses {
545                        String::new()
546                    } else {
547                        "()".to_string()
548                    };
549                }
550
551                // Check if we should use indented format
552                let mut should_indent = false;
553                if config.should_indent_by_ref_count(values.len()) {
554                    should_indent = true;
555                } else {
556                    // Try inline format first to check line length
557                    let values_str = values
558                        .iter()
559                        .map(|v| format_value(v))
560                        .collect::<Vec<_>>()
561                        .join(" ");
562
563                    let test_line = if let Some(id_val) = id {
564                        let id_str = escape_reference(&id_val.to_string());
565                        if config.less_parentheses {
566                            format!("{}: {}", id_str, values_str)
567                        } else {
568                            format!("({}: {})", id_str, values_str)
569                        }
570                    } else if config.less_parentheses {
571                        values_str.clone()
572                    } else {
573                        format!("({})", values_str)
574                    };
575
576                    if config.should_indent_by_length(&test_line) {
577                        should_indent = true;
578                    }
579                }
580
581                // Format with indentation if needed
582                if should_indent && !config.prefer_inline {
583                    return self.format_indented(config);
584                }
585
586                // Standard inline formatting
587                let values_str = values
588                    .iter()
589                    .map(|v| format_value(v))
590                    .collect::<Vec<_>>()
591                    .join(" ");
592
593                // Link with values only (null id)
594                if id.is_none() {
595                    if config.less_parentheses {
596                        // Check if all values are simple (no nested values)
597                        let all_simple = values.iter().all(|v| matches!(v, LiNo::Ref(_)));
598                        if all_simple {
599                            return values
600                                .iter()
601                                .map(|v| match v {
602                                    LiNo::Ref(r) => escape_reference(&r.to_string()),
603                                    _ => format_value(v),
604                                })
605                                .collect::<Vec<_>>()
606                                .join(" ");
607                        }
608                        return values_str;
609                    }
610                    return format!("({})", values_str);
611                }
612
613                // Link with ID and values
614                let id_str = escape_reference(&id.as_ref().unwrap().to_string());
615                let with_colon = format!("{}: {}", id_str, values_str);
616                if config.less_parentheses && !needs_parentheses(&id.as_ref().unwrap().to_string())
617                {
618                    with_colon
619                } else {
620                    format!("({})", with_colon)
621                }
622            }
623        }
624    }
625
626    /// Format the link with indentation.
627    fn format_indented(&self, config: &FormatConfig) -> String {
628        match self {
629            LiNo::Ref(value) => {
630                let escaped = escape_reference(&value.to_string());
631                format!("({})", escaped)
632            }
633            LiNo::Link { id, values } => {
634                if id.is_none() {
635                    // Values only - format each on separate line
636                    values
637                        .iter()
638                        .map(|v| format!("{}{}", config.indent_string, format_value(v)))
639                        .collect::<Vec<_>>()
640                        .join("\n")
641                } else {
642                    // Link with ID - format as id:\n  value1\n  value2
643                    let id_str = escape_reference(&id.as_ref().unwrap().to_string());
644                    let mut lines = vec![format!("{}:", id_str)];
645                    for v in values {
646                        lines.push(format!("{}{}", config.indent_string, format_value(v)));
647                    }
648                    lines.join("\n")
649                }
650            }
651        }
652    }
653}
654
655impl<T: ToString> fmt::Display for LiNo<T> {
656    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
657        match self {
658            LiNo::Ref(value) => write!(f, "{}", escape_reference(&value.to_string())),
659            LiNo::Link { id, values } => {
660                let id_str = id
661                    .as_ref()
662                    .map(|id| format!("{}: ", escape_reference(&id.to_string())))
663                    .unwrap_or_default();
664
665                if f.alternate() {
666                    // Format top-level as lines
667                    let lines = values
668                        .iter()
669                        .map(|value| {
670                            // For alternate formatting, ensure standalone references are wrapped in parentheses
671                            // so that flattened structures like indented blocks render as "(ref)" lines
672                            match value {
673                                LiNo::Ref(_) => format!("{}({})", id_str, value),
674                                _ => format!("{}{}", id_str, value),
675                            }
676                        })
677                        .collect::<Vec<_>>()
678                        .join("\n");
679                    write!(f, "{}", lines)
680                } else {
681                    let values_str = values
682                        .iter()
683                        .map(|value| value.to_string())
684                        .collect::<Vec<_>>()
685                        .join(" ");
686                    write!(f, "({}{})", id_str, values_str)
687                }
688            }
689        }
690    }
691}
692
693impl LiNo<SyntaxReference> {
694    /// The same link with plain IDs, which is what the AST mode reads: every
695    /// [`SyntaxReference`] becomes its [`value`](SyntaxReference::value).
696    ///
697    /// # Examples
698    /// ```
699    /// use links_notation::{LiNo, parse_lino_to_links, parse_lino_to_syntax_links};
700    ///
701    /// let syntax = parse_lino_to_syntax_links("('a': \"b\")").unwrap();
702    /// let plain: LiNo<String> = syntax[0].clone().into_values();
703    /// assert_eq!(plain, parse_lino_to_links("(a: b)").unwrap()[0]);
704    /// assert_eq!(LiNo::<String>::from(syntax[0].clone()), plain);
705    /// ```
706    pub fn into_values(self) -> LiNo<String> {
707        match self {
708            LiNo::Ref(reference) => LiNo::Ref(reference.value),
709            LiNo::Link { id, values } => LiNo::Link {
710                id: id.map(|id| id.value),
711                values: values.into_iter().map(LiNo::into_values).collect(),
712            },
713        }
714    }
715
716    /// The ID exactly as the document wrote it: the text of a reference, or of
717    /// a link's ID. `None` for a link without an ID and for a reference built
718    /// in code.
719    ///
720    /// # Examples
721    /// ```
722    /// use links_notation::{LiNo, parse_lino_to_syntax_links};
723    ///
724    /// let links = parse_lino_to_syntax_links("'a': ~1{62}").unwrap();
725    /// assert_eq!(links[0].raw_id(), Some("'a'"));
726    /// let LiNo::Link { values, .. } = &links[0] else { unreachable!() };
727    /// assert_eq!(values[0].raw_id(), Some("~1{62}"));
728    /// ```
729    pub fn raw_id(&self) -> Option<&str> {
730        match self {
731            LiNo::Ref(reference) => reference.raw.as_deref(),
732            LiNo::Link { id, .. } => id.as_ref().and_then(|id| id.raw.as_deref()),
733        }
734    }
735
736    /// The delimiter the ID was written between: `'`, `"` or `` ` `` when it
737    /// was quoted, and `None` otherwise; see [`SyntaxReference::quote`].
738    pub fn quote(&self) -> Option<char> {
739        self.raw_id().and_then(reference_quote)
740    }
741}
742
743/// Drops how the IDs were written; see [`LiNo::into_values`].
744impl From<LiNo<SyntaxReference>> for LiNo<String> {
745    fn from(link: LiNo<SyntaxReference>) -> Self {
746        link.into_values()
747    }
748}
749
750// Convert from parser::Link to LiNo (without flattening)
751impl From<parser::Link> for LiNo<String> {
752    fn from(link: parser::Link) -> Self {
753        if let Some(body) = &link.nested {
754            return transform_nested(body, &plain_id);
755        }
756        if link.values.is_empty() && link.children.is_empty() {
757            if let Some(id) = link.id {
758                LiNo::Ref(id)
759            } else {
760                LiNo::Link {
761                    id: None,
762                    values: vec![],
763                }
764            }
765        } else {
766            let values: Vec<LiNo<String>> = link.values.into_iter().map(|v| v.into()).collect();
767            LiNo::Link {
768                id: link.id,
769                values,
770            }
771        }
772    }
773}
774
775/// How the links of a parsed document get their IDs: as the plain value, or as
776/// a [`SyntaxReference`] that also records how the ID was written.
777trait IdReader<T>: Fn(&parser::Link) -> Option<T> {}
778
779impl<T, F: Fn(&parser::Link) -> Option<T>> IdReader<T> for F {}
780
781/// The ID of a parsed link, as its plain value.
782fn plain_id(link: &parser::Link) -> Option<String> {
783    link.id.clone()
784}
785
786/// The ID of a parsed link with the text it was written as, sliced from the
787/// document the caller passed in rather than from the comment-blanked copy the
788/// parser read, which keeps every byte where it was.
789fn syntax_id(link: &parser::Link, document: &str) -> Option<SyntaxReference> {
790    let value = link.id.clone()?;
791    let raw = link
792        .id_span
793        .as_ref()
794        .and_then(|span| document.get(span.clone()))
795        .map(str::to_owned);
796    Some(SyntaxReference { value, raw })
797}
798
799// A parenthesized group is a nested document: its body follows the same rules as
800// the root, so it is flattened the same way. A body that produces a single link
801// collapses to that link, unless the body is a single parenthesized group, which
802// keeps `((a b))` different from `(a b)`.
803//
804// The body is borrowed, not copied: copying it at every level would copy the
805// levels below once per level above them, which is quadratic in the nesting.
806fn transform_nested<T: Clone, F: IdReader<T>>(body: &[parser::Link], id: &F) -> LiNo<T> {
807    let links = flatten_links(body, id);
808    let wraps_single_group =
809        body.len() == 1 && body[0].nested.is_some() && body[0].children.is_empty();
810    if links.len() == 1 && !wraps_single_group {
811        return links.into_iter().next().unwrap();
812    }
813    LiNo::Link {
814        id: None,
815        values: links,
816    }
817}
818
819// Helper function to flatten indented structures according to Lino spec
820fn flatten_links<T: Clone, F: IdReader<T>>(links: &[parser::Link], id: &F) -> Vec<LiNo<T>> {
821    let mut result = vec![];
822
823    for link in links {
824        flatten_link_recursive(link, None, &mut result, id);
825    }
826
827    result
828}
829
830fn flatten_link_recursive<T: Clone, F: IdReader<T>>(
831    link: &parser::Link,
832    parent: Option<&LiNo<T>>,
833    result: &mut Vec<LiNo<T>>,
834    id: &F,
835) {
836    // Special case: If this is an indented ID (with colon) with children,
837    // the children should become the values of the link (indented ID syntax)
838    if link.is_indented_id
839        && link.id.is_some()
840        && link.values.is_empty()
841        && !link.children.is_empty()
842    {
843        let child_values: Vec<LiNo<T>> = link
844            .children
845            .iter()
846            .map(|child| transform_indented_value(child, id))
847            .collect();
848
849        let current = LiNo::Link {
850            id: id(link),
851            values: child_values,
852        };
853
854        let combined = if let Some(parent) = parent {
855            // Wrap parent in parentheses if it's a reference
856            let wrapped_parent = match parent {
857                LiNo::Ref(ref_id) => LiNo::Link {
858                    id: None,
859                    values: vec![LiNo::Ref(ref_id.clone())],
860                },
861                link => link.clone(),
862            };
863
864            LiNo::Link {
865                id: None,
866                values: vec![wrapped_parent, current],
867            }
868        } else {
869            current
870        };
871
872        result.push(combined);
873        return; // Don't process children again
874    }
875
876    // Create the current link without children
877    let current = if let Some(body) = &link.nested {
878        transform_nested(body, id)
879    } else if link.values.is_empty() {
880        if let Some(id) = id(link) {
881            LiNo::Ref(id)
882        } else {
883            LiNo::Link {
884                id: None,
885                values: vec![],
886            }
887        }
888    } else {
889        LiNo::Link {
890            id: id(link),
891            values: link
892                .values
893                .iter()
894                .map(|value| transform_value(value, id))
895                .collect(),
896        }
897    };
898
899    // Create the combined link (parent + current) with proper wrapping
900    let combined = if let Some(parent) = parent {
901        // Wrap parent in parentheses if it's a reference
902        let wrapped_parent = match parent {
903            LiNo::Ref(ref_id) => LiNo::Link {
904                id: None,
905                values: vec![LiNo::Ref(ref_id.clone())],
906            },
907            link => link.clone(),
908        };
909
910        // Wrap current in parentheses if it's a reference
911        let wrapped_current = match current {
912            LiNo::Ref(ref_id) => LiNo::Link {
913                id: None,
914                values: vec![LiNo::Ref(ref_id)],
915            },
916            link => link,
917        };
918
919        LiNo::Link {
920            id: None,
921            values: vec![wrapped_parent, wrapped_current],
922        }
923    } else {
924        current
925    };
926
927    // Only a link with children needs a copy: each child repeats it as its
928    // parent. Copying it regardless would copy every nested group once per
929    // level above it.
930    if link.children.is_empty() {
931        result.push(combined);
932        return;
933    }
934    result.push(combined.clone());
935
936    // Process children
937    for child in &link.children {
938        flatten_link_recursive(child, Some(&combined), result, id);
939    }
940}
941
942// Convert a link without its indented children, borrowing the parsed tree.
943fn transform_value<T: Clone, F: IdReader<T>>(link: &parser::Link, id: &F) -> LiNo<T> {
944    if let Some(body) = &link.nested {
945        return transform_nested(body, id);
946    }
947    if link.values.is_empty() {
948        return match id(link) {
949            Some(id) => LiNo::Ref(id),
950            None => LiNo::Link {
951                id: None,
952                values: vec![],
953            },
954        };
955    }
956    LiNo::Link {
957        id: id(link),
958        values: link
959            .values
960            .iter()
961            .map(|value| transform_value(value, id))
962            .collect(),
963    }
964}
965
966// Convert a child line and all its descendants into one value of an indented ID.
967fn transform_indented_value<T: Clone, F: IdReader<T>>(link: &parser::Link, id: &F) -> LiNo<T> {
968    let children: Vec<_> = link
969        .children
970        .iter()
971        .map(|child| transform_indented_value(child, id))
972        .collect();
973    if !children.is_empty() && link.is_indented_id && link.id.is_some() && link.values.is_empty() {
974        return LiNo::Link {
975            id: id(link),
976            values: children,
977        };
978    }
979    let current = transform_value(link, id);
980
981    if !children.is_empty() {
982        match current {
983            LiNo::Link { id, mut values } => {
984                values.extend(children);
985                LiNo::Link { id, values }
986            }
987            LiNo::Ref(id) => {
988                if link.nested.is_some() {
989                    return LiNo::Link {
990                        id: Some(id),
991                        values: children,
992                    };
993                }
994                let mut values = vec![LiNo::Ref(id)];
995                values.extend(children);
996                LiNo::Link { id: None, values }
997            }
998        }
999    } else if link.id.is_none() && link.nested.is_none() && link.values.len() == 1 {
1000        match current {
1001            LiNo::Link { id: None, values } => values.into_iter().next().unwrap(),
1002            value => value,
1003        }
1004    } else {
1005        current
1006    }
1007}
1008
1009/// The document as the parser reads it: with the comments blanked out when the
1010/// configuration asks for comments, and untouched when it does not.
1011///
1012/// Blanking keeps every byte of the document where it was, so a position the
1013/// parser reports is a position in the document the caller passed in.
1014fn prepare<'a>(document: &'a str, config: &ParserConfig) -> Cow<'a, str> {
1015    if config.comments {
1016        Cow::Owned(strip_comments(document))
1017    } else {
1018        Cow::Borrowed(document)
1019    }
1020}
1021
1022/// Reads a document, with comments.
1023///
1024/// A `#` written where a line or a token starts opens a comment that runs to
1025/// the end of the line; [`parse_lino_with_config`] reads a document without
1026/// them.
1027///
1028/// This is the AST mode: the links hold what each reference means, not how it
1029/// was written. [`parse_lino_to_syntax_links`] reads the same links with the
1030/// text of every ID, and [`parse_lino_to_cst`] reads every character of the
1031/// document.
1032///
1033/// # Examples
1034/// ```
1035/// use links_notation::parse_lino;
1036///
1037/// let parsed = parse_lino("# what the gate checks\nci_gate: rust\n").unwrap();
1038/// assert_eq!(format!("{}", parsed), "((ci_gate: rust))");
1039/// ```
1040pub fn parse_lino(document: &str) -> Result<LiNo<String>, ParseError> {
1041    parse_lino_with_config(document, &ParserConfig::default())
1042}
1043
1044/// Reads a document the way `config` says to.
1045///
1046/// # Examples
1047/// ```
1048/// use links_notation::{parse_lino_with_config, ParserConfig};
1049///
1050/// let document = "# a: b";
1051/// assert_eq!(
1052///     format!("{}", parse_lino_with_config(document, &ParserConfig::new()).unwrap()),
1053///     "()"
1054/// );
1055/// assert!(parse_lino_with_config(document, &ParserConfig::without_comments()).is_err());
1056/// ```
1057pub fn parse_lino_with_config(
1058    document: &str,
1059    config: &ParserConfig,
1060) -> Result<LiNo<String>, ParseError> {
1061    // Handle empty or whitespace-only input by returning empty result
1062    if document.trim_matches(parser::is_whitespace_char).is_empty() {
1063        return Ok(LiNo::Link {
1064            id: None,
1065            values: vec![],
1066        });
1067    }
1068
1069    let prepared = prepare(document, config);
1070    match parser::parse_prepared_document(&prepared, document, config.max_depth) {
1071        Ok(links) => {
1072            if links.is_empty() {
1073                Ok(LiNo::Link {
1074                    id: None,
1075                    values: vec![],
1076                })
1077            } else {
1078                // Flatten the indented structure according to Lino spec
1079                let flattened = flatten_links(&links, &plain_id);
1080                Ok(LiNo::Link {
1081                    id: None,
1082                    values: flattened,
1083                })
1084            }
1085        }
1086        Err(failure) => Err(parse_error(document, failure)),
1087    }
1088}
1089
1090/// Reads a document into a collection of links, with comments.
1091///
1092/// This is the AST mode: `1` and `'1'` are both read as the reference `1`.
1093/// [`parse_lino_to_syntax_links`] reads the same links and records how each ID
1094/// was written, and [`parse_lino_to_cst`] keeps every character of the document.
1095///
1096/// # Examples
1097/// ```
1098/// use links_notation::{format_links, parse_lino_to_links};
1099///
1100/// let links = parse_lino_to_links("(t 1 '1')").unwrap();
1101/// assert_eq!(format_links(&links), "(t 1 1)");
1102/// ```
1103pub fn parse_lino_to_links(document: &str) -> Result<Vec<LiNo<String>>, ParseError> {
1104    parse_lino_to_links_with_config(document, &ParserConfig::default())
1105}
1106
1107/// Reads a document into a collection of links the way `config` says to.
1108///
1109/// # Examples
1110/// ```
1111/// use links_notation::{parse_lino_to_links_with_config, ParserConfig};
1112///
1113/// let links = parse_lino_to_links_with_config("a: b # why", &ParserConfig::new()).unwrap();
1114/// assert_eq!(links.len(), 1);
1115/// assert_eq!(format!("{}", links[0]), "(a: b)");
1116/// ```
1117pub fn parse_lino_to_links_with_config(
1118    document: &str,
1119    config: &ParserConfig,
1120) -> Result<Vec<LiNo<String>>, ParseError> {
1121    read_links(document, config, &plain_id)
1122}
1123
1124/// Reads a document into a collection of links whose references record how
1125/// they were written.
1126///
1127/// The links are the ones [`parse_lino_to_links`] reads, with every ID a
1128/// [`SyntaxReference`]: its [`value`](SyntaxReference::value) is the ID
1129/// `parse_lino_to_links` reads and its [`raw`](SyntaxReference::raw) is the ID
1130/// exactly as written, so `(t 1 '1')` still holds two equal references `1` but
1131/// the second one knows it was quoted. A link made by combining the lines of an
1132/// indented document has no ID, so it has no raw text either.
1133///
1134/// [`parse_lino_to_links`] and [`parse_lino`] are the AST mode: they read the
1135/// same links without the syntax. Turn one into the other with
1136/// [`LiNo::into_values`] or `From`. [`StreamParser::syntax`] reads the same
1137/// links from a stream.
1138///
1139/// # Examples
1140/// ```
1141/// use links_notation::{LiNo, format_links, parse_lino_to_links, parse_lino_to_syntax_links};
1142///
1143/// let links = parse_lino_to_syntax_links("(\"a\": 'b' c)").unwrap();
1144/// let LiNo::Link { id: Some(id), values } = &links[0] else { unreachable!() };
1145/// assert_eq!(id.value, "a");
1146/// assert_eq!(id.raw.as_deref(), Some("\"a\""));
1147/// assert_eq!(id.quote(), Some('"'));
1148/// assert_eq!(values[0].raw_id(), Some("'b'"));
1149/// assert_eq!(values[1].quote(), None);
1150///
1151/// // Formatting and the plain links are exactly what the AST mode reads.
1152/// assert_eq!(format!("{}", links[0]), "(a: b c)");
1153/// let plain: Vec<LiNo<String>> = links.into_iter().map(LiNo::into_values).collect();
1154/// assert_eq!(plain, parse_lino_to_links("(\"a\": 'b' c)").unwrap());
1155/// assert_eq!(format_links(&plain), "(a: b c)");
1156/// ```
1157pub fn parse_lino_to_syntax_links(
1158    document: &str,
1159) -> Result<Vec<LiNo<SyntaxReference>>, ParseError> {
1160    parse_lino_to_syntax_links_with_config(document, &ParserConfig::default())
1161}
1162
1163/// Reads a document the way `config` says to into a collection of links whose
1164/// references record how they were written; see [`parse_lino_to_syntax_links`].
1165///
1166/// # Examples
1167/// ```
1168/// use links_notation::{LiNo, ParserConfig, parse_lino_to_syntax_links_with_config};
1169///
1170/// let config = ParserConfig::without_comments();
1171/// let links = parse_lino_to_syntax_links_with_config("a #b", &config).unwrap();
1172/// let LiNo::Link { values, .. } = &links[0] else { unreachable!() };
1173/// assert_eq!(values[1].raw_id(), Some("#b"));
1174/// ```
1175pub fn parse_lino_to_syntax_links_with_config(
1176    document: &str,
1177    config: &ParserConfig,
1178) -> Result<Vec<LiNo<SyntaxReference>>, ParseError> {
1179    read_links(document, config, &|link| syntax_id(link, document))
1180}
1181
1182/// Reads a document into a collection of links, giving each link the ID `id`
1183/// reads from the parsed link.
1184fn read_links<T: Clone, F: IdReader<T>>(
1185    document: &str,
1186    config: &ParserConfig,
1187    id: &F,
1188) -> Result<Vec<LiNo<T>>, ParseError> {
1189    // Handle empty or whitespace-only input by returning empty collection
1190    if document.trim_matches(parser::is_whitespace_char).is_empty() {
1191        return Ok(vec![]);
1192    }
1193
1194    let prepared = prepare(document, config);
1195    match parser::parse_prepared_document(&prepared, document, config.max_depth) {
1196        Ok(links) => {
1197            if links.is_empty() {
1198                Ok(vec![])
1199            } else {
1200                // Flatten the indented structure according to Lino spec
1201                let flattened = flatten_links(&links, id);
1202                Ok(flattened)
1203            }
1204        }
1205        Err(failure) => Err(parse_error(document, failure)),
1206    }
1207}
1208
1209/// Parses a document only to find out whether it parses, returning the document
1210/// as the parser read it: with its comments blanked when `config` asks for
1211/// comments.
1212pub(crate) fn check_document<'a>(
1213    document: &'a str,
1214    config: &ParserConfig,
1215) -> Result<Cow<'a, str>, ParseError> {
1216    let prepared = prepare(document, config);
1217    if document.trim_matches(parser::is_whitespace_char).is_empty() {
1218        return Ok(prepared);
1219    }
1220    match parser::parse_prepared_document(&prepared, document, config.max_depth) {
1221        Ok(_) => Ok(prepared),
1222        Err(failure) => Err(parse_error(document, failure)),
1223    }
1224}
1225
1226/// Formats a collection of LiNo links as a multi-line string.
1227/// Each link is formatted on a separate line.
1228pub fn format_links(links: &[LiNo<String>]) -> String {
1229    links
1230        .iter()
1231        .map(|link| format!("{}", link))
1232        .collect::<Vec<_>>()
1233        .join("\n")
1234}
1235
1236/// Formats a collection of LiNo links as a multi-line string using FormatConfig.
1237/// Supports all formatting options including consecutive link grouping.
1238///
1239/// # Arguments
1240/// * `links` - The collection of links to format
1241/// * `config` - The FormatConfig to use for formatting
1242///
1243/// # Returns
1244/// Formatted string in Lino notation
1245pub fn format_links_with_config(links: &[LiNo<String>], config: &FormatConfig) -> String {
1246    if links.is_empty() {
1247        return String::new();
1248    }
1249
1250    // Apply consecutive link grouping if enabled
1251    let links_to_format = if config.group_consecutive {
1252        group_consecutive_links(links)
1253    } else {
1254        links.to_vec()
1255    };
1256
1257    links_to_format
1258        .iter()
1259        .map(|link| link.format_with_config(config))
1260        .collect::<Vec<_>>()
1261        .join("\n")
1262}
1263
1264/// Groups consecutive links with the same ID.
1265///
1266/// For example:
1267/// ```text
1268/// SetA a
1269/// SetA b
1270/// SetA c
1271/// ```
1272/// Becomes:
1273/// ```text
1274/// SetA
1275///   a
1276///   b
1277///   c
1278/// ```
1279fn group_consecutive_links(links: &[LiNo<String>]) -> Vec<LiNo<String>> {
1280    if links.is_empty() {
1281        return vec![];
1282    }
1283
1284    let mut grouped = vec![];
1285    let mut i = 0;
1286
1287    while i < links.len() {
1288        let current = &links[i];
1289
1290        // Look ahead for consecutive links with same ID
1291        if let LiNo::Link {
1292            id: Some(current_id),
1293            values: current_values,
1294        } = current
1295        {
1296            if !current_values.is_empty() {
1297                // Collect all values with same ID
1298                let mut same_id_values = current_values.clone();
1299                let mut j = i + 1;
1300
1301                while j < links.len() {
1302                    if let LiNo::Link {
1303                        id: Some(next_id),
1304                        values: next_values,
1305                    } = &links[j]
1306                    {
1307                        if next_id == current_id && !next_values.is_empty() {
1308                            same_id_values.extend(next_values.clone());
1309                            j += 1;
1310                        } else {
1311                            break;
1312                        }
1313                    } else {
1314                        break;
1315                    }
1316                }
1317
1318                // If we found consecutive links, create grouped link
1319                if j > i + 1 {
1320                    grouped.push(LiNo::Link {
1321                        id: Some(current_id.clone()),
1322                        values: same_id_values,
1323                    });
1324                    i = j;
1325                    continue;
1326                }
1327            }
1328        }
1329
1330        grouped.push(current.clone());
1331        i += 1;
1332    }
1333
1334    grouped
1335}
1336
1337/// Escape a reference string by adding quotes if necessary.
1338fn escape_reference(reference: &str) -> String {
1339    reference_literal::format_reference(reference)
1340}
1341
1342/// Check if a string needs to be wrapped in parentheses.
1343fn needs_parentheses(s: &str) -> bool {
1344    s.contains(' ') || s.contains(':') || s.contains('(') || s.contains(')')
1345}
1346
1347/// Format a value within a link.
1348fn format_value<T: ToString>(value: &LiNo<T>) -> String {
1349    match value {
1350        LiNo::Ref(r) => escape_reference(&r.to_string()),
1351        LiNo::Link { id, values } => {
1352            // Simple link with just an ID - don't wrap in extra parentheses
1353            if values.is_empty() {
1354                if let Some(id_val) = id {
1355                    return escape_reference(&id_val.to_string());
1356                }
1357                return String::new();
1358            }
1359            // Complex value - format with parentheses
1360            format!("{}", value)
1361        }
1362    }
1363}
1364
1365// Tuple conversion implementations for ergonomic link creation
1366// These implementations allow creating links using Rust tuple syntax
1367//
1368// The macro generates From implementations for tuples of sizes 2-12.
1369// For each size, it generates 4 types of conversions:
1370// 1. All &str - first element becomes ID, rest become values
1371// 2. All String - first element becomes ID, rest become values
1372// 3. &str ID with LiNo values - first element becomes ID, LiNo elements become values
1373// 4. All LiNo - creates anonymous link (no ID) with all elements as values
1374
1375/// Macro to implement From trait for tuples converting to LiNo<String>.
1376///
1377/// This macro generates four From implementations for each tuple size:
1378/// - `(&str, &str, ...)` - First element becomes ID, rest become string values
1379/// - `(String, String, ...)` - First element becomes ID, rest become string values
1380/// - `(&str, LiNo<String>, ...)` - First element becomes ID, LiNo elements become values
1381/// - `(LiNo<String>, LiNo<String>, ...)` - Creates anonymous link with all elements as values
1382///
1383/// # Examples
1384/// ```
1385/// use links_notation::LiNo;
1386///
1387/// // 2-tuple: ("id", "value") -> (id: value)
1388/// let link: LiNo<String> = ("papa", "mama").into();
1389/// assert_eq!(format!("{}", link), "(papa: mama)");
1390///
1391/// // 3-tuple: ("id", "v1", "v2") -> (id: v1 v2)
1392/// let link: LiNo<String> = ("parent", "child1", "child2").into();
1393/// assert_eq!(format!("{}", link), "(parent: child1 child2)");
1394///
1395/// // Anonymous link from all LiNo elements
1396/// let a = LiNo::Ref("a".to_string());
1397/// let b = LiNo::Ref("b".to_string());
1398/// let link: LiNo<String> = (a, b).into();
1399/// assert_eq!(format!("{}", link), "(a b)");
1400/// ```
1401macro_rules! impl_tuple_from {
1402    // Implementation for 2-tuples
1403    (@str_tuple 2, $t0:tt, $t1:tt) => {
1404        impl From<(&str, &str)> for LiNo<String> {
1405            fn from(tuple: (&str, &str)) -> Self {
1406                LiNo::Link {
1407                    id: Some(tuple.$t0.to_string()),
1408                    values: vec![LiNo::Ref(tuple.$t1.to_string())],
1409                }
1410            }
1411        }
1412    };
1413    (@string_tuple 2, $t0:tt, $t1:tt) => {
1414        impl From<(String, String)> for LiNo<String> {
1415            fn from(tuple: (String, String)) -> Self {
1416                LiNo::Link {
1417                    id: Some(tuple.$t0),
1418                    values: vec![LiNo::Ref(tuple.$t1)],
1419                }
1420            }
1421        }
1422    };
1423    (@str_lino_tuple 2, $t0:tt, $t1:tt) => {
1424        impl From<(&str, LiNo<String>)> for LiNo<String> {
1425            fn from(tuple: (&str, LiNo<String>)) -> Self {
1426                LiNo::Link {
1427                    id: Some(tuple.$t0.to_string()),
1428                    values: vec![tuple.$t1],
1429                }
1430            }
1431        }
1432    };
1433    (@lino_tuple 2, $t0:tt, $t1:tt) => {
1434        impl From<(LiNo<String>, LiNo<String>)> for LiNo<String> {
1435            fn from(tuple: (LiNo<String>, LiNo<String>)) -> Self {
1436                LiNo::Link {
1437                    id: None,
1438                    values: vec![tuple.$t0, tuple.$t1],
1439                }
1440            }
1441        }
1442    };
1443
1444    // Implementation for 3-tuples
1445    (@str_tuple 3, $t0:tt, $t1:tt, $t2:tt) => {
1446        impl From<(&str, &str, &str)> for LiNo<String> {
1447            fn from(tuple: (&str, &str, &str)) -> Self {
1448                LiNo::Link {
1449                    id: Some(tuple.$t0.to_string()),
1450                    values: vec![LiNo::Ref(tuple.$t1.to_string()), LiNo::Ref(tuple.$t2.to_string())],
1451                }
1452            }
1453        }
1454    };
1455    (@string_tuple 3, $t0:tt, $t1:tt, $t2:tt) => {
1456        impl From<(String, String, String)> for LiNo<String> {
1457            fn from(tuple: (String, String, String)) -> Self {
1458                LiNo::Link {
1459                    id: Some(tuple.$t0),
1460                    values: vec![LiNo::Ref(tuple.$t1), LiNo::Ref(tuple.$t2)],
1461                }
1462            }
1463        }
1464    };
1465    (@str_lino_tuple 3, $t0:tt, $t1:tt, $t2:tt) => {
1466        impl From<(&str, LiNo<String>, LiNo<String>)> for LiNo<String> {
1467            fn from(tuple: (&str, LiNo<String>, LiNo<String>)) -> Self {
1468                LiNo::Link {
1469                    id: Some(tuple.$t0.to_string()),
1470                    values: vec![tuple.$t1, tuple.$t2],
1471                }
1472            }
1473        }
1474    };
1475    (@lino_tuple 3, $t0:tt, $t1:tt, $t2:tt) => {
1476        impl From<(LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1477            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1478                LiNo::Link {
1479                    id: None,
1480                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2],
1481                }
1482            }
1483        }
1484    };
1485
1486    // Implementation for 4-tuples
1487    (@str_tuple 4, $t0:tt, $t1:tt, $t2:tt, $t3:tt) => {
1488        impl From<(&str, &str, &str, &str)> for LiNo<String> {
1489            fn from(tuple: (&str, &str, &str, &str)) -> Self {
1490                LiNo::Link {
1491                    id: Some(tuple.$t0.to_string()),
1492                    values: vec![
1493                        LiNo::Ref(tuple.$t1.to_string()),
1494                        LiNo::Ref(tuple.$t2.to_string()),
1495                        LiNo::Ref(tuple.$t3.to_string()),
1496                    ],
1497                }
1498            }
1499        }
1500    };
1501    (@string_tuple 4, $t0:tt, $t1:tt, $t2:tt, $t3:tt) => {
1502        impl From<(String, String, String, String)> for LiNo<String> {
1503            fn from(tuple: (String, String, String, String)) -> Self {
1504                LiNo::Link {
1505                    id: Some(tuple.$t0),
1506                    values: vec![LiNo::Ref(tuple.$t1), LiNo::Ref(tuple.$t2), LiNo::Ref(tuple.$t3)],
1507                }
1508            }
1509        }
1510    };
1511    (@str_lino_tuple 4, $t0:tt, $t1:tt, $t2:tt, $t3:tt) => {
1512        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1513            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1514                LiNo::Link {
1515                    id: Some(tuple.$t0.to_string()),
1516                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3],
1517                }
1518            }
1519        }
1520    };
1521    (@lino_tuple 4, $t0:tt, $t1:tt, $t2:tt, $t3:tt) => {
1522        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1523            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1524                LiNo::Link {
1525                    id: None,
1526                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3],
1527                }
1528            }
1529        }
1530    };
1531
1532    // Implementation for 5-tuples
1533    (@str_tuple 5, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt) => {
1534        impl From<(&str, &str, &str, &str, &str)> for LiNo<String> {
1535            fn from(tuple: (&str, &str, &str, &str, &str)) -> Self {
1536                LiNo::Link {
1537                    id: Some(tuple.$t0.to_string()),
1538                    values: vec![
1539                        LiNo::Ref(tuple.$t1.to_string()),
1540                        LiNo::Ref(tuple.$t2.to_string()),
1541                        LiNo::Ref(tuple.$t3.to_string()),
1542                        LiNo::Ref(tuple.$t4.to_string()),
1543                    ],
1544                }
1545            }
1546        }
1547    };
1548    (@string_tuple 5, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt) => {
1549        impl From<(String, String, String, String, String)> for LiNo<String> {
1550            fn from(tuple: (String, String, String, String, String)) -> Self {
1551                LiNo::Link {
1552                    id: Some(tuple.$t0),
1553                    values: vec![
1554                        LiNo::Ref(tuple.$t1),
1555                        LiNo::Ref(tuple.$t2),
1556                        LiNo::Ref(tuple.$t3),
1557                        LiNo::Ref(tuple.$t4),
1558                    ],
1559                }
1560            }
1561        }
1562    };
1563    (@str_lino_tuple 5, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt) => {
1564        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1565            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1566                LiNo::Link {
1567                    id: Some(tuple.$t0.to_string()),
1568                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4],
1569                }
1570            }
1571        }
1572    };
1573    (@lino_tuple 5, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt) => {
1574        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1575            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1576                LiNo::Link {
1577                    id: None,
1578                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4],
1579                }
1580            }
1581        }
1582    };
1583
1584    // Implementation for 6-tuples
1585    (@str_tuple 6, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt) => {
1586        impl From<(&str, &str, &str, &str, &str, &str)> for LiNo<String> {
1587            fn from(tuple: (&str, &str, &str, &str, &str, &str)) -> Self {
1588                LiNo::Link {
1589                    id: Some(tuple.$t0.to_string()),
1590                    values: vec![
1591                        LiNo::Ref(tuple.$t1.to_string()),
1592                        LiNo::Ref(tuple.$t2.to_string()),
1593                        LiNo::Ref(tuple.$t3.to_string()),
1594                        LiNo::Ref(tuple.$t4.to_string()),
1595                        LiNo::Ref(tuple.$t5.to_string()),
1596                    ],
1597                }
1598            }
1599        }
1600    };
1601    (@string_tuple 6, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt) => {
1602        impl From<(String, String, String, String, String, String)> for LiNo<String> {
1603            fn from(tuple: (String, String, String, String, String, String)) -> Self {
1604                LiNo::Link {
1605                    id: Some(tuple.$t0),
1606                    values: vec![
1607                        LiNo::Ref(tuple.$t1),
1608                        LiNo::Ref(tuple.$t2),
1609                        LiNo::Ref(tuple.$t3),
1610                        LiNo::Ref(tuple.$t4),
1611                        LiNo::Ref(tuple.$t5),
1612                    ],
1613                }
1614            }
1615        }
1616    };
1617    (@str_lino_tuple 6, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt) => {
1618        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1619            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1620                LiNo::Link {
1621                    id: Some(tuple.$t0.to_string()),
1622                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5],
1623                }
1624            }
1625        }
1626    };
1627    (@lino_tuple 6, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt) => {
1628        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1629            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1630                LiNo::Link {
1631                    id: None,
1632                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5],
1633                }
1634            }
1635        }
1636    };
1637
1638    // Implementation for 7-tuples
1639    (@str_tuple 7, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt) => {
1640        impl From<(&str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1641            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str)) -> Self {
1642                LiNo::Link {
1643                    id: Some(tuple.$t0.to_string()),
1644                    values: vec![
1645                        LiNo::Ref(tuple.$t1.to_string()),
1646                        LiNo::Ref(tuple.$t2.to_string()),
1647                        LiNo::Ref(tuple.$t3.to_string()),
1648                        LiNo::Ref(tuple.$t4.to_string()),
1649                        LiNo::Ref(tuple.$t5.to_string()),
1650                        LiNo::Ref(tuple.$t6.to_string()),
1651                    ],
1652                }
1653            }
1654        }
1655    };
1656    (@string_tuple 7, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt) => {
1657        impl From<(String, String, String, String, String, String, String)> for LiNo<String> {
1658            fn from(tuple: (String, String, String, String, String, String, String)) -> Self {
1659                LiNo::Link {
1660                    id: Some(tuple.$t0),
1661                    values: vec![
1662                        LiNo::Ref(tuple.$t1),
1663                        LiNo::Ref(tuple.$t2),
1664                        LiNo::Ref(tuple.$t3),
1665                        LiNo::Ref(tuple.$t4),
1666                        LiNo::Ref(tuple.$t5),
1667                        LiNo::Ref(tuple.$t6),
1668                    ],
1669                }
1670            }
1671        }
1672    };
1673    (@str_lino_tuple 7, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt) => {
1674        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1675            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1676                LiNo::Link {
1677                    id: Some(tuple.$t0.to_string()),
1678                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6],
1679                }
1680            }
1681        }
1682    };
1683    (@lino_tuple 7, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt) => {
1684        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1685            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1686                LiNo::Link {
1687                    id: None,
1688                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6],
1689                }
1690            }
1691        }
1692    };
1693
1694    // Implementation for 8-tuples
1695    (@str_tuple 8, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt) => {
1696        impl From<(&str, &str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1697            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
1698                LiNo::Link {
1699                    id: Some(tuple.$t0.to_string()),
1700                    values: vec![
1701                        LiNo::Ref(tuple.$t1.to_string()),
1702                        LiNo::Ref(tuple.$t2.to_string()),
1703                        LiNo::Ref(tuple.$t3.to_string()),
1704                        LiNo::Ref(tuple.$t4.to_string()),
1705                        LiNo::Ref(tuple.$t5.to_string()),
1706                        LiNo::Ref(tuple.$t6.to_string()),
1707                        LiNo::Ref(tuple.$t7.to_string()),
1708                    ],
1709                }
1710            }
1711        }
1712    };
1713    (@string_tuple 8, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt) => {
1714        impl From<(String, String, String, String, String, String, String, String)> for LiNo<String> {
1715            fn from(tuple: (String, String, String, String, String, String, String, String)) -> Self {
1716                LiNo::Link {
1717                    id: Some(tuple.$t0),
1718                    values: vec![
1719                        LiNo::Ref(tuple.$t1),
1720                        LiNo::Ref(tuple.$t2),
1721                        LiNo::Ref(tuple.$t3),
1722                        LiNo::Ref(tuple.$t4),
1723                        LiNo::Ref(tuple.$t5),
1724                        LiNo::Ref(tuple.$t6),
1725                        LiNo::Ref(tuple.$t7),
1726                    ],
1727                }
1728            }
1729        }
1730    };
1731    (@str_lino_tuple 8, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt) => {
1732        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1733            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1734                LiNo::Link {
1735                    id: Some(tuple.$t0.to_string()),
1736                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7],
1737                }
1738            }
1739        }
1740    };
1741    (@lino_tuple 8, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt) => {
1742        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1743            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1744                LiNo::Link {
1745                    id: None,
1746                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7],
1747                }
1748            }
1749        }
1750    };
1751
1752    // Implementation for 9-tuples
1753    (@str_tuple 9, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt) => {
1754        impl From<(&str, &str, &str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1755            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
1756                LiNo::Link {
1757                    id: Some(tuple.$t0.to_string()),
1758                    values: vec![
1759                        LiNo::Ref(tuple.$t1.to_string()),
1760                        LiNo::Ref(tuple.$t2.to_string()),
1761                        LiNo::Ref(tuple.$t3.to_string()),
1762                        LiNo::Ref(tuple.$t4.to_string()),
1763                        LiNo::Ref(tuple.$t5.to_string()),
1764                        LiNo::Ref(tuple.$t6.to_string()),
1765                        LiNo::Ref(tuple.$t7.to_string()),
1766                        LiNo::Ref(tuple.$t8.to_string()),
1767                    ],
1768                }
1769            }
1770        }
1771    };
1772    (@string_tuple 9, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt) => {
1773        impl From<(String, String, String, String, String, String, String, String, String)> for LiNo<String> {
1774            fn from(tuple: (String, String, String, String, String, String, String, String, String)) -> Self {
1775                LiNo::Link {
1776                    id: Some(tuple.$t0),
1777                    values: vec![
1778                        LiNo::Ref(tuple.$t1),
1779                        LiNo::Ref(tuple.$t2),
1780                        LiNo::Ref(tuple.$t3),
1781                        LiNo::Ref(tuple.$t4),
1782                        LiNo::Ref(tuple.$t5),
1783                        LiNo::Ref(tuple.$t6),
1784                        LiNo::Ref(tuple.$t7),
1785                        LiNo::Ref(tuple.$t8),
1786                    ],
1787                }
1788            }
1789        }
1790    };
1791    (@str_lino_tuple 9, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt) => {
1792        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1793            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1794                LiNo::Link {
1795                    id: Some(tuple.$t0.to_string()),
1796                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8],
1797                }
1798            }
1799        }
1800    };
1801    (@lino_tuple 9, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt) => {
1802        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1803            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1804                LiNo::Link {
1805                    id: None,
1806                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8],
1807                }
1808            }
1809        }
1810    };
1811
1812    // Implementation for 10-tuples
1813    (@str_tuple 10, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt) => {
1814        impl From<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1815            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
1816                LiNo::Link {
1817                    id: Some(tuple.$t0.to_string()),
1818                    values: vec![
1819                        LiNo::Ref(tuple.$t1.to_string()),
1820                        LiNo::Ref(tuple.$t2.to_string()),
1821                        LiNo::Ref(tuple.$t3.to_string()),
1822                        LiNo::Ref(tuple.$t4.to_string()),
1823                        LiNo::Ref(tuple.$t5.to_string()),
1824                        LiNo::Ref(tuple.$t6.to_string()),
1825                        LiNo::Ref(tuple.$t7.to_string()),
1826                        LiNo::Ref(tuple.$t8.to_string()),
1827                        LiNo::Ref(tuple.$t9.to_string()),
1828                    ],
1829                }
1830            }
1831        }
1832    };
1833    (@string_tuple 10, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt) => {
1834        impl From<(String, String, String, String, String, String, String, String, String, String)> for LiNo<String> {
1835            fn from(tuple: (String, String, String, String, String, String, String, String, String, String)) -> Self {
1836                LiNo::Link {
1837                    id: Some(tuple.$t0),
1838                    values: vec![
1839                        LiNo::Ref(tuple.$t1),
1840                        LiNo::Ref(tuple.$t2),
1841                        LiNo::Ref(tuple.$t3),
1842                        LiNo::Ref(tuple.$t4),
1843                        LiNo::Ref(tuple.$t5),
1844                        LiNo::Ref(tuple.$t6),
1845                        LiNo::Ref(tuple.$t7),
1846                        LiNo::Ref(tuple.$t8),
1847                        LiNo::Ref(tuple.$t9),
1848                    ],
1849                }
1850            }
1851        }
1852    };
1853    (@str_lino_tuple 10, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt) => {
1854        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1855            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1856                LiNo::Link {
1857                    id: Some(tuple.$t0.to_string()),
1858                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9],
1859                }
1860            }
1861        }
1862    };
1863    (@lino_tuple 10, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt) => {
1864        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1865            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1866                LiNo::Link {
1867                    id: None,
1868                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9],
1869                }
1870            }
1871        }
1872    };
1873
1874    // Implementation for 11-tuples
1875    (@str_tuple 11, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt) => {
1876        impl From<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1877            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
1878                LiNo::Link {
1879                    id: Some(tuple.$t0.to_string()),
1880                    values: vec![
1881                        LiNo::Ref(tuple.$t1.to_string()),
1882                        LiNo::Ref(tuple.$t2.to_string()),
1883                        LiNo::Ref(tuple.$t3.to_string()),
1884                        LiNo::Ref(tuple.$t4.to_string()),
1885                        LiNo::Ref(tuple.$t5.to_string()),
1886                        LiNo::Ref(tuple.$t6.to_string()),
1887                        LiNo::Ref(tuple.$t7.to_string()),
1888                        LiNo::Ref(tuple.$t8.to_string()),
1889                        LiNo::Ref(tuple.$t9.to_string()),
1890                        LiNo::Ref(tuple.$t10.to_string()),
1891                    ],
1892                }
1893            }
1894        }
1895    };
1896    (@string_tuple 11, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt) => {
1897        impl From<(String, String, String, String, String, String, String, String, String, String, String)> for LiNo<String> {
1898            fn from(tuple: (String, String, String, String, String, String, String, String, String, String, String)) -> Self {
1899                LiNo::Link {
1900                    id: Some(tuple.$t0),
1901                    values: vec![
1902                        LiNo::Ref(tuple.$t1),
1903                        LiNo::Ref(tuple.$t2),
1904                        LiNo::Ref(tuple.$t3),
1905                        LiNo::Ref(tuple.$t4),
1906                        LiNo::Ref(tuple.$t5),
1907                        LiNo::Ref(tuple.$t6),
1908                        LiNo::Ref(tuple.$t7),
1909                        LiNo::Ref(tuple.$t8),
1910                        LiNo::Ref(tuple.$t9),
1911                        LiNo::Ref(tuple.$t10),
1912                    ],
1913                }
1914            }
1915        }
1916    };
1917    (@str_lino_tuple 11, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt) => {
1918        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1919            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1920                LiNo::Link {
1921                    id: Some(tuple.$t0.to_string()),
1922                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9, tuple.$t10],
1923                }
1924            }
1925        }
1926    };
1927    (@lino_tuple 11, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt) => {
1928        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1929            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1930                LiNo::Link {
1931                    id: None,
1932                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9, tuple.$t10],
1933                }
1934            }
1935        }
1936    };
1937
1938    // Implementation for 12-tuples
1939    (@str_tuple 12, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt, $t11:tt) => {
1940        impl From<(&str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str)> for LiNo<String> {
1941            fn from(tuple: (&str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str, &str)) -> Self {
1942                LiNo::Link {
1943                    id: Some(tuple.$t0.to_string()),
1944                    values: vec![
1945                        LiNo::Ref(tuple.$t1.to_string()),
1946                        LiNo::Ref(tuple.$t2.to_string()),
1947                        LiNo::Ref(tuple.$t3.to_string()),
1948                        LiNo::Ref(tuple.$t4.to_string()),
1949                        LiNo::Ref(tuple.$t5.to_string()),
1950                        LiNo::Ref(tuple.$t6.to_string()),
1951                        LiNo::Ref(tuple.$t7.to_string()),
1952                        LiNo::Ref(tuple.$t8.to_string()),
1953                        LiNo::Ref(tuple.$t9.to_string()),
1954                        LiNo::Ref(tuple.$t10.to_string()),
1955                        LiNo::Ref(tuple.$t11.to_string()),
1956                    ],
1957                }
1958            }
1959        }
1960    };
1961    (@string_tuple 12, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt, $t11:tt) => {
1962        impl From<(String, String, String, String, String, String, String, String, String, String, String, String)> for LiNo<String> {
1963            fn from(tuple: (String, String, String, String, String, String, String, String, String, String, String, String)) -> Self {
1964                LiNo::Link {
1965                    id: Some(tuple.$t0),
1966                    values: vec![
1967                        LiNo::Ref(tuple.$t1),
1968                        LiNo::Ref(tuple.$t2),
1969                        LiNo::Ref(tuple.$t3),
1970                        LiNo::Ref(tuple.$t4),
1971                        LiNo::Ref(tuple.$t5),
1972                        LiNo::Ref(tuple.$t6),
1973                        LiNo::Ref(tuple.$t7),
1974                        LiNo::Ref(tuple.$t8),
1975                        LiNo::Ref(tuple.$t9),
1976                        LiNo::Ref(tuple.$t10),
1977                        LiNo::Ref(tuple.$t11),
1978                    ],
1979                }
1980            }
1981        }
1982    };
1983    (@str_lino_tuple 12, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt, $t11:tt) => {
1984        impl From<(&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1985            fn from(tuple: (&str, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1986                LiNo::Link {
1987                    id: Some(tuple.$t0.to_string()),
1988                    values: vec![tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9, tuple.$t10, tuple.$t11],
1989                }
1990            }
1991        }
1992    };
1993    (@lino_tuple 12, $t0:tt, $t1:tt, $t2:tt, $t3:tt, $t4:tt, $t5:tt, $t6:tt, $t7:tt, $t8:tt, $t9:tt, $t10:tt, $t11:tt) => {
1994        impl From<(LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)> for LiNo<String> {
1995            fn from(tuple: (LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>, LiNo<String>)) -> Self {
1996                LiNo::Link {
1997                    id: None,
1998                    values: vec![tuple.$t0, tuple.$t1, tuple.$t2, tuple.$t3, tuple.$t4, tuple.$t5, tuple.$t6, tuple.$t7, tuple.$t8, tuple.$t9, tuple.$t10, tuple.$t11],
1999                }
2000            }
2001        }
2002    };
2003
2004    // Entry point - generates all four types for a given tuple size
2005    (2) => {
2006        impl_tuple_from!(@str_tuple 2, 0, 1);
2007        impl_tuple_from!(@string_tuple 2, 0, 1);
2008        impl_tuple_from!(@str_lino_tuple 2, 0, 1);
2009        impl_tuple_from!(@lino_tuple 2, 0, 1);
2010    };
2011    (3) => {
2012        impl_tuple_from!(@str_tuple 3, 0, 1, 2);
2013        impl_tuple_from!(@string_tuple 3, 0, 1, 2);
2014        impl_tuple_from!(@str_lino_tuple 3, 0, 1, 2);
2015        impl_tuple_from!(@lino_tuple 3, 0, 1, 2);
2016    };
2017    (4) => {
2018        impl_tuple_from!(@str_tuple 4, 0, 1, 2, 3);
2019        impl_tuple_from!(@string_tuple 4, 0, 1, 2, 3);
2020        impl_tuple_from!(@str_lino_tuple 4, 0, 1, 2, 3);
2021        impl_tuple_from!(@lino_tuple 4, 0, 1, 2, 3);
2022    };
2023    (5) => {
2024        impl_tuple_from!(@str_tuple 5, 0, 1, 2, 3, 4);
2025        impl_tuple_from!(@string_tuple 5, 0, 1, 2, 3, 4);
2026        impl_tuple_from!(@str_lino_tuple 5, 0, 1, 2, 3, 4);
2027        impl_tuple_from!(@lino_tuple 5, 0, 1, 2, 3, 4);
2028    };
2029    (6) => {
2030        impl_tuple_from!(@str_tuple 6, 0, 1, 2, 3, 4, 5);
2031        impl_tuple_from!(@string_tuple 6, 0, 1, 2, 3, 4, 5);
2032        impl_tuple_from!(@str_lino_tuple 6, 0, 1, 2, 3, 4, 5);
2033        impl_tuple_from!(@lino_tuple 6, 0, 1, 2, 3, 4, 5);
2034    };
2035    (7) => {
2036        impl_tuple_from!(@str_tuple 7, 0, 1, 2, 3, 4, 5, 6);
2037        impl_tuple_from!(@string_tuple 7, 0, 1, 2, 3, 4, 5, 6);
2038        impl_tuple_from!(@str_lino_tuple 7, 0, 1, 2, 3, 4, 5, 6);
2039        impl_tuple_from!(@lino_tuple 7, 0, 1, 2, 3, 4, 5, 6);
2040    };
2041    (8) => {
2042        impl_tuple_from!(@str_tuple 8, 0, 1, 2, 3, 4, 5, 6, 7);
2043        impl_tuple_from!(@string_tuple 8, 0, 1, 2, 3, 4, 5, 6, 7);
2044        impl_tuple_from!(@str_lino_tuple 8, 0, 1, 2, 3, 4, 5, 6, 7);
2045        impl_tuple_from!(@lino_tuple 8, 0, 1, 2, 3, 4, 5, 6, 7);
2046    };
2047    (9) => {
2048        impl_tuple_from!(@str_tuple 9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
2049        impl_tuple_from!(@string_tuple 9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
2050        impl_tuple_from!(@str_lino_tuple 9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
2051        impl_tuple_from!(@lino_tuple 9, 0, 1, 2, 3, 4, 5, 6, 7, 8);
2052    };
2053    (10) => {
2054        impl_tuple_from!(@str_tuple 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
2055        impl_tuple_from!(@string_tuple 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
2056        impl_tuple_from!(@str_lino_tuple 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
2057        impl_tuple_from!(@lino_tuple 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
2058    };
2059    (11) => {
2060        impl_tuple_from!(@str_tuple 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
2061        impl_tuple_from!(@string_tuple 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
2062        impl_tuple_from!(@str_lino_tuple 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
2063        impl_tuple_from!(@lino_tuple 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
2064    };
2065    (12) => {
2066        impl_tuple_from!(@str_tuple 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
2067        impl_tuple_from!(@string_tuple 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
2068        impl_tuple_from!(@str_lino_tuple 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
2069        impl_tuple_from!(@lino_tuple 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
2070    };
2071}
2072
2073// Generate implementations for tuples of sizes 2 through 12
2074// This follows the Rust standard library convention of supporting up to 12-tuples
2075impl_tuple_from!(2);
2076impl_tuple_from!(3);
2077impl_tuple_from!(4);
2078impl_tuple_from!(5);
2079impl_tuple_from!(6);
2080impl_tuple_from!(7);
2081impl_tuple_from!(8);
2082impl_tuple_from!(9);
2083impl_tuple_from!(10);
2084impl_tuple_from!(11);
2085impl_tuple_from!(12);
2086
2087// Vec-based conversions for arbitrary-length link creation
2088//
2089// These implementations provide an escape hatch for creating links with more
2090// than 12 values, or when the number of values is determined at runtime.
2091//
2092// Note: Rust does not support variadic generics (as of Rust 1.92), which means
2093// we cannot implement `From` for tuples of arbitrary length. This is a fundamental
2094// limitation of Rust's type system. The Rust standard library faces the same
2095// limitation, which is why traits like `Debug`, `Default`, `Hash`, etc. are only
2096// implemented for tuples up to 12 elements.
2097//
2098// For more information, see:
2099// - https://github.com/rust-lang/rfcs/issues/376 (Draft RFC: variadic generics)
2100// - https://github.com/rust-lang/rust/issues/10124 (RFC: variadic generics)
2101//
2102// Alternative approaches for arbitrary-length links:
2103// 1. Use the `LiNoBuilder` API for fluent construction
2104// 2. Use `LiNo::new()` or `LiNo::anonymous()` with a `Vec`
2105// 3. Use the `From<Vec<_>>` implementations below
2106
2107/// Convert a Vec of strings into an anonymous link.
2108///
2109/// # Examples
2110/// ```
2111/// use links_notation::LiNo;
2112///
2113/// // Create anonymous link from vector of any size
2114/// let values: Vec<&str> = (1..=20).map(|_| "val").collect();
2115/// let link: LiNo<String> = values.into();
2116/// ```
2117impl From<Vec<&str>> for LiNo<String> {
2118    fn from(values: Vec<&str>) -> Self {
2119        LiNo::Link {
2120            id: None,
2121            values: values
2122                .into_iter()
2123                .map(|s| LiNo::Ref(s.to_string()))
2124                .collect(),
2125        }
2126    }
2127}
2128
2129/// Convert a Vec of Strings into an anonymous link.
2130impl From<Vec<String>> for LiNo<String> {
2131    fn from(values: Vec<String>) -> Self {
2132        LiNo::Link {
2133            id: None,
2134            values: values.into_iter().map(LiNo::Ref).collect(),
2135        }
2136    }
2137}
2138
2139/// Convert a Vec of LiNo into an anonymous link.
2140impl From<Vec<LiNo<String>>> for LiNo<String> {
2141    fn from(values: Vec<LiNo<String>>) -> Self {
2142        LiNo::Link { id: None, values }
2143    }
2144}
2145
2146/// Convert a tuple of (id, Vec<values>) into a named link.
2147///
2148/// # Examples
2149/// ```
2150/// use links_notation::LiNo;
2151///
2152/// // Create named link with arbitrary number of values
2153/// let values: Vec<&str> = vec!["v1", "v2", "v3", "v4", "v5"];
2154/// let link: LiNo<String> = ("myLink", values).into();
2155/// assert_eq!(format!("{}", link), "(myLink: v1 v2 v3 v4 v5)");
2156/// ```
2157impl From<(&str, Vec<&str>)> for LiNo<String> {
2158    fn from((id, values): (&str, Vec<&str>)) -> Self {
2159        LiNo::Link {
2160            id: Some(id.to_string()),
2161            values: values
2162                .into_iter()
2163                .map(|s| LiNo::Ref(s.to_string()))
2164                .collect(),
2165        }
2166    }
2167}
2168
2169/// Convert a tuple of (id, Vec<String>) into a named link.
2170impl From<(String, Vec<String>)> for LiNo<String> {
2171    fn from((id, values): (String, Vec<String>)) -> Self {
2172        LiNo::Link {
2173            id: Some(id),
2174            values: values.into_iter().map(LiNo::Ref).collect(),
2175        }
2176    }
2177}
2178
2179/// Convert a tuple of (id, Vec<LiNo>) into a named link.
2180impl From<(&str, Vec<LiNo<String>>)> for LiNo<String> {
2181    fn from((id, values): (&str, Vec<LiNo<String>>)) -> Self {
2182        LiNo::Link {
2183            id: Some(id.to_string()),
2184            values,
2185        }
2186    }
2187}
2188
2189/// Convert a tuple of (String id, Vec<LiNo>) into a named link.
2190impl From<(String, Vec<LiNo<String>>)> for LiNo<String> {
2191    fn from((id, values): (String, Vec<LiNo<String>>)) -> Self {
2192        LiNo::Link {
2193            id: Some(id),
2194            values,
2195        }
2196    }
2197}