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