Skip to main content

links_notation/
lib.rs

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