nested_modules/lib.rs
1//! Nested modules.
2//!
3//! This package enables the integration of a (nested) module system into
4//! projects such as compilers, interpreters etc.
5//! Its speciality is the way it resolves module references:
6//! In particular, we can always only reference modules by
7//! their shortest possible path relative to the current position.
8//!
9//! For example, suppose we have the modules a/b/c, a/d/e and f.
10//! Furthermore, let us be in the module a/b/c.
11//! We can then reference
12//! a/b/c, a/b, a and the root module (ancestors of our current module)
13//! only by the empty path.
14//! Furthermore, we can reference
15//! a/d by the path d,
16//! a/d/e by the path d/e, and
17//! f by the path f.
18//!
19//! This restriction on module references enables
20//! a very simple implementation as well as
21//! the property that we can always wrap modules around other modules and
22//! be certain that this preserves valid references of the original module.
23//!
24//! In the following example, we create
25//! a module with root data 0 and the following submodules:
26//! * b: 1
27//! * a: 2
28//! * b: 3
29//!
30//! We then search for the module "b" twice from different modules:
31//! When we are inside "a", we find two occurrences of "b", namely "a"/"b" (3) and "b" (1).
32//! When we are inside the root module, we find only one occurrence of "b", namely "b" (1).
33//!
34//! ~~~
35//! # use nested_modules::{Context, Module};
36//! let mut ctx = Context::new();
37//! ctx.get_mut().data = 0;
38//! ctx.insert("b", Module::from(1));
39//! assert!(!ctx.close());
40//! ctx.open_or("a", Module::from(2));
41//! ctx.insert("b", Module::from(3));
42//!
43//! // we are now in module "a"
44//! assert_eq!(ctx.get().data, 2);
45//! // searching for a module "b" yields two results
46//! let result: Vec<_> = ctx.find(["b"].iter()).map(|m| m.data).collect();
47//! assert_eq!(result, vec![3, 1]);
48//! // searching for "a" yields no result, because we are inside it
49//! let result: Vec<_> = ctx.find(["a"].iter()).map(|m| m.data).collect();
50//! assert_eq!(result, vec![]);
51//! assert!(ctx.close());
52//!
53//! // we are now in the root module
54//! assert_eq!(ctx.get().data, 0);
55//! // searching for either module "b", "a", or "a"/"b" yields only one result now
56//! let result: Vec<_> = ctx.find(["b"].iter()).map(|m| m.data).collect();
57//! assert_eq!(result, vec![1]);
58//! let result: Vec<_> = ctx.find(["a"].iter()).map(|m| m.data).collect();
59//! assert_eq!(result, vec![2]);
60//! let result: Vec<_> = ctx.find(["a", "b"].iter()).map(|m| m.data).collect();
61//! assert_eq!(result, vec![3]);
62//! assert!(!ctx.close());
63//! ~~~
64
65#![no_std]
66extern crate alloc;
67
68use alloc::borrow::Borrow;
69use alloc::collections::BTreeMap;
70use alloc::vec::Vec;
71
72/// A module with data and submodules.
73pub struct Module<K, V> {
74 pub data: V,
75 modules: BTreeMap<K, Module<K, V>>,
76}
77
78impl<K: Ord, V> From<V> for Module<K, V> {
79 fn from(data: V) -> Self {
80 Self {
81 data,
82 modules: Default::default(),
83 }
84 }
85}
86
87impl<K: Ord, V: Default> Default for Module<K, V> {
88 fn default() -> Self {
89 Module {
90 data: Default::default(),
91 modules: Default::default(),
92 }
93 }
94}
95
96impl<K: Ord, V> Module<K, V> {
97 /// Return a reference to a submodule at the given path.
98 pub fn get<'q, Q: 'q, I>(mut self: &Self, path: I) -> Option<&Self>
99 where
100 K: Borrow<Q>,
101 Q: Ord + ?Sized,
102 I: Iterator<Item = &'q Q>,
103 {
104 for p in path {
105 self = self.modules.get(p)?;
106 }
107 Some(self)
108 }
109}
110
111/// A module with a path to an open submodule.
112pub struct Context<K, V> {
113 root: Module<K, V>,
114 open: Vec<(K, Module<K, V>)>,
115}
116
117impl<K, V> From<Module<K, V>> for Context<K, V> {
118 fn from(root: Module<K, V>) -> Self {
119 let open = Vec::new();
120 Self { root, open }
121 }
122}
123
124impl<K: Ord, V: Default> Default for Context<K, V> {
125 fn default() -> Self {
126 Self::from(Module::default())
127 }
128}
129
130impl<K: Ord, V: Default> Context<K, V> {
131 /// Create a new context with a default root module as current open module.
132 pub fn new() -> Self {
133 Default::default()
134 }
135
136 /// Open a default module inside the previously open module.
137 pub fn open_or_default(&mut self, name: K) {
138 let module = self.remove(&name).unwrap_or_default();
139 self.open.push((name, module))
140 }
141}
142
143impl<K: Ord, V> Context<K, V> {
144 /// Open a module inside the previously open module.
145 ///
146 /// Use the provided module if the module with the given name does not exist.
147 pub fn open_or(&mut self, name: K, module: Module<K, V>) {
148 let module = self.remove(&name).unwrap_or(module);
149 self.open.push((name, module))
150 }
151
152 /// Open a module inside the previously open module.
153 ///
154 /// Use the provided closure if the module with the given name does not exist.
155 pub fn open_or_else(&mut self, name: K, f: impl FnOnce() -> Module<K, V>) {
156 let module = self.remove(&name).unwrap_or_else(f);
157 self.open.push((name, module))
158 }
159
160 /// Insert a module into the currently open module.
161 pub fn insert(&mut self, name: K, module: Module<K, V>) -> Option<Module<K, V>> {
162 self.get_mut().modules.insert(name, module)
163 }
164
165 /// Remove a module from the currently open module.
166 pub fn remove(&mut self, name: &K) -> Option<Module<K, V>> {
167 self.get_mut().modules.remove(name)
168 }
169
170 /// Close the currently open module.
171 ///
172 /// Return false if the current open module is the root module.
173 pub fn close(&mut self) -> bool {
174 if let Some((name, module)) = self.open.pop() {
175 self.get_mut().modules.insert(name, module);
176 return true;
177 }
178 false
179 }
180
181 /// Find modules matching the given path from the currently open module.
182 pub fn find<'a, 'q, Q: 'q, I>(&'a self, path: I) -> impl Iterator<Item = &'a Module<K, V>>
183 where
184 K: Borrow<Q>,
185 Q: Ord + ?Sized,
186 I: Iterator<Item = &'q Q> + Clone,
187 {
188 let patc = path.clone();
189 let open = self.open.iter().rev();
190 let init = open.filter_map(move |o| o.1.get(patc.clone()));
191
192 // this could be written shorter with `core::iter::once_with`,
193 // but it would require a newer Rust version
194 let mut last = Some(move || self.root.get(path));
195 init.chain(core::iter::from_fn(move || last.take()?()))
196 }
197}
198
199impl<K, V> Context<K, V> {
200 /// Return a reference to the currently open module.
201 pub fn get(&self) -> &Module<K, V> {
202 match self.open.iter().last() {
203 None => &self.root,
204 Some((_name, module)) => module,
205 }
206 }
207
208 /// Return a mutable reference to the currently open module.
209 pub fn get_mut(&mut self) -> &mut Module<K, V> {
210 match self.open.iter_mut().last() {
211 None => &mut self.root,
212 Some((_name, module)) => module,
213 }
214 }
215
216 /// Return the path of the currently open module.
217 pub fn get_path(&self) -> impl Iterator<Item = &K> {
218 self.open.iter().map(|(key, _)| key)
219 }
220}
221
222impl<K: Ord, V> From<Context<K, V>> for Module<K, V> {
223 fn from(mut ctx: Context<K, V>) -> Self {
224 while ctx.close() {}
225 ctx.root
226 }
227}