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