Skip to main content

nwnrs_resman/
manager.rs

1use std::{collections::HashSet, fmt, sync::Arc};
2
3use nwnrs_lru::prelude::*;
4use nwnrs_resref::prelude::*;
5use tracing::instrument;
6
7use crate::prelude::*;
8
9/// Layered resource manager.
10///
11/// Containers are searched from front to back, so newly added containers take
12/// precedence over earlier ones. An optional weighted LRU cache can memoize
13/// recent lookups.
14pub struct ResMan {
15    containers: Vec<Arc<dyn ResContainer>>,
16    cache:      Option<WeightedLru<ResRef, Res>>,
17}
18
19impl fmt::Debug for ResMan {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        f.debug_struct("ResMan")
22            .field("container_count", &self.containers.len())
23            .field("has_cache", &self.cache.is_some())
24            .finish()
25    }
26}
27
28impl ResMan {
29    /// Creates an empty resource manager.
30    ///
31    /// `cache_size_mb` controls the optional lookup cache size in megabytes. A
32    /// value of `0` disables the manager-level cache. Newly added containers
33    /// will be searched before earlier ones.
34    #[must_use]
35    pub fn new(cache_size_mb: usize) -> Self {
36        Self {
37            containers: Vec::new(),
38            cache:      (cache_size_mb > 0)
39                .then(|| WeightedLru::new(cache_size_mb * 1024 * 1024, 1)),
40        }
41    }
42
43    /// Returns whether any container can resolve `rr`.
44    ///
45    /// When [`CachePolicy::Use`] is selected, the manager cache is checked
46    /// first.
47    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("contains",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(47u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("resref")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("resref");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("cache_policy")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("cache_policy");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&rr)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&cache_policy)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: bool = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if cache_policy.uses_cache() &&
                    self.cache.as_mut().is_some_and(|cache|
                            cache.contains_key(rr)) {
                return true;
            }
            self.containers.iter().any(|container| container.contains(rr))
        }
    }
}#[instrument(level = "debug", skip_all, fields(resref = %rr, cache_policy = ?cache_policy))]
48    pub fn contains(&mut self, rr: &ResRef, cache_policy: CachePolicy) -> bool {
49        if cache_policy.uses_cache()
50            && self
51                .cache
52                .as_mut()
53                .is_some_and(|cache| cache.contains_key(rr))
54        {
55            return true;
56        }
57
58        self.containers
59            .iter()
60            .any(|container| container.contains(rr))
61    }
62
63    /// Resolves `rr` to the highest-precedence matching resource.
64    ///
65    /// When [`CachePolicy::Use`] is selected, successful lookups are memoized
66    /// in the manager cache.
67    ///
68    /// # Errors
69    ///
70    /// Returns [`ResManError`] if no container provides the requested resource
71    /// or the demand fails.
72    #[allow(clippy :: redundant_closure_call)]
match (move ||
                {

                    #[allow(unknown_lints, unreachable_code, clippy ::
                    diverging_sub_expression, clippy :: empty_loop, clippy ::
                    let_unit_value, clippy :: let_with_type_underscore, clippy
                    :: needless_return, clippy :: unreachable)]
                    if false {
                        let __tracing_attr_fake_return: ResManResult<Res> = loop {};
                        return __tracing_attr_fake_return;
                    }
                    {
                        if cache_policy.uses_cache() &&
                                let Some(cached) =
                                    self.cache.as_mut().and_then(|cache| cache.get(rr).cloned())
                            {
                            return Ok(cached);
                        }
                        for container in &self.containers {
                            if container.contains(rr) {
                                let result = container.demand(rr)?;
                                if cache_policy.uses_cache() &&
                                        let Some(cache) = self.cache.as_mut() {
                                    let weight =
                                        usize::try_from(result.io_size().max(1)).unwrap_or(usize::MAX);
                                    cache.insert_weighted(rr.clone(), weight, result.clone());
                                }
                                return Ok(result);
                            }
                        }
                        Err(ResManError::msg(::alloc::__export::must_use({
                                        ::alloc::fmt::format(format_args!("not found: {0}", rr))
                                    })))
                    }
                })()
    {
        #[allow(clippy :: unit_arg)]
        Ok(x) => Ok(x),
    Err(e) => {
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("event src/manager.rs:72",
                                "nwnrs_resman::manager", ::tracing::Level::ERROR,
                                ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                ::tracing_core::__macro_support::Option::Some(72u32),
                                ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("error")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("error");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::EVENT)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let enabled =
                ::tracing::Level::ERROR <=
                            ::tracing::level_filters::STATIC_MAX_LEVEL &&
                        ::tracing::Level::ERROR <=
                            ::tracing::level_filters::LevelFilter::current() &&
                    {
                        let interest = __CALLSITE.interest();
                        !interest.is_never() &&
                            ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                interest)
                    };
            if enabled {
                (|value_set: ::tracing::field::ValueSet|
                            {
                                let meta = __CALLSITE.metadata();
                                ::tracing::Event::dispatch(meta, &value_set);
                                ;
                            })({
                        #[allow(unused_imports)]
                        use ::tracing::field::{debug, display, Value};
                        __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&e)
                                                    as &dyn ::tracing::field::Value))])
                    });
            } else { ; }
        };
        Err(e)
    }
}#[instrument(level = "debug", skip_all, err, fields(resref = %rr, cache_policy = ?cache_policy))]
73    pub fn demand(&mut self, rr: &ResRef, cache_policy: CachePolicy) -> ResManResult<Res> {
74        if cache_policy.uses_cache()
75            && let Some(cached) = self.cache.as_mut().and_then(|cache| cache.get(rr).cloned())
76        {
77            return Ok(cached);
78        }
79
80        for container in &self.containers {
81            if container.contains(rr) {
82                let result = container.demand(rr)?;
83                if cache_policy.uses_cache()
84                    && let Some(cache) = self.cache.as_mut()
85                {
86                    let weight = usize::try_from(result.io_size().max(1)).unwrap_or(usize::MAX);
87                    cache.insert_weighted(rr.clone(), weight, result.clone());
88                }
89                return Ok(result);
90            }
91        }
92
93        Err(ResManError::msg(format!("not found: {rr}")))
94    }
95
96    /// Returns the union of resource references exposed by all containers.
97    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("contents",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(97u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("self")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("self");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("container_count")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("container_count");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&self)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&self.containers.len()
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: HashSet<ResRef> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let mut result = HashSet::new();
            for container in &self.containers {
                result.extend(container.contents());
            }
            result
        }
    }
}#[instrument(level = "debug", fields(container_count = self.containers.len()))]
98    pub fn contents(&self) -> HashSet<ResRef> {
99        let mut result = HashSet::new();
100        for container in &self.containers {
101            result.extend(container.contents());
102        }
103        result
104    }
105
106    /// Resolves a fully specified `name.ext` resource reference.
107    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("get_resolved",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(107u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("resref")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("resref");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&rr)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Option<Res> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let base = rr.base().clone();
            self.contains(&base,
                        CachePolicy::Use).then(||
                        self.demand(&base, CachePolicy::Use).ok()).flatten()
        }
    }
}#[instrument(level = "debug", skip_all, fields(resref = %rr))]
108    pub fn get_resolved(&mut self, rr: &ResolvedResRef) -> Option<Res> {
109        let base = rr.base().clone();
110        self.contains(&base, CachePolicy::Use)
111            .then(|| self.demand(&base, CachePolicy::Use).ok())
112            .flatten()
113    }
114
115    /// Resolves `rr`, returning `None` instead of an error when absent.
116    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("get",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(116u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("resref")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("resref");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&rr)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Option<Res> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            self.contains(rr,
                        CachePolicy::Use).then(||
                        self.demand(rr, CachePolicy::Use).ok()).flatten()
        }
    }
}#[instrument(level = "debug", skip_all, fields(resref = %rr))]
117    pub fn get(&mut self, rr: &ResRef) -> Option<Res> {
118        self.contains(rr, CachePolicy::Use)
119            .then(|| self.demand(rr, CachePolicy::Use).ok())
120            .flatten()
121    }
122
123    /// Adds `container` at the front of the search order.
124    ///
125    /// This means the most recently added container has the highest precedence.
126    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("add",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(126u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set_all(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        { self.containers.insert(0, container); }
    }
}#[instrument(level = "debug", skip_all)]
127    pub fn add(&mut self, container: Arc<dyn ResContainer>) {
128        self.containers.insert(0, container);
129    }
130
131    /// Returns the current container search order.
132    #[must_use]
133    pub fn containers(&self) -> &[Arc<dyn ResContainer>] {
134        &self.containers
135    }
136
137    /// Removes the exact container instance when present.
138    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("remove",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(138u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set_all(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: bool = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if let Some(index) =
                    self.containers.iter().position(|candidate|
                            Arc::ptr_eq(candidate, container)) {
                self.containers.remove(index);
                true
            } else { false }
        }
    }
}#[instrument(level = "debug", skip_all)]
139    pub fn remove(&mut self, container: &Arc<dyn ResContainer>) -> bool {
140        if let Some(index) = self
141            .containers
142            .iter()
143            .position(|candidate| Arc::ptr_eq(candidate, container))
144        {
145            self.containers.remove(index);
146            true
147        } else {
148            false
149        }
150    }
151
152    /// Removes the container at `index`.
153    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("remove_at",
                                    "nwnrs_resman::manager", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("src/manager.rs"),
                                    ::tracing_core::__macro_support::Option::Some(153u32),
                                    ::tracing_core::__macro_support::Option::Some("nwnrs_resman::manager"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("self")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("self");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("index")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("index");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&self)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::Empty
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: Option<Arc<dyn ResContainer>> =
                loop {};
            return __tracing_attr_fake_return;
        }
        {
            (index <
                        self.containers.len()).then(||
                    self.containers.remove(index))
        }
    }
}#[instrument(level = "debug", fields(index))]
154    pub fn remove_at(&mut self, index: usize) -> Option<Arc<dyn ResContainer>> {
155        (index < self.containers.len()).then(|| self.containers.remove(index))
156    }
157
158    /// Returns the manager-level cache when caching is enabled.
159    pub fn cache(&mut self) -> Option<&mut WeightedLru<ResRef, Res>> {
160        self.cache.as_mut()
161    }
162}
163
164#[allow(clippy::panic)]
165#[cfg(test)]
166mod tests {
167    use std::{collections::HashMap, io::Cursor, sync::Arc, time::SystemTime};
168
169    use nwnrs_checksums::EMPTY_SECURE_HASH;
170    use nwnrs_exo::ExoResFileCompressionType;
171    use nwnrs_resref::{ResRef, ResolvedResRef};
172    use nwnrs_restype::ResType;
173
174    use crate::{
175        CachePolicy, Res, ResContainer, ResMan, ResManError, ResManResult, new_res_origin,
176        shared_stream,
177    };
178
179    #[derive(Clone)]
180    struct TestContainer {
181        label:   &'static str,
182        entries: HashMap<nwnrs_resref::ResRef, Res>,
183    }
184
185    impl std::fmt::Display for TestContainer {
186        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187            f.write_str(self.label)
188        }
189    }
190
191    impl ResContainer for TestContainer {
192        fn contains(&self, rr: &nwnrs_resref::ResRef) -> bool {
193            self.entries.contains_key(rr)
194        }
195
196        fn demand(&self, rr: &nwnrs_resref::ResRef) -> ResManResult<Res> {
197            self.entries
198                .get(rr)
199                .cloned()
200                .ok_or_else(|| ResManError::msg(format!("not found: {rr}")))
201        }
202
203        fn count(&self) -> usize {
204            self.entries.len()
205        }
206
207        fn contents(&self) -> Vec<nwnrs_resref::ResRef> {
208            self.entries.keys().cloned().collect()
209        }
210    }
211
212    fn make_res(name: &str, ty: u16, bytes: &[u8], label: &str) -> Res {
213        let rr = ResRef::new(name, ResType(ty)).unwrap_or_else(|error| {
214            panic!("make rr: {error}");
215        });
216        Res::new_with_stream(
217            new_res_origin("TestContainer", label),
218            rr,
219            SystemTime::UNIX_EPOCH,
220            shared_stream(Cursor::new(bytes.to_vec())),
221            bytes.len() as i64,
222            0,
223            ExoResFileCompressionType::None,
224            None,
225            bytes.len(),
226            EMPTY_SECURE_HASH,
227        )
228    }
229
230    #[test]
231    fn resolves_latest_container_first_and_unions_contents() {
232        let shared = ResRef::new("shared", ResType(2027)).unwrap_or_else(|error| {
233            panic!("shared rr: {error}");
234        });
235        let older = TestContainer {
236            label:   "older",
237            entries: HashMap::from([
238                (shared.clone(), make_res("shared", 2027, b"older", "older")),
239                (
240                    ResRef::new("only_old", ResType(2027)).unwrap_or_else(|error| {
241                        panic!("only_old rr: {error}");
242                    }),
243                    make_res("only_old", 2027, b"old", "older"),
244                ),
245            ]),
246        };
247        let newer = TestContainer {
248            label:   "newer",
249            entries: HashMap::from([(shared.clone(), make_res("shared", 2027, b"newer", "newer"))]),
250        };
251
252        let mut manager = ResMan::new(1);
253        manager.add(Arc::new(older));
254        manager.add(Arc::new(newer));
255
256        let res = match manager.demand(&shared, CachePolicy::Bypass) {
257            Ok(value) => value,
258            Err(error) => panic!("demand shared: {error}"),
259        };
260        let bytes = match res.read_all(CachePolicy::Bypass) {
261            Ok(value) => value,
262            Err(error) => panic!("read shared bytes: {error}"),
263        };
264        assert_eq!(bytes, b"newer".to_vec());
265        assert_eq!(manager.contents().len(), 2);
266    }
267
268    #[test]
269    fn resolves_fully_specified_references() {
270        let rr = ResRef::new("alpha", ResType(2027)).unwrap_or_else(|error| {
271            panic!("alpha rr: {error}");
272        });
273        let container = TestContainer {
274            label:   "single",
275            entries: HashMap::from([(rr.clone(), make_res("alpha", 2027, b"alpha", "single"))]),
276        };
277        let mut manager = ResMan::new(1);
278        manager.add(Arc::new(container));
279
280        let resolved = ResolvedResRef::from_filename("alpha.utc").unwrap_or_else(|error| {
281            panic!("resolved rr: {error}");
282        });
283        assert!(manager.get_resolved(&resolved).is_some());
284    }
285}