Skip to main content

links_notation/
lib.rs

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