Skip to main content

links_notation/
lib.rs

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