wasmtime_environ/component/
names.rs1use crate::collections::TryCow;
2use crate::error::{Result, bail};
3use crate::{Atom, StringPool, prelude::*};
4use alloc::sync::Arc;
5use core::borrow::Borrow;
6use core::hash::Hash;
7use semver::Version;
8use serde_derive::{Deserialize, Serialize};
9
10#[derive(Serialize, Deserialize, Debug)]
19pub struct NameMap<K, V>
20where
21 K: TryClone + Hash + Eq + Ord,
22{
23 definitions: TryIndexMap<K, V>,
31
32 alternate_lookups: TryIndexMap<K, (K, TryVersion)>,
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
57struct TryVersion(Arc<Version>);
58
59impl TryFrom<Version> for TryVersion {
60 type Error = OutOfMemory;
61
62 fn try_from(value: Version) -> Result<Self, Self::Error> {
63 Ok(Self(try_new::<Arc<_>>(value)?))
64 }
65}
66
67impl TryClone for TryVersion {
68 #[inline]
69 fn try_clone(&self) -> Result<Self, OutOfMemory> {
70 Ok(Self(self.0.clone()))
71 }
72}
73
74impl core::ops::Deref for TryVersion {
75 type Target = Version;
76
77 #[inline]
78 fn deref(&self) -> &Self::Target {
79 &self.0
80 }
81}
82
83impl Borrow<Version> for TryVersion {
84 #[inline]
85 fn borrow(&self) -> &Version {
86 &self.0
87 }
88}
89
90impl serde::Serialize for TryVersion {
91 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92 where
93 S: serde::Serializer,
94 {
95 self.0.serialize(serializer)
96 }
97}
98
99impl<'de> serde::Deserialize<'de> for TryVersion {
100 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101 where
102 D: serde::Deserializer<'de>,
103 {
104 use serde::de::Error;
105 let v = Version::deserialize(deserializer)?;
106 let v = try_new::<Arc<_>>(v).map_err(|oom| D::Error::custom(oom))?;
107 Ok(Self(v))
108 }
109}
110
111impl<K, V> TryClone for NameMap<K, V>
112where
113 K: TryClone + Hash + Eq + Ord,
114 V: TryClone,
115{
116 fn try_clone(&self) -> Result<Self, OutOfMemory> {
117 Ok(Self {
118 definitions: self.definitions.try_clone()?,
119 alternate_lookups: self.alternate_lookups.try_clone()?,
120 })
121 }
122}
123
124impl<K, V> NameMap<K, V>
125where
126 K: TryClone + Hash + Eq + Ord,
127{
128 pub fn insert<I>(&mut self, name: &str, cx: &mut I, allow_shadowing: bool, item: V) -> Result<K>
140 where
141 I: NameMapIntern<Key = K>,
142 I::BorrowedKey: Hash + Eq,
143 {
144 let key = cx.intern(name)?;
146 if !allow_shadowing && self.definitions.contains_key(&key) {
147 bail!("map entry `{name}` defined twice")
148 }
149 self.definitions.insert(key.try_to_owned()?, item)?;
150
151 if let Some((alternate_key, version)) = alternate_lookup_key(name) {
157 let alternate_key = cx.intern(alternate_key)?;
158 let version = TryVersion::try_from(version)?;
159 if let Some((prev_key, prev_version)) = self.alternate_lookups.insert(
160 alternate_key.try_clone()?,
161 (key.try_clone()?, version.clone()),
162 )? {
163 if version < prev_version {
166 self.alternate_lookups
167 .insert(alternate_key, (prev_key, prev_version))?;
168 }
169 }
170 }
171 Ok(key)
172 }
173
174 pub fn get<I>(&self, name: &str, cx: &I) -> Option<&V>
181 where
182 I: NameMapIntern<Key = K>,
183 I::Key: Borrow<I::BorrowedKey>,
184 I::BorrowedKey: Hash + Eq,
185 {
186 let candidate = cx.lookup(name).and_then(|k| self.definitions.get(&*k));
190 if let Some(def) = candidate {
191 return Some(def);
192 }
193
194 let (alternate_name, _version) = alternate_lookup_key(name)?;
200 let alternate_key = cx.lookup(alternate_name)?;
201 let (exact_key, _version) = self.alternate_lookups.get(&alternate_key)?;
202 self.definitions.get(exact_key.borrow())
203 }
204
205 pub fn raw_iter(&self) -> impl Iterator<Item = (&K, &V)> {
211 self.definitions.iter()
212 }
213
214 pub fn raw_get_mut(&mut self, key: &K) -> Option<&mut V> {
216 self.definitions.get_mut(key)
217 }
218}
219
220impl<K, V> Default for NameMap<K, V>
221where
222 K: TryClone + Hash + Eq + Ord,
223{
224 fn default() -> NameMap<K, V> {
225 NameMap {
226 definitions: Default::default(),
227 alternate_lookups: Default::default(),
228 }
229 }
230}
231
232pub trait NameMapIntern {
235 type Key: Borrow<Self::BorrowedKey>;
237
238 type BorrowedKey: ?Sized + TryToOwned<Owned = Self::Key>;
240
241 fn intern(&mut self, s: &str) -> Result<Self::Key, OutOfMemory>;
243
244 fn lookup<'a>(&'a self, s: &'a str) -> Option<TryCow<'a, Self::BorrowedKey>>;
247}
248
249pub struct NameMapNoIntern;
252
253impl NameMapIntern for NameMapNoIntern {
254 type Key = TryString;
255 type BorrowedKey = str;
256
257 fn intern(&mut self, s: &str) -> Result<Self::Key, OutOfMemory> {
258 TryString::try_from(s)
259 }
260
261 fn lookup<'a>(&'a self, s: &'a str) -> Option<TryCow<'a, Self::BorrowedKey>> {
262 Some(TryCow::Borrowed(s))
263 }
264}
265
266impl NameMapIntern for StringPool {
267 type Key = Atom;
268 type BorrowedKey = Atom;
269
270 fn intern(&mut self, string: &str) -> Result<Atom, OutOfMemory> {
271 self.insert(string)
272 }
273
274 fn lookup(&self, string: &str) -> Option<TryCow<'_, Atom>> {
275 self.get_atom(string).map(TryCow::Owned)
276 }
277}
278
279fn alternate_lookup_key(name: &str) -> Option<(&str, Version)> {
294 let at = name.find('@')?;
295 let version_string = &name[at + 1..];
296 let version = Version::parse(version_string).ok()?;
297 if !version.pre.is_empty() {
298 None
301 } else if version.major != 0 {
302 let first_dot = version_string.find('.')? + at + 1;
305 Some((&name[..first_dot], version))
306 } else if version.minor != 0 {
307 let first_dot = version_string.find('.')? + at + 1;
310 let second_dot = name[first_dot + 1..].find('.')? + first_dot + 1;
311 Some((&name[..second_dot], version))
312 } else {
313 None
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::{NameMap, NameMapNoIntern};
323
324 #[test]
325 fn alternate_lookup_key() {
326 fn alt(s: &str) -> Option<&str> {
327 super::alternate_lookup_key(s).map(|(s, _)| s)
328 }
329
330 assert_eq!(alt("x"), None);
331 assert_eq!(alt("x:y/z"), None);
332 assert_eq!(alt("x:y/z@1.0.0"), Some("x:y/z@1"));
333 assert_eq!(alt("x:y/z@1.1.0"), Some("x:y/z@1"));
334 assert_eq!(alt("x:y/z@1.1.2"), Some("x:y/z@1"));
335 assert_eq!(alt("x:y/z@2.1.2"), Some("x:y/z@2"));
336 assert_eq!(alt("x:y/z@2.1.2+abc"), Some("x:y/z@2"));
337 assert_eq!(alt("x:y/z@0.1.2"), Some("x:y/z@0.1"));
338 assert_eq!(alt("x:y/z@0.1.3"), Some("x:y/z@0.1"));
339 assert_eq!(alt("x:y/z@0.2.3"), Some("x:y/z@0.2"));
340 assert_eq!(alt("x:y/z@0.2.3+abc"), Some("x:y/z@0.2"));
341 assert_eq!(alt("x:y/z@0.0.1"), None);
342 assert_eq!(alt("x:y/z@0.0.1-pre"), None);
343 assert_eq!(alt("x:y/z@0.1.0-pre"), None);
344 assert_eq!(alt("x:y/z@1.0.0-pre"), None);
345 }
346
347 #[test]
348 fn name_map_smoke() {
349 let mut map = NameMap::default();
350 let mut intern = NameMapNoIntern;
351
352 map.insert("a", &mut intern, false, 0).unwrap();
353 map.insert("b", &mut intern, false, 1).unwrap();
354
355 assert!(map.insert("a", &mut intern, false, 0).is_err());
356 assert!(map.insert("a", &mut intern, true, 0).is_ok());
357
358 assert_eq!(map.get("a", &intern), Some(&0));
359 assert_eq!(map.get("b", &intern), Some(&1));
360 assert_eq!(map.get("c", &intern), None);
361
362 map.insert("a:b/c@1.0.0", &mut intern, false, 2).unwrap();
363 map.insert("a:b/c@1.0.1", &mut intern, false, 3).unwrap();
364 assert_eq!(map.get("a:b/c@1.0.0", &intern), Some(&2));
365 assert_eq!(map.get("a:b/c@1.0.1", &intern), Some(&3));
366 assert_eq!(map.get("a:b/c@1.0.2", &intern), Some(&3));
367 assert_eq!(map.get("a:b/c@1.1.0", &intern), Some(&3));
368 }
369}