Skip to main content

links_notation/
lib.rs

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