Build the ancestor chain for a view, ordered root → leaf. Walks `parent_of` from the leaf upward, guarding against cycles with a visited set. A root view (no parent) yields a chain of length 1. `parent_of` is injected so the traversal is pure and unit-testable; the command wires it to `Repository::get_view_info(...).parent_name`. # Example ```rust,ignore // draft `orange` parented on `dev` → [
(
leaf: &str,
mut parent_of: impl FnMut(&str) -> Result<Option<String>, E>,
)
| 60 | /// })?; |
| 61 | /// ``` |
| 62 | pub fn build_view_chain<E>( |
| 63 | leaf: &str, |
| 64 | mut parent_of: impl FnMut(&str) -> Result<Option<String>, E>, |
| 65 | ) -> Result<Vec<String>, ChainError<E>> { |
| 66 | let mut chain = vec![leaf.to_string()]; |
| 67 | let mut visited: HashSet<String> = HashSet::new(); |
| 68 | visited.insert(leaf.to_string()); |
| 69 | |
| 70 | let mut current = leaf.to_string(); |
| 71 | while let Some(parent) = parent_of(¤t).map_err(ChainError::Lookup)? { |
| 72 | if !visited.insert(parent.clone()) { |
| 73 | return Err(ChainError::Cycle { view: parent }); |
| 74 | } |
| 75 | chain.push(parent.clone()); |
| 76 | current = parent; |
| 77 | } |
| 78 | |
| 79 | chain.reverse(); |
| 80 | Ok(chain) |
| 81 | } |
| 82 | |
| 83 | // Sync Planning |
| 84 |