Skip to main content

links_notation/
lib.rs

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