Skip to main content

links_notation/
lib.rs

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