use std::fmt;

use super::{entries, Dir, Entries, Query, VPath, VPathBuf};

pub struct Cd<T> {
    parent: T,
    path: VPathBuf,
}

impl<T> Cd<T> {
    pub fn new(parent: T, path: VPathBuf) -> Self {
        Self { parent, path }
    }
}

impl<T> Cd<T>
where
    T: Dir,
{
    fn dir(&self, path: &VPath) -> Vec<VPathBuf> {
        entries(&self.parent, &self.path.join(path))
            .into_iter()
            .map(|entry| {
                entry
                    .strip_prefix(&self.path)
                    .expect("all entries must be anchored within `self.path`")
                    .to_owned()
            })
            .collect()
    }
}

impl<T> Dir for Cd<T>
where
    T: Dir,
{
    fn query(&self, path: &VPath, query: &mut Query) {
        // The only query that meaningfully needs to return something else is `dir`, which must
        // be modified to strip prefixes off of the parent's returned paths.
        query.provide(|| Entries(self.dir(path)));

        // Other queries can run unmodified, only passing them the right path.
        self.parent.query(&self.path.join(path), query);
    }
}

impl<T> fmt::Debug for Cd<T>
where
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}/{:?}", self.parent, self.path)
    }
}
