1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! # [`depmap`](crate) - Dependency map manipulation
//!
//! Dependency maps are useful for any application that works with stuff that depends on other
//! stuff.
//!
//! Cyclic dependencies are found and handled.

/// An error type.
pub enum Error<T, E> {
    /// A cyclic dependency error.
    CyclicDep(Vec<T>),
    /// A user-defined error.
    UserDef(E),
}

impl<T, E> From<E> for Error<T, E> {
    fn from(err: E) -> Self {
        Error::UserDef(err)
    }
}

/// The dependency map.
pub struct DepMap<T: PartialEq> {
    /// A list of lists of things that need to be worked on at the same level.
    /// The first of each list is 'active'; the others will be handled in reverse order.
    /// The last few lists might be empty, called free lists.
    list: Vec<Vec<T>>,
    /// The result list.
    result: Vec<T>,
    /// The number of used lists.
    used: usize,
}

impl<T: PartialEq> DepMap<T> {
    /// Creates a new [`DepMap`] from an initial list.
    pub fn new(list: Vec<T>) -> Self {
        Self {
            used: if list.is_empty() {0} else {1},
            list: vec![list],
            result: Vec::new(),
        }
    }

    /// Runs through a whole dependency map using a single producer function.
    ///
    /// This is probably what one should use.
    pub fn process<F, I, E>(initial: Vec<T>, mut f: F) -> Result<Vec<T>, Error<T, E>>
    where F: FnMut(&T) -> I, I: Iterator<Item = Result<T, E>> {
        // The current map.
        let mut state = Self::new(initial);
        loop {
            match state.destroy() {
                Ok(res) => break Ok(res),
                Err(map) => state = map,
            };

            // Not empty; Process
            state.add(&mut f)?
                .map(|deps| deps.len())
                .map_or(Ok(()), |len| Err(state.list.iter_mut()
                    .take(state.used)
                    .skip(state.used - len)
                    .map(|list| list.swap_remove(0))
                    .collect::<Vec<_>>()))
                .map_err(Error::CyclicDep)?;
        }
    }

    /// Whether the map is empty (i.e nothing needs to be worked on).
    pub fn is_empty(&self) -> bool {
        self.used == 0
    }

    /// Returns the result list if the dependency map is empty.
    ///
    /// If it is not empty, then an error is returned with the whole map.
    pub fn destroy(self) -> Result<Vec<T>, Self> {
        if self.is_empty() {
            Ok(self.result)
        } else {
            Err(self)
        }
    }

    /// Adds the latest target's dependencies at the end, removing those already done and
    /// returning cyclic dependency errors (if any).
    ///
    /// When cyclic dependency errors occur, the target is retained but its dependencies are not.
    /// Skips everything if the depmap is empty.
    pub fn add<F, I, E>(&mut self, f: F) -> Result<Option<Vec<&T>>, E>
    where F: FnOnce(&T) -> I, I: Iterator<Item = Result<T, E>> {
        if self.is_empty() {
            return Ok(None);
        }

        // Get a free list.
        let mut free = self.get_free();
        // Add to it the new targets.
        for tgt in (f)(&self.list[self.used - 1][0]) {
            let tgt = tgt?;
            if self.result.iter().any(|done| done == &tgt) {
                // Found in result list; already done, skip
                continue;
            } else if let Some(pos) = self.list[0..self.used].iter()
                    .map(|list| &list[0]).position(|cur| cur == &tgt) {
                // Found in active target list; cyclic dependency, fail
                free.clear();
                self.list.push(free);
                return Ok(Some(self.list[pos..self.used].iter().map(|list| &list[0]).collect()))
            } else {
                // No issues; unhandled, add to list
                free.push(tgt)
            }
        }
        // If the list is empty, then the target is a node; drop active targets.
        // Otherwise, add the list to the used space.
        if free.is_empty() {
            self.drop_cur();
        } else {
            // Add the free length to the used space.
            let len = self.list.len();
            self.list.push(free);
            self.list.swap(len, self.used);
            self.used += 1;
        }
        Ok(None)
    }

    /// Returns a free list.
    fn get_free(&mut self) -> Vec<T> {
        if self.used < self.list.len() {
            // Some free lengths exist; Pop one off.
            self.list.pop().unwrap()
        } else {
            // No free lengths exist; Just make a new list.
            Vec::new()
        }
    }

    /// Drops as many active targets as possible, beginning from the end.
    fn drop_cur(&mut self) {
        // While used lengths exist:
        while self.used > 0 {
            // Get the latest used list.
            let list = &mut self.list[self.used - 1];
            // Drop the active target into the result list.
            self.result.push(list.swap_remove(0));
            // While the list isn't empty, search for a target that has not been handled yet.
            let found = loop {
                if list.is_empty() {
                    break false
                }

                let tgt = &list[0];

                // In result list: Already handled, remove and continue
                // Otherwise: found unhandled, stop
                if self.result.iter().any(|done| done == tgt) {
                    list.swap_remove(0);
                } else {
                    break true
                }
            };
            // If found: Stop.
            // Otherwise: Mark as free (now empty) and move on.
            if found {
                break
            } else {
                self.used -= 1;
            }
        }
    }
}