// Copyright 2018-2025 the Deno authors. MIT license. use std::collections::BTreeMap; use url::Url; #[allow(clippy::disallowed_types)] type UrlRc = deno_maybe_sync::MaybeArc; /// A map that stores values scoped to a specific directory /// on the file system. pub struct FolderScopedMap { scoped: BTreeMap, } impl std::fmt::Debug for FolderScopedMap where TValue: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FolderScopedMap") .field("scoped", &self.scoped) .finish() } } impl Default for FolderScopedMap { fn default() -> Self { Self { scoped: Default::default(), } } } impl FolderScopedMap { pub fn from_map(map: BTreeMap) -> Self { Self { scoped: map } } pub fn count(&self) -> usize { self.scoped.len() } pub fn get_for_specifier(&self, specifier: &Url) -> Option<&TValue> { let specifier_str = specifier.as_str(); self .scoped .iter() .rfind(|(s, _)| specifier_str.starts_with(s.as_str())) .map(|(_, v)| v) } pub fn entry_for_specifier( &self, specifier: &Url, ) -> Option<(&UrlRc, &TValue)> { self .scoped .iter() .rfind(|(s, _)| specifier.as_str().starts_with(s.as_str())) } pub fn entries_for_specifier<'a>( &'a self, specifier: &Url, ) -> impl Iterator { struct ValueIter< 'a, 'b, TValue: 'a, Iter: Iterator, > { previously_found_dir: bool, iter: Iter, specifier: &'b Url, } impl<'a, TValue, Iter: Iterator> Iterator for ValueIter<'a, '_, TValue, Iter> { type Item = (&'a UrlRc, &'a TValue); fn next(&mut self) -> Option { for (dir_url, value) in self.iter.by_ref() { if !self.specifier.as_str().starts_with(dir_url.as_str()) { if self.previously_found_dir { break; } else { continue; } } self.previously_found_dir = true; return Some((dir_url, value)); } None } } ValueIter { previously_found_dir: false, iter: self.scoped.iter().rev(), specifier, } } pub fn get_for_scope(&self, scope: &Url) -> Option<&TValue> { self.scoped.get(scope) } pub fn entries(&self) -> impl Iterator { self.scoped.iter() } pub fn values(&self) -> impl Iterator { self.scoped.values() } pub fn insert(&mut self, dir_url: UrlRc, value: TValue) { debug_assert!(dir_url.path().ends_with("/")); // must be a dir url debug_assert_eq!(dir_url.scheme(), "file"); self.scoped.insert(dir_url, value); } pub fn try_map( &self, mut f: impl FnMut(&TValue) -> Result, ) -> Result, E> { Ok(FolderScopedMap { scoped: self .scoped .iter() .map(|(s, v)| Ok((s.clone(), f(v)?))) .collect::>()?, }) } } /// A map that stores values scoped to a specific directory /// on the file system, but also having the concept of "unscoped" /// for any folders that land outside. /// /// The root directory is considered "unscoped" so values that /// fall outside the other directories land here (ex. remote modules). pub struct FolderScopedWithUnscopedMap { pub unscoped: TValue, scoped: FolderScopedMap, } impl std::fmt::Debug for FolderScopedWithUnscopedMap where TValue: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FolderScopedWithUnscopedMap") .field("unscoped", &self.unscoped) .field("scoped", &self.scoped) .finish() } } impl Default for FolderScopedWithUnscopedMap where TValue: Default, { fn default() -> Self { Self::new(Default::default()) } } impl FolderScopedWithUnscopedMap { pub fn new(unscoped: TValue) -> Self { Self { unscoped, scoped: Default::default(), } } pub fn count(&self) -> usize { // +1 for unscoped self.scoped.count() + 1 } pub fn get_for_specifier(&self, specifier: &Url) -> &TValue { self .scoped .get_for_specifier(specifier) .unwrap_or(&self.unscoped) } pub fn entry_for_specifier( &self, specifier: &Url, ) -> (Option<&UrlRc>, &TValue) { self .scoped .entry_for_specifier(specifier) .map(|(s, v)| (Some(s), v)) .unwrap_or((None, &self.unscoped)) } pub fn get_for_scope(&self, scope: Option<&Url>) -> Option<&TValue> { let Some(scope) = scope else { return Some(&self.unscoped); }; self.scoped.get_for_scope(scope) } pub fn entries(&self) -> impl Iterator, &TValue)> { [(None, &self.unscoped)] .into_iter() .chain(self.scoped.entries().map(|(s, v)| (Some(s), v))) } pub fn insert(&mut self, dir_url: UrlRc, value: TValue) { debug_assert!(dir_url.path().ends_with("/")); // must be a dir url debug_assert_eq!(dir_url.scheme(), "file"); self.scoped.insert(dir_url, value); } pub fn try_map( &self, mut f: impl FnMut(&TValue) -> Result, ) -> Result, E> { Ok(FolderScopedWithUnscopedMap { unscoped: f(&self.unscoped)?, scoped: self.scoped.try_map(f)?, }) } }