Skip to main content

substrait_explain/extensions/
simple.rs

1use std::collections::BTreeMap;
2use std::collections::btree_map::Entry;
3use std::fmt;
4
5use pext::simple_extension_declaration::MappingType;
6use substrait::proto::extensions as pext;
7use thiserror::Error;
8
9pub const EXTENSIONS_HEADER: &str = "=== Extensions";
10pub const EXTENSION_URNS_HEADER: &str = "URNs:";
11pub const EXTENSION_FUNCTIONS_HEADER: &str = "Functions:";
12pub const EXTENSION_TYPES_HEADER: &str = "Types:";
13pub const EXTENSION_TYPE_VARIATIONS_HEADER: &str = "Type Variations:";
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16pub enum ExtensionKind {
17    // Urn,
18    Function,
19    Type,
20    TypeVariation,
21}
22
23impl ExtensionKind {
24    pub fn name(&self) -> &'static str {
25        match self {
26            // ExtensionKind::Urn => "urn",
27            ExtensionKind::Function => "function",
28            ExtensionKind::Type => "type",
29            ExtensionKind::TypeVariation => "type_variation",
30        }
31    }
32}
33
34impl fmt::Display for ExtensionKind {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            // ExtensionKind::Urn => write!(f, "URN"),
38            ExtensionKind::Function => write!(f, "Function"),
39            ExtensionKind::Type => write!(f, "Type"),
40            ExtensionKind::TypeVariation => write!(f, "Type Variation"),
41        }
42    }
43}
44
45#[derive(Error, Debug, PartialEq, Clone)]
46pub enum InsertError {
47    #[error("Extension declaration missing mapping type")]
48    MissingMappingType,
49
50    #[error("Duplicate URN anchor {anchor} for {prev} and {name}")]
51    DuplicateUrnAnchor {
52        anchor: u32,
53        prev: String,
54        name: String,
55    },
56
57    #[error("Duplicate extension {kind} anchor {anchor} for {prev} and {name}")]
58    DuplicateAnchor {
59        kind: ExtensionKind,
60        anchor: u32,
61        prev: String,
62        name: String,
63    },
64
65    #[error("Missing URN anchor {urn} for extension {kind} anchor {anchor} name {name}")]
66    MissingUrn {
67        kind: ExtensionKind,
68        anchor: u32,
69        name: String,
70        urn: u32,
71    },
72
73    #[error(
74        "Duplicate extension {kind} anchor {anchor} for {prev} and {name}, also missing URN {urn}"
75    )]
76    DuplicateAndMissingUrn {
77        kind: ExtensionKind,
78        anchor: u32,
79        prev: String,
80        name: String,
81        urn: u32,
82    },
83
84    #[error("Invalid {kind} name '{name}': u! prefix is only valid for type names")]
85    InvalidName { kind: ExtensionKind, name: String },
86}
87
88/// A Substrait compound function name, e.g. `"equal:any_any"`, `"count:"`, or `"add"`.
89///
90/// A name is either *simple* (no `:`, e.g. `"add"`) or *full* (has a `:`,
91/// e.g. `"count:"` or `"equal:any_any"`). The part after the `:` is the
92/// type-signature suffix, which encodes the argument types (`"i64_i64"`) or
93/// zero argument types (`"count:"`).
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct CompoundName {
96    /// Full name including the signature suffix, e.g. `"equal:any_any"`.
97    name: String,
98    /// Byte index of the `:` separator, or `name.len()` when absent.
99    index: usize,
100}
101
102impl CompoundName {
103    pub fn new(name: impl Into<String>) -> Self {
104        let name = name.into();
105        let index = name.find(':').unwrap_or(name.len());
106        Self { name, index }
107    }
108
109    /// The base name (part before the first `:`), e.g. `"equal"`.
110    pub fn base(&self) -> &str {
111        &self.name[..self.index]
112    }
113
114    /// The full compound name, e.g. `"equal:any_any"` or `"count:"`.
115    pub fn full(&self) -> &str {
116        &self.name
117    }
118
119    /// `true` when the name includes a `:` signature suffix (e.g. `"equal:any_any"`, `"count:"`).
120    pub fn has_signature(&self) -> bool {
121        self.index < self.name.len()
122    }
123
124    /// Returns `true` if `self` (a stored name) is matched by `pattern` (a written name).
125    ///
126    /// - Simple pattern (no `:`): matches any stored name with the same base.
127    /// - Full pattern (has `:`): exact match only.
128    pub fn matches(&self, pattern: &str) -> bool {
129        if pattern.contains(':') {
130            self.full() == pattern
131        } else {
132            self.base() == pattern
133        }
134    }
135}
136
137impl fmt::Display for CompoundName {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.write_str(&self.name)
140    }
141}
142
143/// ExtensionLookup contains mappings from anchors to extension URNs, functions,
144/// types, and type variations.
145#[derive(Default, Debug, Clone, PartialEq)]
146pub struct SimpleExtensions {
147    // Maps from extension URN anchor to URN
148    urns: BTreeMap<u32, String>,
149    // Maps from anchor and extension kind to (URN anchor, name)
150    extensions: BTreeMap<(u32, ExtensionKind), (u32, CompoundName)>,
151}
152
153impl SimpleExtensions {
154    pub fn new() -> Self {
155        Self::default()
156    }
157
158    pub fn from_extensions<'a>(
159        urns: impl IntoIterator<Item = &'a pext::SimpleExtensionUrn>,
160        extensions: impl IntoIterator<Item = &'a pext::SimpleExtensionDeclaration>,
161    ) -> (Self, Vec<InsertError>) {
162        // TODO: this checks for missing URNs and duplicate anchors, but not
163        // duplicate names with the same anchor.
164
165        let mut exts = Self::new();
166
167        let mut errors = Vec::<InsertError>::new();
168
169        for urn in urns {
170            if let Err(e) = exts.add_extension_urn(urn.urn.clone(), urn.extension_urn_anchor) {
171                errors.push(e);
172            }
173        }
174
175        for extension in extensions {
176            match &extension.mapping_type {
177                Some(MappingType::ExtensionType(t)) => {
178                    if let Err(e) = exts.add_extension(
179                        ExtensionKind::Type,
180                        t.extension_urn_reference,
181                        t.type_anchor,
182                        t.name.clone(),
183                    ) {
184                        errors.push(e);
185                    }
186                }
187                Some(MappingType::ExtensionFunction(f)) => {
188                    if let Err(e) = exts.add_extension(
189                        ExtensionKind::Function,
190                        f.extension_urn_reference,
191                        f.function_anchor,
192                        f.name.clone(),
193                    ) {
194                        errors.push(e);
195                    }
196                }
197                Some(MappingType::ExtensionTypeVariation(v)) => {
198                    if let Err(e) = exts.add_extension(
199                        ExtensionKind::TypeVariation,
200                        v.extension_urn_reference,
201                        v.type_variation_anchor,
202                        v.name.clone(),
203                    ) {
204                        errors.push(e);
205                    }
206                }
207                None => {
208                    errors.push(InsertError::MissingMappingType);
209                }
210            }
211        }
212
213        (exts, errors)
214    }
215
216    pub fn add_extension_urn(&mut self, urn: String, anchor: u32) -> Result<(), InsertError> {
217        match self.urns.entry(anchor) {
218            Entry::Occupied(e) => {
219                return Err(InsertError::DuplicateUrnAnchor {
220                    anchor,
221                    prev: e.get().clone(),
222                    name: urn,
223                });
224            }
225            Entry::Vacant(e) => {
226                e.insert(urn);
227            }
228        }
229        Ok(())
230    }
231
232    pub fn add_extension(
233        &mut self,
234        kind: ExtensionKind,
235        urn: u32,
236        anchor: u32,
237        name: String,
238    ) -> Result<(), InsertError> {
239        // Normalize type names: strip the optional "u!" prefix so that "u!json" and "json"
240        // are stored identically and resolve to the same anchor.
241        // For other extension kinds, "u!" prefix is invalid per the Substrait spec.
242        let name = if kind == ExtensionKind::Type {
243            name.strip_prefix("u!").map(str::to_string).unwrap_or(name)
244        } else if name.starts_with("u!") {
245            return Err(InsertError::InvalidName { kind, name });
246        } else {
247            name
248        };
249        let missing_urn = !self.urns.contains_key(&urn);
250
251        let prev = match self.extensions.entry((anchor, kind)) {
252            Entry::Occupied(e) => Some(e.get().1.full().to_string()),
253            Entry::Vacant(v) => {
254                v.insert((urn, CompoundName::new(name.clone())));
255                None
256            }
257        };
258
259        match (missing_urn, prev) {
260            (true, Some(prev)) => Err(InsertError::DuplicateAndMissingUrn {
261                kind,
262                anchor,
263                prev,
264                name,
265                urn,
266            }),
267            (false, Some(prev)) => Err(InsertError::DuplicateAnchor {
268                kind,
269                anchor,
270                prev,
271                name,
272            }),
273            (true, None) => Err(InsertError::MissingUrn {
274                kind,
275                anchor,
276                name,
277                urn,
278            }),
279            (false, None) => Ok(()),
280        }
281    }
282
283    pub fn is_empty(&self) -> bool {
284        self.urns.is_empty() && self.extensions.is_empty()
285    }
286
287    /// Convert the extension URNs to protobuf format for Plan construction.
288    pub fn to_extension_urns(&self) -> Vec<pext::SimpleExtensionUrn> {
289        self.urns
290            .iter()
291            .map(|(anchor, urn)| pext::SimpleExtensionUrn {
292                extension_urn_anchor: *anchor,
293                urn: urn.clone(),
294            })
295            .collect()
296    }
297
298    /// Convert the extensions to protobuf format for Plan construction.
299    pub fn to_extension_declarations(&self) -> Vec<pext::SimpleExtensionDeclaration> {
300        self.extensions
301            .iter()
302            .map(|((anchor, kind), (urn_ref, name))| {
303                let mapping_type = match kind {
304                    ExtensionKind::Function => MappingType::ExtensionFunction(
305                        pext::simple_extension_declaration::ExtensionFunction {
306                            extension_urn_reference: *urn_ref,
307                            function_anchor: *anchor,
308                            name: name.full().to_string(),
309                        },
310                    ),
311                    ExtensionKind::Type => MappingType::ExtensionType(
312                        pext::simple_extension_declaration::ExtensionType {
313                            extension_urn_reference: *urn_ref,
314                            type_anchor: *anchor,
315                            name: name.full().to_string(),
316                        },
317                    ),
318                    ExtensionKind::TypeVariation => MappingType::ExtensionTypeVariation(
319                        pext::simple_extension_declaration::ExtensionTypeVariation {
320                            extension_urn_reference: *urn_ref,
321                            type_variation_anchor: *anchor,
322                            name: name.full().to_string(),
323                        },
324                    ),
325                };
326                pext::SimpleExtensionDeclaration {
327                    mapping_type: Some(mapping_type),
328                }
329            })
330            .collect()
331    }
332
333    /// Write the extensions to the given writer, with the given indent.
334    ///
335    /// The header will be included if there are any extensions.
336    pub fn write<W: fmt::Write>(&self, w: &mut W, indent: &str) -> fmt::Result {
337        if self.is_empty() {
338            // No extensions, so no need to write anything.
339            return Ok(());
340        }
341
342        writeln!(w, "{EXTENSIONS_HEADER}")?;
343        if !self.urns.is_empty() {
344            writeln!(w, "{EXTENSION_URNS_HEADER}")?;
345            for (anchor, urn) in &self.urns {
346                writeln!(w, "{indent}@{anchor:3}: {urn}")?;
347            }
348        }
349
350        let kinds_and_headers = [
351            (ExtensionKind::Function, EXTENSION_FUNCTIONS_HEADER),
352            (ExtensionKind::Type, EXTENSION_TYPES_HEADER),
353            (
354                ExtensionKind::TypeVariation,
355                EXTENSION_TYPE_VARIATIONS_HEADER,
356            ),
357        ];
358        for (kind, header) in kinds_and_headers {
359            let mut filtered = self
360                .extensions
361                .iter()
362                .filter(|((_a, k), _)| *k == kind)
363                .peekable();
364            if filtered.peek().is_none() {
365                continue;
366            }
367
368            writeln!(w, "{header}")?;
369            for ((anchor, _), (urn_ref, name)) in filtered {
370                writeln!(w, "{indent}#{anchor:3} @{urn_ref:3}: {name}")?;
371            }
372        }
373        Ok(())
374    }
375
376    pub fn to_string(&self, indent: &str) -> String {
377        let mut output = String::new();
378        self.write(&mut output, indent).unwrap();
379        output
380    }
381}
382
383#[derive(Error, Debug, Clone, PartialEq)]
384pub enum MissingReference {
385    #[error("Missing URN for {0}")]
386    MissingUrn(u32),
387    #[error("Missing anchor for {0}: {1}")]
388    MissingAnchor(ExtensionKind, u32),
389    #[error("Missing name for {0}: {1}")]
390    MissingName(ExtensionKind, String),
391    #[error("Mismatched {0}: {1}#{2}")]
392    /// When the name of the value does not match the expected name
393    Mismatched(ExtensionKind, String, u32),
394    #[error("Duplicate name without anchor for {0}: {1}")]
395    DuplicateName(ExtensionKind, String),
396}
397
398#[derive(Debug, Clone, PartialEq)]
399pub struct SimpleExtension {
400    pub kind: ExtensionKind,
401    pub name: String,
402    pub anchor: u32,
403    pub urn: u32,
404}
405
406/// The result of resolving a function anchor to its full metadata.
407pub struct ResolvedFunction<'a> {
408    pub anchor: u32,
409    pub urn: u32,
410    /// The full compound name stored for this anchor.
411    pub name: &'a CompoundName,
412    /// `true` when the base name is unique across all registered functions
413    /// (controls whether the signature suffix is needed in compact mode).
414    pub base_name_unique: bool,
415    /// `true` when the full compound name is unique across all registered
416    /// functions (controls whether the `#anchor` suffix is needed).
417    pub name_unique: bool,
418}
419
420impl SimpleExtensions {
421    pub fn find_urn(&self, anchor: u32) -> Result<&str, MissingReference> {
422        self.urns
423            .get(&anchor)
424            .map(String::as_str)
425            .ok_or(MissingReference::MissingUrn(anchor))
426    }
427
428    pub fn find_by_anchor(
429        &self,
430        kind: ExtensionKind,
431        anchor: u32,
432    ) -> Result<(u32, &CompoundName), MissingReference> {
433        let &(urn, ref name) = self
434            .extensions
435            .get(&(anchor, kind))
436            .ok_or(MissingReference::MissingAnchor(kind, anchor))?;
437
438        Ok((urn, name))
439    }
440
441    pub fn find_by_name(&self, kind: ExtensionKind, name: &str) -> Result<u32, MissingReference> {
442        let mut matches = self
443            .extensions
444            .iter()
445            .filter(move |((_a, k), (_, n))| *k == kind && n.full() == name)
446            .map(|((anchor, _), _)| *anchor);
447
448        let anchor = matches
449            .next()
450            .ok_or(MissingReference::MissingName(kind, name.to_string()))?;
451
452        match matches.next() {
453            Some(_) => Err(MissingReference::DuplicateName(kind, name.to_string())),
454            None => Ok(anchor),
455        }
456    }
457
458    /// Returns `true` when no other extension of the same kind has the same
459    /// full compound name (i.e. the anchor display can be suppressed).
460    ///
461    /// Returns `Err` when `anchor` is not registered for `kind`.
462    pub fn is_name_unique(
463        &self,
464        kind: ExtensionKind,
465        anchor: u32,
466        name: &str,
467    ) -> Result<bool, MissingReference> {
468        let mut found = false;
469        let mut other = false;
470        for (&(a, k), (_, n)) in self.extensions.iter() {
471            if k != kind {
472                continue;
473            }
474
475            if a == anchor {
476                found = true;
477                if n.full() != name {
478                    return Err(MissingReference::Mismatched(kind, name.to_string(), anchor));
479                }
480                continue;
481            }
482
483            if n.full() != name {
484                // Neither anchor nor name match, so this is irrelevant.
485                continue;
486            }
487
488            // At this point, the anchor is different, but the name is the same.
489            other = true;
490            if found {
491                break;
492            }
493        }
494
495        match (found, other) {
496            // Found the one we're looking for, and no other matches.
497            (true, false) => Ok(true),
498            // Found the one we're looking for, and another match.
499            (true, true) => Ok(false),
500            // Didn't find the one we're looking for.
501            (false, _) => Err(MissingReference::MissingAnchor(kind, anchor)),
502        }
503    }
504
505    /// Look up a function anchor and return its full resolution metadata.
506    /// The caller already has `anchor` from the
507    /// Substrait plan and needs the name, URN, and uniqueness flags.
508    pub fn lookup_function(&self, anchor: u32) -> Result<ResolvedFunction<'_>, MissingReference> {
509        let (urn, name) = self.find_by_anchor(ExtensionKind::Function, anchor)?;
510        let name_unique = self.is_name_unique(ExtensionKind::Function, anchor, name.full())?;
511        let base_name_unique = self.is_base_name_unique(ExtensionKind::Function, anchor)?;
512        Ok(ResolvedFunction {
513            anchor,
514            urn,
515            name,
516            name_unique,
517            base_name_unique,
518        })
519    }
520
521    /// Resolve a [`CompoundName`] written in the plan to a [`ResolvedFunction`].
522    ///
523    /// * `anchor = Some(a)` — the anchor identifies the function; the name is
524    ///   validated for consistency using [`CompoundName::matches`].
525    /// * `anchor = None` — the name must be unambiguous on its own:
526    ///   - Simple (no `:`): base-name search; fails if more than one function
527    ///     shares that base name.
528    ///   - Full (has `:`): exact match only.
529    pub fn resolve_function(
530        &self,
531        name: &str,
532        anchor: Option<u32>,
533    ) -> Result<ResolvedFunction<'_>, MissingReference> {
534        let resolved_anchor = match anchor {
535            Some(a) => {
536                let (_, stored) = self.find_by_anchor(ExtensionKind::Function, a)?;
537                if stored.matches(name) {
538                    a
539                } else {
540                    return Err(MissingReference::Mismatched(
541                        ExtensionKind::Function,
542                        name.to_string(),
543                        a,
544                    ));
545                }
546            }
547            None => {
548                if name.contains(':') {
549                    self.find_by_name(ExtensionKind::Function, name)?
550                } else {
551                    self.find_by_base_name(ExtensionKind::Function, name)?
552                }
553            }
554        };
555        self.lookup_function(resolved_anchor)
556    }
557
558    fn is_base_name_unique(
559        &self,
560        kind: ExtensionKind,
561        anchor: u32,
562    ) -> Result<bool, MissingReference> {
563        let (_, name) = self.find_by_anchor(kind, anchor)?;
564        let my_base = name.base();
565
566        let other_exists = self
567            .extensions
568            .iter()
569            .any(|(&(a, k), (_, n))| k == kind && a != anchor && n.base() == my_base);
570
571        Ok(!other_exists)
572    }
573
574    fn find_by_base_name(&self, kind: ExtensionKind, base: &str) -> Result<u32, MissingReference> {
575        let mut matches = self
576            .extensions
577            .iter()
578            .filter(|&(&(_a, k), (_, n))| k == kind && n.matches(base))
579            .map(|(&(anchor, _), _)| anchor);
580
581        let anchor = matches
582            .next()
583            .ok_or_else(|| MissingReference::MissingName(kind, base.to_string()))?;
584
585        match matches.next() {
586            Some(_) => Err(MissingReference::DuplicateName(kind, base.to_string())),
587            None => Ok(anchor),
588        }
589    }
590}
591
592#[cfg(test)]
593mod tests {
594    use pext::simple_extension_declaration::{
595        ExtensionFunction, ExtensionType, ExtensionTypeVariation, MappingType,
596    };
597    use substrait::proto::extensions as pext;
598
599    use super::*;
600
601    fn new_urn(anchor: u32, urn_str: &str) -> pext::SimpleExtensionUrn {
602        pext::SimpleExtensionUrn {
603            extension_urn_anchor: anchor,
604            urn: urn_str.to_string(),
605        }
606    }
607
608    fn new_ext_fn(anchor: u32, urn_ref: u32, name: &str) -> pext::SimpleExtensionDeclaration {
609        pext::SimpleExtensionDeclaration {
610            mapping_type: Some(MappingType::ExtensionFunction(ExtensionFunction {
611                extension_urn_reference: urn_ref,
612                function_anchor: anchor,
613                name: name.to_string(),
614            })),
615        }
616    }
617
618    fn new_ext_type(anchor: u32, urn_ref: u32, name: &str) -> pext::SimpleExtensionDeclaration {
619        pext::SimpleExtensionDeclaration {
620            mapping_type: Some(MappingType::ExtensionType(ExtensionType {
621                extension_urn_reference: urn_ref,
622                type_anchor: anchor,
623                name: name.to_string(),
624            })),
625        }
626    }
627
628    fn new_type_var(anchor: u32, urn_ref: u32, name: &str) -> pext::SimpleExtensionDeclaration {
629        pext::SimpleExtensionDeclaration {
630            mapping_type: Some(MappingType::ExtensionTypeVariation(
631                ExtensionTypeVariation {
632                    extension_urn_reference: urn_ref,
633                    type_variation_anchor: anchor,
634                    name: name.to_string(),
635                },
636            )),
637        }
638    }
639
640    fn assert_no_errors(errs: &[InsertError]) {
641        for err in errs {
642            println!("Error: {err:?}");
643        }
644        assert!(errs.is_empty());
645    }
646
647    fn unwrap_new_extensions<'a>(
648        urns: impl IntoIterator<Item = &'a pext::SimpleExtensionUrn>,
649        extensions: impl IntoIterator<Item = &'a pext::SimpleExtensionDeclaration>,
650    ) -> SimpleExtensions {
651        let (exts, errs) = SimpleExtensions::from_extensions(urns, extensions);
652        assert_no_errors(&errs);
653        exts
654    }
655
656    #[test]
657    fn test_extension_lookup_empty() {
658        let lookup = SimpleExtensions::new();
659        assert!(lookup.find_urn(1).is_err());
660        assert!(lookup.find_by_anchor(ExtensionKind::Function, 1).is_err());
661        assert!(lookup.find_by_anchor(ExtensionKind::Type, 1).is_err());
662        assert!(
663            lookup
664                .find_by_anchor(ExtensionKind::TypeVariation, 1)
665                .is_err()
666        );
667        assert!(lookup.find_by_name(ExtensionKind::Function, "any").is_err());
668        assert!(lookup.find_by_name(ExtensionKind::Type, "any").is_err());
669        assert!(
670            lookup
671                .find_by_name(ExtensionKind::TypeVariation, "any")
672                .is_err()
673        );
674    }
675
676    #[test]
677    fn test_from_extensions_basic() {
678        let urns = vec![new_urn(1, "urn1"), new_urn(2, "urn2")];
679        let extensions = vec![
680            new_ext_fn(10, 1, "func1"),
681            new_ext_type(20, 1, "type1"),
682            new_type_var(30, 2, "var1"),
683        ];
684        let exts = unwrap_new_extensions(&urns, &extensions);
685
686        assert_eq!(exts.find_urn(1).unwrap(), "urn1");
687        assert_eq!(exts.find_urn(2).unwrap(), "urn2");
688        assert!(exts.find_urn(3).is_err());
689
690        let (urn, name) = exts.find_by_anchor(ExtensionKind::Function, 10).unwrap();
691        assert_eq!(name.full(), "func1");
692        assert_eq!(urn, 1);
693        assert!(exts.find_by_anchor(ExtensionKind::Function, 11).is_err());
694
695        let (urn, name) = exts.find_by_anchor(ExtensionKind::Type, 20).unwrap();
696        assert_eq!(name.full(), "type1");
697        assert_eq!(urn, 1);
698        assert!(exts.find_by_anchor(ExtensionKind::Type, 21).is_err());
699
700        let (urn, name) = exts
701            .find_by_anchor(ExtensionKind::TypeVariation, 30)
702            .unwrap();
703        assert_eq!(name.full(), "var1");
704        assert_eq!(urn, 2);
705        assert!(
706            exts.find_by_anchor(ExtensionKind::TypeVariation, 31)
707                .is_err()
708        );
709    }
710
711    #[test]
712    fn test_from_extensions_duplicates() {
713        let urns = vec![
714            new_urn(1, "urn_old"),
715            new_urn(1, "urn_new"),
716            new_urn(2, "second"),
717        ];
718        let extensions = vec![
719            new_ext_fn(10, 1, "func_old"),
720            new_ext_fn(10, 2, "func_new"), // Duplicate function anchor
721            new_ext_fn(11, 3, "func_missing"),
722        ];
723        let (exts, errs) = SimpleExtensions::from_extensions(&urns, &extensions);
724        assert_eq!(
725            errs,
726            vec![
727                InsertError::DuplicateUrnAnchor {
728                    anchor: 1,
729                    name: "urn_new".to_string(),
730                    prev: "urn_old".to_string()
731                },
732                InsertError::DuplicateAnchor {
733                    kind: ExtensionKind::Function,
734                    anchor: 10,
735                    prev: "func_old".to_string(),
736                    name: "func_new".to_string()
737                },
738                InsertError::MissingUrn {
739                    kind: ExtensionKind::Function,
740                    anchor: 11,
741                    name: "func_missing".to_string(),
742                    urn: 3,
743                },
744            ]
745        );
746
747        // This is a duplicate anchor, so the first one is used.
748        assert_eq!(exts.find_urn(1).unwrap(), "urn_old");
749        let (urn, name) = exts.find_by_anchor(ExtensionKind::Function, 10).unwrap();
750        assert_eq!(urn, 1);
751        assert_eq!(name.full(), "func_old");
752    }
753
754    #[test]
755    fn test_from_extensions_invalid_mapping_type() {
756        let extensions = vec![pext::SimpleExtensionDeclaration { mapping_type: None }];
757
758        let (_exts, errs) = SimpleExtensions::from_extensions(vec![], &extensions);
759        assert_eq!(errs.len(), 1);
760        let err = &errs[0];
761        assert_eq!(err, &InsertError::MissingMappingType);
762    }
763
764    #[test]
765    fn test_find_by_name() {
766        let urns = vec![new_urn(1, "urn1")];
767        let extensions = vec![
768            new_ext_fn(10, 1, "name1"),
769            new_ext_fn(11, 1, "name2"),
770            new_ext_fn(12, 1, "name1"), // Duplicate name
771            new_ext_type(20, 1, "type_name1"),
772            new_type_var(30, 1, "var_name1"),
773        ];
774        let exts = unwrap_new_extensions(&urns, &extensions);
775
776        let err = exts
777            .find_by_name(ExtensionKind::Function, "name1")
778            .unwrap_err();
779        assert_eq!(
780            err,
781            MissingReference::DuplicateName(ExtensionKind::Function, "name1".to_string())
782        );
783
784        let found = exts.find_by_name(ExtensionKind::Function, "name2").unwrap();
785        assert_eq!(found, 11);
786
787        let found = exts
788            .find_by_name(ExtensionKind::Type, "type_name1")
789            .unwrap();
790        assert_eq!(found, 20);
791
792        let err = exts
793            .find_by_name(ExtensionKind::Type, "non_existent_type_name")
794            .unwrap_err();
795        assert_eq!(
796            err,
797            MissingReference::MissingName(
798                ExtensionKind::Type,
799                "non_existent_type_name".to_string()
800            )
801        );
802
803        let found = exts
804            .find_by_name(ExtensionKind::TypeVariation, "var_name1")
805            .unwrap();
806        assert_eq!(found, 30);
807
808        let err = exts
809            .find_by_name(ExtensionKind::TypeVariation, "non_existent_var_name")
810            .unwrap_err();
811        assert_eq!(
812            err,
813            MissingReference::MissingName(
814                ExtensionKind::TypeVariation,
815                "non_existent_var_name".to_string()
816            )
817        );
818    }
819
820    #[test]
821    fn test_display_extension_lookup_empty() {
822        let lookup = SimpleExtensions::new();
823        let mut output = String::new();
824        lookup.write(&mut output, "  ").unwrap();
825        let expected = r"";
826        assert_eq!(output, expected.trim_start());
827    }
828
829    #[test]
830    fn test_display_extension_lookup_with_content() {
831        let urns = vec![
832            new_urn(1, "/my/urn1"),
833            new_urn(42, "/another/urn"),
834            new_urn(4091, "/big/anchor"),
835        ];
836        let extensions = vec![
837            new_ext_fn(10, 1, "my_func"),
838            new_ext_type(20, 1, "my_type"),
839            new_type_var(30, 42, "my_var"),
840            new_ext_fn(11, 42, "another_func"),
841            new_ext_fn(108812, 4091, "big_func"),
842        ];
843        let exts = unwrap_new_extensions(&urns, &extensions);
844        let display_str = exts.to_string("  ");
845
846        let expected = r"
847=== Extensions
848URNs:
849  @  1: /my/urn1
850  @ 42: /another/urn
851  @4091: /big/anchor
852Functions:
853  # 10 @  1: my_func
854  # 11 @ 42: another_func
855  #108812 @4091: big_func
856Types:
857  # 20 @  1: my_type
858Type Variations:
859  # 30 @ 42: my_var
860";
861        assert_eq!(display_str, expected.trim_start());
862    }
863
864    #[test]
865    fn test_extensions_output() {
866        // Manually build the extensions
867        let mut extensions = SimpleExtensions::new();
868        extensions
869            .add_extension_urn("/urn/common".to_string(), 1)
870            .unwrap();
871        extensions
872            .add_extension_urn("/urn/specific_funcs".to_string(), 2)
873            .unwrap();
874        extensions
875            .add_extension(ExtensionKind::Function, 1, 10, "func_a".to_string())
876            .unwrap();
877        extensions
878            .add_extension(ExtensionKind::Function, 2, 11, "func_b_special".to_string())
879            .unwrap();
880        extensions
881            .add_extension(ExtensionKind::Type, 1, 20, "SomeType".to_string())
882            .unwrap();
883        extensions
884            .add_extension(ExtensionKind::TypeVariation, 2, 30, "VarX".to_string())
885            .unwrap();
886
887        // Convert to string
888        let output = extensions.to_string("  ");
889
890        // The output should match the expected format
891        let expected_output = r#"
892=== Extensions
893URNs:
894  @  1: /urn/common
895  @  2: /urn/specific_funcs
896Functions:
897  # 10 @  1: func_a
898  # 11 @  2: func_b_special
899Types:
900  # 20 @  1: SomeType
901Type Variations:
902  # 30 @  2: VarX
903"#;
904
905        assert_eq!(output, expected_output.trim_start());
906    }
907
908    #[test]
909    fn test_compound_name_full_zero_arg_type_signature() {
910        // A Full name whose type signature encodes zero argument types (nothing after the colon).
911        let n = CompoundName::new("add:");
912        assert_eq!(n.full(), "add:");
913        assert_eq!(n.base(), "add");
914        // Full pattern: exact match only.
915        assert!(n.matches("add:"));
916        assert!(!n.matches("add:i64_i64"));
917        // Simple pattern: base match.
918        assert!(n.matches("add"));
919    }
920
921    #[test]
922    fn test_compound_name_with_signature() {
923        let n = CompoundName::new("equal:any_any");
924        assert_eq!(n.full(), "equal:any_any");
925        assert_eq!(n.base(), "equal");
926
927        let n2 = CompoundName::new("regexp_match_substring:str_str_i64");
928        assert_eq!(n2.base(), "regexp_match_substring");
929        assert_eq!(n2.full(), "regexp_match_substring:str_str_i64");
930
931        let n3 = CompoundName::new("add:i64_i64");
932        assert_eq!(n3.base(), "add");
933    }
934
935    // ---- Tests for lookup_function ----
936
937    fn make_overloaded_extensions() -> SimpleExtensions {
938        let urns = vec![new_urn(1, "urn:comparison")];
939        let extensions = vec![
940            new_ext_fn(1, 1, "equal:any_any"),
941            new_ext_fn(2, 1, "equal:str_str"),
942            new_ext_fn(3, 1, "add:i64_i64"),
943        ];
944        unwrap_new_extensions(&urns, &extensions)
945    }
946
947    #[test]
948    fn test_lookup_function_uniqueness_flags() {
949        // `equal:any_any` and `equal:str_str` share the base name "equal" →
950        // base_name_unique false, compound name unique within the one URN.
951        // `add:i64_i64` is the only "add" → both flags true.
952        let exts = make_overloaded_extensions();
953
954        let r1 = exts.lookup_function(1).unwrap();
955        assert_eq!(r1.name.full(), "equal:any_any");
956        assert!(!r1.base_name_unique, "two 'equal' overloads");
957        assert!(r1.name_unique, "compound name 'equal:any_any' is unique");
958
959        let r2 = exts.lookup_function(2).unwrap();
960        assert_eq!(r2.name.full(), "equal:str_str");
961        assert!(!r2.base_name_unique);
962        assert!(r2.name_unique);
963
964        let r3 = exts.lookup_function(3).unwrap();
965        assert_eq!(r3.name.full(), "add:i64_i64");
966        assert!(r3.base_name_unique, "only one 'add' overload");
967        assert!(r3.name_unique, "compound name appears only once");
968    }
969
970    #[test]
971    fn test_lookup_function_missing_anchor() {
972        let exts = SimpleExtensions::new();
973        assert!(exts.lookup_function(99).is_err());
974    }
975
976    #[test]
977    fn test_lookup_function_plain_name_overloaded_across_urns() {
978        // Same plain name in two URNs → base_name_unique false, name_unique false.
979        let urns = vec![new_urn(1, "urn1"), new_urn(2, "urn2")];
980        let extensions = vec![
981            new_ext_fn(1, 1, "duplicated"),
982            new_ext_fn(2, 2, "duplicated"),
983        ];
984        let exts = unwrap_new_extensions(&urns, &extensions);
985
986        let r = exts.lookup_function(1).unwrap();
987        assert!(!r.base_name_unique);
988        assert!(!r.name_unique);
989    }
990
991    #[test]
992    fn test_lookup_function_different_base_names_each_unique() {
993        // `equal:any_any` and `like:str_str` have distinct base names → each unique.
994        let urns = vec![new_urn(1, "urn1")];
995        let extensions = vec![
996            new_ext_fn(1, 1, "equal:any_any"),
997            new_ext_fn(2, 1, "like:str_str"),
998        ];
999        let exts = unwrap_new_extensions(&urns, &extensions);
1000
1001        assert!(exts.lookup_function(1).unwrap().base_name_unique);
1002        assert!(exts.lookup_function(2).unwrap().base_name_unique);
1003    }
1004
1005    // ---- Tests for resolve_function ----
1006
1007    fn make_resolve_extensions() -> SimpleExtensions {
1008        let urns = vec![new_urn(1, "test_urn")];
1009        let extensions = vec![
1010            new_ext_fn(1, 1, "equal:any_any"),
1011            new_ext_fn(2, 1, "equal:str_str"),
1012            new_ext_fn(3, 1, "add:i64_i64"),
1013            new_ext_fn(4, 1, "add:"),
1014        ];
1015        unwrap_new_extensions(&urns, &extensions)
1016    }
1017
1018    #[test]
1019    fn test_resolve_function_with_anchor() {
1020        // Explicit anchor: exact compound name and mismatch errors.
1021        let exts = make_resolve_extensions();
1022
1023        // Exact compound name matches stored name → resolves.
1024        assert_eq!(
1025            exts.resolve_function("equal:any_any", Some(1))
1026                .unwrap()
1027                .anchor,
1028            1
1029        );
1030
1031        // Simple (no-sig) form matches any stored name with the same base.
1032        assert_eq!(exts.resolve_function("add", Some(3)).unwrap().anchor, 3);
1033        assert_eq!(exts.resolve_function("add", Some(4)).unwrap().anchor, 4);
1034
1035        // Full form requires exact match — "add:" does not match anchor 3 (stored "add:i64_i64").
1036        assert!(exts.resolve_function("add:", Some(3)).is_err());
1037
1038        // "add" does not match stored "equal:any_any" (different base) → error.
1039        assert!(exts.resolve_function("add", Some(1)).is_err());
1040
1041        // Same base name, different overload → error.
1042        assert!(exts.resolve_function("equal:any_any", Some(2)).is_err());
1043    }
1044
1045    #[test]
1046    fn test_resolve_function_without_anchor() {
1047        // No anchor: exact compound name resolution.
1048        let exts = make_resolve_extensions();
1049
1050        assert_eq!(
1051            exts.resolve_function("equal:any_any", None).unwrap().anchor,
1052            1
1053        );
1054        assert_eq!(
1055            exts.resolve_function("equal:str_str", None).unwrap().anchor,
1056            2
1057        );
1058    }
1059
1060    #[test]
1061    fn test_resolve_function_without_anchor_full_sig() {
1062        // Full form (has colon) resolves by exact match only — no fallback.
1063        let exts = make_resolve_extensions();
1064
1065        assert_eq!(exts.resolve_function("add:", None).unwrap().anchor, 4);
1066        assert_eq!(
1067            exts.resolve_function("add:i64_i64", None).unwrap().anchor,
1068            3
1069        );
1070
1071        // "equal:" is not registered → error (no fallback to base-name search).
1072        assert!(exts.resolve_function("equal:", None).is_err());
1073    }
1074
1075    #[test]
1076    fn test_resolve_function_without_anchor_no_sig() {
1077        // No-signature form (no colon) without anchor: base-name search.
1078        let exts = make_resolve_extensions();
1079
1080        // Ambiguous base name → error.
1081        assert!(exts.resolve_function("add", None).is_err());
1082        assert!(exts.resolve_function("equal", None).is_err());
1083    }
1084
1085    #[test]
1086    fn test_resolve_function_plain_stored_name() {
1087        // Functions stored without a signature still resolve by their plain name.
1088        let urns = vec![new_urn(1, "urn")];
1089        let extensions = vec![new_ext_fn(10, 1, "coalesce")];
1090        let exts = unwrap_new_extensions(&urns, &extensions);
1091        assert_eq!(exts.resolve_function("coalesce", None).unwrap().anchor, 10);
1092    }
1093
1094    #[test]
1095    fn test_resolve_function_not_found() {
1096        let exts = SimpleExtensions::new();
1097        assert!(exts.resolve_function("nonexistent", None).is_err());
1098    }
1099
1100    #[test]
1101    fn test_compound_name_roundtrip_in_extensions_section() {
1102        // Verify that compound names survive a write → parse roundtrip through
1103        // the Extensions section text format.
1104        let urns = vec![new_urn(1, "substrait:functions_comparison")];
1105        let extensions = vec![
1106            new_ext_fn(1, 1, "equal:any_any"),
1107            new_ext_fn(2, 1, "equal:str_str"),
1108        ];
1109        let exts = unwrap_new_extensions(&urns, &extensions);
1110
1111        let text = exts.to_string("  ");
1112        assert!(
1113            text.contains("equal:any_any"),
1114            "compound name must appear in output"
1115        );
1116        assert!(
1117            text.contains("equal:str_str"),
1118            "compound name must appear in output"
1119        );
1120    }
1121
1122    #[test]
1123    fn test_insert_error_invalid_name_type_variation() {
1124        // u! prefix is invalid on TypeVariation declarations, just as it is for Functions.
1125        let mut exts = SimpleExtensions::new();
1126        exts.add_extension_urn("urn:example".to_string(), 1)
1127            .unwrap();
1128        let err = exts
1129            .add_extension(ExtensionKind::TypeVariation, 1, 30, "u!myvar".to_string())
1130            .unwrap_err();
1131        assert_eq!(
1132            err,
1133            InsertError::InvalidName {
1134                kind: ExtensionKind::TypeVariation,
1135                name: "u!myvar".to_string()
1136            }
1137        );
1138    }
1139}