hyperon/metta/runner/modules/
mod_names.rs

1//! Implements logic for working with module names, including a low-allocation layered tree structure 
2
3use crate::metta::runner::*;
4use hyperon_common::owned_or_borrowed::OwnedOrBorrowed;
5
6/// The name of the top module in a runner
7pub const TOP_MOD_NAME: &'static str = "top";
8
9/// The name to refer to the current module in the module name hierarchy
10pub const SELF_MOD_NAME: &'static str = "self";
11
12/// The separator between parent and child module names in a module name path
13pub const MOD_NAME_SEPARATOR: char = ':';
14
15/// Private struct, A node in a tree to locate loaded mods by name
16///
17/// # Name Resolution Behavior
18/// `top` is a reserved module name for the module at the top of the runner
19/// `top.some_mod` and `some_mod` are equivalent.  In other words, `top` is optional in a path
20/// `self` is an alias for the current module
21/// `self.some_mod` is a private sub-module of the current module
22///
23#[derive(Clone, Debug, Default)]
24pub(crate) struct ModNameNode {
25    pub mod_id: ModId,
26    children: Option<HashMap<String, ModNameNode>>
27}
28
29/// Returns `Some(path)`, with `path` being the relative portion following `self:`, if the name
30/// begins with "self:". Returns "" if the name exactly equals "self".  Otherwise returns None
31pub(crate) fn mod_name_relative_path(name: &str) -> Option<&str> {
32    mod_name_remove_prefix(name, SELF_MOD_NAME)
33}
34
35/// Returns the portion of `mod_path` that is a sub-path on top of `base_path`.  The reverse
36/// of `concat_relative_module_path`
37pub(crate) fn mod_name_remove_prefix<'a>(name: &'a str, prefix: &str) -> Option<&'a str> {
38    if name.starts_with(prefix) {
39        if name.len() == prefix.len() {
40            Some("")
41        } else {
42            if name.as_bytes()[prefix.len()] == MOD_NAME_SEPARATOR as u8 {
43                Some(&name[(prefix.len()+1)..])
44            } else {
45                None
46            }
47        }
48    } else {
49        None
50    }
51}
52
53/// Returns the part of a module name path after the last separator, or the entire path if it does not
54/// contain a separator.  May panic if the mod-name-path is invalid
55pub(crate) fn mod_name_from_path(path: &str) -> &str {
56    let mut start_idx = 0;
57    for (idx, the_char) in path.char_indices() {
58        if the_char == MOD_NAME_SEPARATOR {
59            start_idx = idx+1;
60        }
61    }
62    &path[start_idx..]
63}
64
65pub(crate) fn chop_trailing_separator(path: &str) -> &str {
66    if path.ends_with(MOD_NAME_SEPARATOR) {
67        &path[0..path.len()-1]
68    } else {
69        path
70    }
71}
72
73/// Join a relative module path as a sub-module to `&self`
74pub(crate) fn concat_relative_module_path(base_path: &str, relative_path: &str) -> String {
75    if relative_path.len() > 0 {
76        format!("{base_path}:{relative_path}")
77    } else {
78        base_path.to_string()
79    }
80}
81
82/// Normalize a module name into a canonical name-path form, and expanding a relative module-path
83///
84/// On input, module names that don't begin with either `top` nor `self` will be assumed to be
85/// relative to the current module.  On output, the result will be an absolute path beginning
86/// with `top`.
87pub(crate) fn normalize_relative_module_name(base_path: &str, mod_name: &str) -> Result<String, String> {
88    let mod_name = chop_trailing_separator(mod_name);
89    let full_name_path = match mod_name_relative_path(mod_name) {
90        Some(remainder) => concat_relative_module_path(base_path, remainder),
91        None => {
92            match mod_name_remove_prefix(mod_name, TOP_MOD_NAME) {
93                Some(remainder) => {
94                    if remainder.len() > 0 {
95                        format!("{}:{}", TOP_MOD_NAME, remainder)
96                    } else {
97                        TOP_MOD_NAME.to_string()
98                    }
99                },
100                None => concat_relative_module_path(base_path, mod_name),
101            }
102        },
103    };
104    Ok(full_name_path)
105}
106
107/// Decomposes name path components into individual module names.  Reverse of [compose_name_path]
108#[allow(dead_code)] //Some clients are behind feature gates
109pub(crate) fn decompose_name_path(name: &str) -> Result<Vec<&str>, String> {
110    let mut components: Vec<&str> = vec![];
111    let (_, _, last) = ModNameNode::parse_parent_generic(ModNameNode::top(), name, &OverlayMap::none(),
112        |node, _| Some(node),
113        |_, component| {components.push(component)})?;
114    if last.len() > 0 {
115        components.push(last);
116    }
117    Ok(components)
118}
119
120/// Composes a name path from a slice of individual module names.  Reverse of [decompose_name_path]
121#[allow(dead_code)] //Some clients are behind feature gates
122pub(crate) fn compose_name_path(components: &[&str]) -> Result<String, String> {
123    let mut new_name = TOP_MOD_NAME.to_string();
124    for component in components {
125        new_name.push_str(":");
126        new_name.push_str(component);
127    }
128    Ok(new_name)
129}
130
131/// Internal map to allow subtrees to be overlaid and effectively merged
132#[derive(Debug)]
133struct OverlayMap<'a>(Option<smallvec::SmallVec<[(*const ModNameNode, &'a str, usize); 4]>>);
134
135impl<'a> OverlayMap<'a> {
136    fn none() -> Self {
137        Self(None)
138    }
139    fn with_capacity(capacity: usize) -> Self {
140        Self(Some(smallvec::SmallVec::with_capacity(capacity)))
141    }
142    fn push(&mut self, parent_node: *const ModNameNode, name: &'a str, subtree_idx: usize) {
143        match &mut self.0 {
144            Some(map) => map.push((parent_node, name, subtree_idx)),
145            None => panic!()
146        }
147    }
148    fn get(&self, parent_node: *const ModNameNode, name: &str) -> Option<usize> {
149        match &self.0 {
150            Some(map) => for (map_parent_node, map_name, subtree_idx) in map.iter() {
151                if std::ptr::eq(parent_node, *map_parent_node) && name == *map_name {
152                    return Some(*subtree_idx);
153                }
154            }
155            None => {}
156        }
157        None
158    }
159}
160
161impl ModNameNode {
162
163    /// Returns the node corresponding to the runner's "top" mod
164    pub fn top() -> Self {
165        Self::new(ModId::TOP)
166    }
167
168    pub fn new(mod_id: ModId) -> Self {
169        Self {
170            mod_id,
171            children: None,
172        }
173    }
174
175    /// Updates the ModId of an existing node, or creates the node if it doesn't exist
176    /// Does NOT recursively add multiple nodes
177    pub fn update(&mut self, name: &str, mod_id: ModId) -> Result<(), String> {
178        self.update_in_layered(&mut[], name, mod_id)
179    }
180
181    /// Layered version of [Self::update].  See [Self::resolve_layered] for details
182    pub fn update_in_layered(&mut self, subtrees: &mut[(&str, &mut Self)], name: &str, mod_id: ModId) -> Result<(), String> {
183        if name == TOP_MOD_NAME || name.len() == 0 {
184            self.mod_id = mod_id;
185            return Ok(());
186        }
187        let (parent_node, mod_name) = self.parse_parent_layered_mut(subtrees, name)?;
188        if mod_name == TOP_MOD_NAME || mod_name == SELF_MOD_NAME || mod_name.len() == 0 {
189            return Err(format!("illegal module name: {name}"));
190        }
191        match parent_node.get_child_mut(mod_name) {
192            Some(node) => node.mod_id = mod_id,
193            None => parent_node.add_child_node(mod_name.to_string(), Self::new(mod_id))
194        }
195        Ok(())
196    }
197
198    /// Adds a single new node to the tree.  Does NOT recursively add multiple nodes
199    ///
200    /// If an entry already exists at that name, the existing entry will be replaced
201    pub fn add(&mut self, name: &str, mod_id: ModId) -> Result<(), String> {
202        self.merge_subtree_into(name, Self::new(mod_id))
203    }
204
205    /// Adds a single new node to a layered tree.  See [Self::resolve_layered] for details
206    #[allow(dead_code)] //NOTE: This function "feels" like it belongs in a complete API, and might be used in the future.  See discussion on [ModuleInitFrame::add_module_to_name_tree]
207    pub fn add_to_layered(&mut self, subtrees: &mut[(&str, &mut Self)], name: &str, mod_id: ModId) -> Result<(), String> {
208        self.merge_subtree_into_layered(subtrees, name, Self::new(mod_id))
209    }
210
211    /// Merges `new_subtree` into `&self`, starting at `root_name`
212    pub fn merge_subtree_into(&mut self, root_name: &str, new_subtree: Self) -> Result<(), String> {
213        self.merge_subtree_into_layered(&mut[], root_name, new_subtree)
214    }
215
216    /// Layered version of [Self::merge_subtree_into].  See [Self::resolve_layered] for details
217    pub fn merge_subtree_into_layered(&mut self, layered_subtrees: &mut[(&str, &mut Self)], root_name: &str, new_subtree: Self) -> Result<(), String> {
218        let (parent_node, mod_name) = self.parse_parent_layered_mut(layered_subtrees, root_name)?;
219        if mod_name == TOP_MOD_NAME || mod_name == SELF_MOD_NAME || mod_name.len() == 0 {
220            return Err(format!("illegal module name: {root_name}"));
221        }
222        parent_node.add_child_node(mod_name.to_string(), new_subtree);
223        Ok(())
224    }
225
226    /// Returns a child node from a name
227    fn get_child(&self, child_name: &str) -> Option<&Self> {
228        match &self.children {
229            Some(children) => children.get(child_name),
230            None => None
231        }
232    }
233
234    /// Mutable version of [get_child]
235    fn get_child_mut(&mut self, child_name: &str) -> Option<&mut Self> {
236        match &mut self.children {
237            Some(children) => children.get_mut(child_name),
238            None => None
239        }
240    }
241
242    /// Adds a child node, operates on a single parent node
243    fn add_child_node(&mut self, child_name: String, child_node: Self) {
244        if self.children.is_none() {
245            self.children = Some(HashMap::new());
246        }
247        self.children.as_mut().unwrap().insert(child_name, child_node);
248    }
249
250    /// Walks a tree running the provided function on each node, including the root
251    pub fn visit_mut<F: FnMut(&str, &mut Self)>(&mut self, root_node_name: &str, mut f: F) {
252        self.visit_mut_internal(root_node_name, &mut f);
253    }
254
255    fn visit_mut_internal<F: FnMut(&str, &mut Self)>(&mut self, root_node_name: &str, f: &mut F) {
256        f(root_node_name, self);
257        if let Some(children) = &mut self.children {
258            for (child_name, child_node) in children.iter_mut() {
259                child_node.visit_mut_internal(child_name, f);
260            }
261        }
262    }
263
264    /// Returns the [ModId] of a module name/path within `&self`, or None if it can't be resolved
265    pub fn resolve(&self, name: &str) -> Option<ModId> {
266        self.name_to_node::<&Self>(&[], name).map(|node| node.mod_id)
267    }
268
269    /// Returns the [ModId] of a module name/path within a tree composed of `&self` and additional
270    /// layered subtrees, or None if it can't be resolved.
271    ///
272    /// Each subtree is oriented with respect to the tree defined by &self and prior elements of the
273    /// 'subtrees` argument. For example, if &self is `"top:root:trunk:branch"` and `subtrees` is the
274    /// following: `[("top:root:trunk", "twig")]`,  then resolving "top:root:trunk:twig:leaf" will
275    /// return a sub-node from `subtrees[0]`.
276    pub fn resolve_layered<SelfRefT: core::borrow::Borrow<Self>>(&self, subtrees: &[(&str, SelfRefT)], name: &str) -> Option<ModId> {
277        self.name_to_node(subtrees, name).map(|node| node.mod_id)
278    }
279
280    /// Resolves a module name/path within &self, and additional subtrees layered in
281    pub fn name_to_node<'a, 'slice, 'out, SelfRefT: core::borrow::Borrow<Self>>(&'a self, subtrees: &'slice[(&str, SelfRefT)], name: &str) -> Option<&'out Self>
282    where 'a: 'out, 'slice: 'out
283    {
284        if name == TOP_MOD_NAME || name.len() == 0 {
285            Some(self)
286        } else {
287            let map = self.build_overlay_map(subtrees).ok()?;
288            let (parent_node, mod_name) = self.parse_parent_layered_internal(name, &map, subtrees).ok()?;
289
290            if let Some(subtree_idx) = map.get(parent_node, mod_name) {
291                Some(subtrees.get(subtree_idx).unwrap().1.borrow())
292            } else {
293                parent_node.get_child(mod_name)
294            }
295        }
296    }
297
298    /// Mutable version of [Self::name_to_node]
299    #[allow(dead_code)] //NOTE: This function "feels" like it belongs in a complete API, but it's currently not needed in the upstream implementation
300    pub fn name_to_node_mut<'a, 'b, 'slice, 'out>(&'a mut self, subtrees: &'slice mut[(&str, &'b mut Self)], name: &str) -> Option<&'out mut Self>
301    where 'a: 'out, 'slice: 'out, 'b: 'out
302    {
303        _ = subtrees;
304        _ = name;
305        panic!("Need Polonius or Rust 2024 edition");
306        // TODO: The below function should work unmodified in Rust 2024 edition, once the Polonius borrow checker is enabled by default
307        // You can try it now on nightly by setting the RUSTFLAGS="-Zpolonius" environment variable
308        // let subtree_idx = if name == TOP_MOD_NAME || name.len() == 0 {
309        //     return Some(self);
310        // } else {
311        //     let map = self.build_overlay_map(subtrees).ok()?;
312        //     let (parent_node, mod_name) = self.parse_parent_layered_internal_mut(name, &map, subtrees).ok()?;
313
314        //     if let Some(subtree_idx) = map.get(parent_node, mod_name) {
315        //         subtree_idx
316        //     } else {
317        //         return parent_node.get_child_mut(mod_name);
318        //     }
319        // };
320        // Some(subtrees.get_mut(subtree_idx).unwrap().1)
321    }
322
323    /// Returns the node corresponding to any part of module name path before the last separator character,
324    /// and the remaining substring
325    #[allow(dead_code)] //NOTE: This function "feels" like it belongs in a complete API, but it's currently not needed in the upstream implementation because the upstream implementation goes straight to `parse_parent_layered_internal`
326    pub fn parse_parent<'a, 'b>(&'a self, name: &'b str) -> Result<(&'a Self, &'b str), String> {
327        self.parse_parent_layered::<&Self>(&[], name)
328    }
329
330    /// Same behavior as [parse_parent], but takes and returns a mutable reference
331    pub fn parse_parent_mut<'a, 'b>(&'a mut self, name: &'b str) -> Result<(&'a mut Self, &'b str), String> {
332        //NOTE: could also be implemented with parse_parent_layered_mut, but this way is less compiled code
333        Self::parse_parent_generic(self, name, &OverlayMap::none(),
334            |node, name| node.get_child_mut(name),
335            |_, _| {}).map(|(node, _subtree_idx, remaining)| (node, remaining))
336    }
337
338    /// Internal generic `parse_parent` that can expand to mutable and const versions.
339    /// Return value is (parent_node, subtree_idx, remaining_name_path)
340    /// If subtree_idx is None, the path was parsed to the end.  If it is Some, then the overlay_map
341    ///  indicated we need to follow a subtree.
342    fn parse_parent_generic<'a, SelfT, GetF, PushParentF>(gen_self: SelfT, name: &'a str, overlay_map: &OverlayMap, get_f: GetF, mut push_f: PushParentF) -> Result<(SelfT, Option<usize>, &'a str), String>
343        where
344        SelfT: core::borrow::Borrow<Self>,
345        GetF: Fn(SelfT, &str) -> Option<SelfT>,
346        PushParentF: FnMut(&mut SelfT, &'a str),
347    {
348        if name.len() == 0 {
349            return Ok((gen_self, None, &name))
350        }
351        let mut cur_node = gen_self;
352        let mut sym_start = 0;
353        for (idx, the_char) in name.char_indices() {
354            match the_char {
355                MOD_NAME_SEPARATOR => {
356                    let sym = &name[sym_start..idx];
357                    if sym == TOP_MOD_NAME && sym_start == 0 {
358                        sym_start = idx+1;
359                        continue;
360                    }
361                    push_f(&mut cur_node, sym);
362                    match overlay_map.get(cur_node.borrow(), sym) {
363                        Some(subtree_idx) => {
364                            sym_start = idx+1;
365                            return if sym_start < name.len() {
366                                Ok((cur_node, Some(subtree_idx), &name[sym_start..]))
367                            } else {
368                                Err(format!("Invalid module name format: {name}"))
369                            };
370                        },
371                        None => {
372                            match get_f(cur_node, sym) {
373                                Some(new_node) => {
374                                    cur_node = new_node;
375                                    sym_start = idx+1;
376                                },
377                                None => return Err(format!("Parent module not loaded: {sym}"))
378                            }
379                        }
380                    }
381                },
382                _ => {},
383            }
384        }
385        if sym_start < name.len() {
386            Ok((cur_node, None, &name[sym_start..]))
387        } else {
388            Err(format!("Invalid module name format: {name}"))
389        }
390    }
391
392    /// Returns the node corresponding to any part of module name path before the last separator character,
393    /// and the remaining substring, searching the composit tree
394    pub fn parse_parent_layered<'a, 'slice, 'out, 'str, SelfRefT: core::borrow::Borrow<Self>>(&'a self, subtrees: &'slice[(&str, SelfRefT)], name: &'str str) -> Result<(&'out Self, &'str str), String>
395    where 'a: 'out, 'slice: 'out
396    {
397        let map = self.build_overlay_map(subtrees)?;
398        self.parse_parent_layered_internal(name, &map, subtrees)
399    }
400
401    /// Internal method to Build the map connecting each subtree branch to its location in the main tree
402    fn build_overlay_map<'str, SelfRefT: core::borrow::Borrow<Self>>(&self, subtrees: &[(&'str str, SelfRefT)]) -> Result<OverlayMap<'str>, String> {
403        let mut map = OverlayMap::with_capacity(subtrees.len());
404        for (idx, (subtree_root_name, _subtree_root_node)) in subtrees.iter().enumerate() {
405            let (parent_node, remaining_name) = self.parse_parent_layered_internal(subtree_root_name, &map, subtrees)?;
406            map.push(parent_node, remaining_name, idx);
407        }
408        Ok(map)
409    }
410
411    /// Internal function to retry the parse, each time we move to a new sub-tree
412    fn parse_parent_layered_internal<'a, 'slice, 'out, 'str, SelfT: core::borrow::Borrow<Self>>(&'a self, name: &'str str, overlay_map: &OverlayMap, subtrees: &'slice[(&str, SelfT)]) -> Result<(&'out Self, &'str str), String>
413    where 'a: 'out, 'slice: 'out
414    {
415        let mut cur_node = self;
416        let mut remaining_name = name;
417        loop {
418            let (parent_node, subtree_idx, remainder) = Self::parse_parent_generic(cur_node, remaining_name, overlay_map,
419                |node, name| node.get_child(name),
420                |_, _| {})?;
421
422            match subtree_idx {
423                None => {return Ok((parent_node, remainder))},
424                Some(subtree_idx) => {
425                    cur_node = subtrees.get(subtree_idx).unwrap().1.borrow();
426                    remaining_name = remainder;
427                }
428            }
429        }
430    }
431
432    /// Mutable version of [Self::parse_parent_layered]
433    pub fn parse_parent_layered_mut<'a, 'b, 'slice, 'out, 'str>(&'a mut self, subtrees: &'slice mut[(&str, &'b mut Self)], name: &'str str) -> Result<(&'out mut Self, &'str str), String>
434    where 'a: 'out, 'b: 'out, 'slice: 'out
435    {
436        let map = self.build_overlay_map(subtrees)?;
437        self.parse_parent_layered_internal_mut(name, &map, subtrees)
438    }
439
440    /// Mutable version of [Self::parse_parent_layered_internal].  More backflips needed to keep mutable borrow
441    fn parse_parent_layered_internal_mut<'a, 'b, 'slice, 'out, 'str>(&'a mut self, name: &'str str, overlay_map: &OverlayMap, subtrees: &'slice mut[(&str, &'b mut Self)]) -> Result<(&'out mut Self, &'str str), String>
442    where 'a: 'out, 'b: 'out, 'slice: 'out
443    {
444        let mut cur_node = &*self;
445        let mut remaining_name = name;
446        let mut last_subtree_idx = None;
447        let (subtree_idx, local_name_path) = loop {
448            let (_parent_node, subtree_idx, remainder) = Self::parse_parent_generic(&*cur_node, remaining_name, overlay_map,
449                |node, name| node.get_child(name),
450                |_, _| {})?;
451
452            match subtree_idx {
453                None => {break (last_subtree_idx, remaining_name)},
454                Some(subtree_idx) => {
455                    cur_node = subtrees.get(subtree_idx).unwrap().1;
456                    last_subtree_idx = Some(subtree_idx);
457                    remaining_name = remainder;
458                }
459            }
460        };
461
462        let subtree_root = match subtree_idx {
463            None => self,
464            Some(subtree_idx) => subtrees.get_mut(subtree_idx).unwrap().1
465        };
466        subtree_root.parse_parent_mut(local_name_path)
467    }
468}
469
470/// A wrapper that allows for parameterized Display of a ModNameNode
471pub(crate) struct ModNameNodeDisplayWrapper<'a, F> {
472    node_name: Option<&'a str>,
473    node: &'a ModNameNode,
474    indent: &'a str,
475    mod_id_renderer: OwnedOrBorrowed<'a, F>,
476}
477
478impl<'a, F> ModNameNodeDisplayWrapper<'a, F>
479    where F: Fn(ModId, &mut std::fmt::Formatter<'_>) -> std::fmt::Result
480{
481    const TEE: &'static str = " ├─";
482    const CORNER: &'static str = " └─";
483    const PIPE: &'static str = " │ ";
484    const SPACE: &'static str = "   ";
485
486    /// Make a new ModNameNodeDisplayWrapper, to display a name tree
487    pub fn new(root_name: &'a str, node: &'a ModNameNode, mod_id_renderer: F) -> Self {
488        Self {
489            node_name: Some(root_name),
490            node,
491            indent: "",
492            mod_id_renderer: OwnedOrBorrowed::from(mod_id_renderer),
493        }
494    }
495
496    /// Internal function to make a ModNameNodeDisplayWrapper to display a subtree within a larger tree
497    fn new_subtree(child_node: &'a ModNameNode, child_indent: &'a str, mod_id_renderer: &'a F) -> Self {
498        Self {
499            node_name: None,
500            node: child_node,
501            indent: child_indent,
502            mod_id_renderer: OwnedOrBorrowed::from(mod_id_renderer),
503        }
504    }
505}
506
507impl<F> std::fmt::Display for ModNameNodeDisplayWrapper<'_, F> 
508    where F: Fn(ModId, &mut std::fmt::Formatter<'_>) -> std::fmt::Result
509{
510    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
511        match self.node_name {
512            Some(node_name) => {
513                let subtree = ModNameNodeDisplayWrapper::new_subtree(self.node, self.indent, self.mod_id_renderer.as_ref());
514                write!(f, "{node_name} = {subtree}")
515            },
516            None => {
517                (self.mod_id_renderer.as_ref())(self.node.mod_id, f)?;
518                writeln!(f)?;
519                if let Some(children) = &self.node.children {
520                    let mut iter = children.iter().peekable();
521                    while let Some((child_name, child_node)) = iter.next() {
522                        write!(f, "{}", self.indent)?;
523                        let child_indent = if iter.peek().is_some() {
524                            write!(f, "{}", Self::TEE)?;
525                            format!("{}{}", self.indent, Self::PIPE)
526                        } else {
527                            write!(f, "{}", Self::CORNER)?;
528                            format!("{}{}", self.indent, Self::SPACE)
529                        };
530                        let subtree = ModNameNodeDisplayWrapper::new_subtree(child_node, &child_indent, self.mod_id_renderer.as_ref());
531                        write!(f, "{child_name} = {subtree}")?;
532                    }
533                }
534                Ok(())
535            }
536        }
537    }
538}
539
540impl std::fmt::Display for ModNameNode {
541    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542        let wrapper = ModNameNodeDisplayWrapper::new(SELF_MOD_NAME, self, |mod_id: ModId, f: &mut std::fmt::Formatter| write!(f, "{}", mod_id.0));
543        write!(f, "{wrapper}")
544    }
545}
546
547/// Returns `true` if a str is a legal name for a module
548///
549/// A module name must be an ascii string, containing only alpha-numeric characters plus [`_`, `-`]
550#[allow(dead_code)] //Some clients are behind feature gates
551pub fn module_name_is_legal(name: &str) -> bool {
552    for the_char in name.chars() {
553        if !the_char.is_ascii() {
554            return false;
555        }
556        if !the_char.is_ascii_alphanumeric() &&
557            the_char != '-' &&
558            the_char != '_' {
559            return false;
560        }
561    }
562    return true;
563}
564
565/// Returns a legal module name composed from the supplied string, by removing or substituting
566/// all illlegal characters.  Returns None if that isn't possible
567#[allow(dead_code)] //Some clients are behind feature gates
568pub fn module_name_make_legal(name: &str) -> Option<String> {
569    let new_name: String = name.chars().filter(|&the_char| {
570        the_char.is_ascii_alphanumeric() ||
571        the_char == '-' ||
572        the_char != '_'
573    }).collect();
574    if new_name.len() > 0 {
575        Some(new_name)
576    } else {
577        None
578    }
579}
580
581/// This test is narrowly focussed on the module namespace path parsing behavior implemented in
582/// [ModNameNode], but it does not test any module operations
583#[test]
584fn module_name_parse_test() {
585    let mut top = ModNameNode::top();
586
587    assert!(top.add("top:sub1", ModId(1)).is_ok());
588    assert!(top.add("sub2", ModId(2)).is_ok());
589    assert!(top.add("sub2:suba", ModId(3)).is_ok());
590    assert!(top.add("sub2:suba:subA", ModId(4)).is_ok());
591    assert!(top.add("top:sub1:subb", ModId(5)).is_ok());
592
593    assert!(top.add("", ModId(6)).is_err());
594    assert!(top.add("top", ModId(6)).is_err());
595    assert!(top.add("sub2::suba:subB", ModId(7)).is_err());
596    assert!(top.add("sub2:suba:subB:", ModId(7)).is_err());
597
598    assert_eq!(top.resolve("top").unwrap(), top.mod_id);
599    assert_eq!(top.resolve("").unwrap(), top.mod_id);
600    assert!(top.resolve("top:").is_none());
601    assert!(top.resolve(":").is_none());
602
603    assert_eq!(top.resolve("sub1").unwrap(), ModId(1));
604    assert_eq!(top.resolve("top:sub2:suba:subA").unwrap(), ModId(4));
605    assert!(top.resolve("sub2:suba:subA:").is_none());
606    assert!(top.resolve("sub1:suba").is_none());
607
608// //NOTE: HashMap internals make the order unpredictable so this is hard to test
609// assert_eq!(format!("\n{top}"), r#"
610// self = 0
611//  ├─sub2 = 2
612//  │  └─suba = 3
613//  │     └─subA = 4
614//  └─sub1 = 1
615//     └─subb = 5
616// "#);
617
618}
619
620/// This test tests multiple [ModNameNode] layered together to form a single tree
621#[test]
622fn module_name_layered_parse_test() {
623    let mut top = ModNameNode::top();
624
625    assert!(top.add("A", ModId(1)).is_ok());
626    assert!(top.add("A:B", ModId(2)).is_ok());
627    assert!(top.add("A:B:C", ModId(3)).is_ok());
628
629    let mut branch = ModNameNode::new(ModId(10));
630    assert!(branch.add("b", ModId(11)).is_ok());
631    assert!(branch.add("b:c", ModId(12)).is_ok());
632    assert!(branch.add("b:c:d", ModId(13)).is_ok());
633
634    let mut leaf = ModNameNode::new(ModId(100));
635    assert!(leaf.add("1", ModId(101)).is_ok());
636    assert!(leaf.add("2", ModId(102)).is_ok());
637    assert!(leaf.add("3", ModId(103)).is_ok());
638
639    //Test one level of layering
640    let subtrees = [("A:B:a", &branch)];
641    assert_eq!(ModNameNode::resolve_layered(&top, &subtrees, "A:B:a").unwrap(), ModId(10));
642    assert_eq!(ModNameNode::resolve_layered(&top, &subtrees, "A:B:a:b").unwrap(), ModId(11));
643    assert_eq!(ModNameNode::resolve_layered(&top, &subtrees, "A:B:C").unwrap(), ModId(3));
644
645    //Test two levels
646    let subtrees = [("A:B:a", &branch), ("A:B:a:b:c:d:digits", &leaf)];
647    assert_eq!(ModNameNode::resolve_layered(&top, &subtrees, "A:B:a:b:c:d:digits:2").unwrap(), ModId(102));
648
649    //Test resolving to get mutable access
650    let mut subtrees = [("A:B:a", &mut branch), ("A:B:a:b:c:d:digits", &mut leaf)];
651    let (parent, _remaining_name) = ModNameNode::parse_parent_layered_mut(&mut top, &mut subtrees, "A:B:a:b:c:d:digits:1").unwrap();
652    assert!(parent.add("1:one", ModId(1001)).is_ok());
653    assert_eq!(ModNameNode::resolve_layered(&top, &subtrees, "A:B:a:b:c:d:digits:1:one").unwrap(), ModId(1001));
654}
655