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