cargo-modules 0.4.4

A cargo plugin for showing a tree-like overview of a crate's modules.
use std::cmp::Ordering;

pub trait Visitor {
    fn visit(&self, module: &Tree, path: &[(usize, usize)], parents: &[&str]);
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Visibility {
    Public,
    Private,
}

#[derive(Debug)]
pub enum Tree {
    Crate {
        name: String,
        subtrees: Vec<Tree>,
        uses: Vec<(Visibility, String)>,
    },
    Module {
        name: String,
        visibility: Visibility,
        condition: Option<String>,
        subtrees: Vec<Tree>,
        uses: Vec<(Visibility, String)>,
    },
    Orphan {
        name: String,
    },
}

impl PartialEq<Tree> for Tree {
    fn eq(&self, other: &Tree) -> bool {
        self.name() == other.name()
    }
}

impl Eq for Tree {}

impl PartialOrd<Tree> for Tree {
    fn partial_cmp(&self, other: &Tree) -> Option<Ordering> {
        self.name().partial_cmp(other.name())
    }
}

impl Ord for Tree {
    fn cmp(&self, other: &Tree) -> Ordering {
        self.name().cmp(other.name())
    }
}

impl Tree {
    pub fn new_crate(name: String) -> Self {
        Tree::Crate {
            name,
            subtrees: vec![],
            uses: vec![],
        }
    }

    pub fn new_module(name: String, visibility: Visibility, condition: Option<String>) -> Self {
        Tree::Module {
            name,
            visibility,
            condition,
            subtrees: vec![],
            uses: vec![],
        }
    }

    pub fn new_orphan(name: String) -> Self {
        Tree::Orphan { name }
    }

    pub fn subtree_at_path(&mut self, path: &[String]) -> Option<&mut Tree> {
        if let Some((name, sub_path)) = path.split_first() {
            match *self {
                Tree::Crate {
                    ref mut subtrees, ..
                }
                | Tree::Module {
                    ref mut subtrees, ..
                } => subtrees
                    .iter_mut()
                    .find(|m| m.name() == *name)
                    .and_then(|subtree| subtree.subtree_at_path(sub_path)),
                Tree::Orphan { .. } => None,
            }
        } else {
            Some(self)
        }
    }

    pub fn insert(&mut self, subtree: Tree) {
        match *self {
            Tree::Crate {
                ref mut subtrees, ..
            }
            | Tree::Module {
                ref mut subtrees, ..
            } => {
                subtrees.push(subtree);
                subtrees.sort();
            }
            Tree::Orphan { .. } => {}
        }
    }

    pub fn insert_use(&mut self, used_module: (Visibility, String)) {
        match *self {
            Tree::Crate { ref mut uses, .. } | Tree::Module { ref mut uses, .. } => {
                uses.push(used_module);
                uses.sort_by(|a, b| a.1.cmp(&b.1));
            }
            Tree::Orphan { .. } => {}
        }
    }

    pub fn name(&self) -> &str {
        match *self {
            Tree::Crate { ref name, .. }
            | Tree::Module { ref name, .. }
            | Tree::Orphan { ref name } => name,
        }
    }

    pub fn subtree_names(&self) -> Vec<String> {
        match *self {
            Tree::Crate { ref subtrees, .. } | Tree::Module { ref subtrees, .. } => {
                subtrees.iter().map(|m| m.name().to_string()).collect()
            }
            Tree::Orphan { .. } => vec![],
        }
    }

    pub fn accept<'a, V>(
        &'a self,
        path: &mut Vec<(usize, usize)>,
        parents: &mut Vec<&'a str>,
        visitor: &V,
    ) where
        V: Visitor,
    {
        visitor.visit(self, path, parents);
        match *self {
            Tree::Crate {
                ref subtrees,
                ref name,
                ..
            }
            | Tree::Module {
                ref subtrees,
                ref name,
                ..
            } => {
                let count = subtrees.len();
                parents.push(name);
                for (index, subtree) in subtrees.iter().enumerate() {
                    path.push((index, count));
                    subtree.accept(path, parents, visitor);
                    let _ = path.pop();
                    let _ = parents.pop();
                }
            }
            Tree::Orphan { .. } => {}
        }
    }
}