caco 0.5.33

library for dealing with doom [et al] wad files
Documentation
use crate::agnostic::Clump;

use itertools::Itertools;

#[cfg(feature = "regex")]
use regex::Regex;

/// A specialised version of the [`Clod`](crate::agnostic::Clod) trait, for formats where every
/// entry is guaranteed to be a [`Clump`] of data, and subdirectories are not possible.
/// 
/// The Doom WAD format is one of these.
pub trait FlatClod<Q> where Q: Clump {
    /// Get the amount of clumps.
    fn clump_amt(&self) -> usize;

    /// Borrows a single clump by number.
    fn nth_clump(&self, n: usize) -> &Q;

    /// Borrows many clumps by numbers. The range may be non-contiguous. The returned clumps are always
    /// in numerically increasing order, and are not duplicated, regardless of the ordering or
    /// overlapping of the provided range(s).
    fn a_sub_nth_clumps(&self, a: impl Iterator<Item = usize>) -> Vec<&Q>;

    // -------------------------------------------------------------------------------------------------------
    // PROVIDED:

    /// Borrows every clump.
    fn all_clumps(&self) -> Vec<&Q> {
        self.a_sub_nth_clumps(0..self.clump_amt())
    }

    /// Indices of clumps with the specified name.
    fn which_clumps(&self, s: impl AsRef<str>) -> Vec<usize> {
        self.all_clumps().into_iter()
            .enumerate()
            .filter(|(_k, l)| l.name() == s.as_ref())
            .map(|(k, _l)| k)
            .collect()
    }

    /// Get every clump with the specified name. The clumps will be in numerically increasing order
    /// by their index in the wad.
    fn clumps_with_name(&self, s: impl AsRef<str>) -> Vec<&Q> {
        self.a_sub_nth_clumps(self.which_clumps(s).into_iter())
    }

    /// Get a clump by it's specific name. If multiple clumps with the name exist, returns the first one.
    /// If no clumps with the name exist, returns [`None`].
    fn clump_by_name(&self, s: impl AsRef<str>) -> Option<&Q> {
        // todo: can it be done with a one-liner? (i.e. no `let`?)
        let v = self.clumps_with_name(s);
        if v.len() == 0 { None } else { Some(v[0]) }
    }

    /// Indices of clumps whose names match the given regex.
    #[cfg(feature = "regex")]
    fn which_clumps_re(&self, r: Regex) -> Vec<usize> {
        self.all_clumps().into_iter()
            .enumerate()
            .filter(|(_k, l)| r.is_match(l.name()))
            .map(|(k, _l)| k)
            .collect()
    }

    /// Get every clump with a name matching the regex. The clumps will be in numerically increasing order
    /// by their index in the wad.
    #[cfg(feature = "regex")]
    fn clumps_with_name_re(&self, r: Regex) -> Vec<&Q> {
        self.a_sub_nth_clumps(self.which_clumps_re(r).into_iter())
    }

    /// Get a clump, matching with a specific regex. If multiple matching clumps exist, returns the first one.
    /// If no clumps matching the pattern exist, returns [`None`].
    #[cfg(feature = "regex")]
    fn clump_by_name_re(&self, r: Regex) -> Option<&Q> {
        // todo: can it be done with a one-liner? (i.e. no `let`?)
        let v = self.clumps_with_name_re(r);
        if v.len() == 0 { None } else { Some(v[0]) }
    }

    /// Returns the indices of clumps between the given markers, sorted numerically.
    fn indices_between_markers(&self, start: impl AsRef<str>, end: impl AsRef<str>) -> Vec<usize> {
        let start_marks = self.which_clumps(start);
        let end_marks = self.which_clumps(end);

        #[derive(Clone, Copy, PartialEq, Eq)]
        enum MarkType {
            Start, End
        }

        let mut u = vec!();

        for (a, b) in start_marks.into_iter().map(|n| (n, MarkType::Start))
        .chain(end_marks.into_iter().map(|n| (n, MarkType::End)))
        .sorted_by(|(n, _), (k, _)| n.cmp(&k))
        .tuple_windows() {
            if b.1 == MarkType::End && a.1 == MarkType::Start {
                u.append(&mut ((a.0 + 1)..b.0).collect());
            }
        }

        u
    }

    /// Borrows all clumps between the given markers, sorted numerically by index.
    fn clumps_between_markers(&self, start: impl AsRef<str>, end: impl AsRef<str>) -> Vec<&Q> {
        self.a_sub_nth_clumps(self.indices_between_markers(start, end).iter().map(|n| *n))
    }
}