<commit_before>// TODO: Remove
#![allow(dead_code)]

// TODO: make this private again, once #18241 is fixed
// https://github.com/rust-lang/rust/issues/18241
pub mod item;
pub mod block;

pub use self::item::{
    ItemExt,
    TypeDef,
    Interface,
    Class,
    ClassMember,
    Field,
    Method,
    TypeItem,
    FormalParameter,
};

pub use self::block::{
    Block,
    BlockStatement,
    Statement,
    StatementType,
    Expr,
    ExprType,
    BinOpType,
    UnaryOpType,
};

use std::fmt::{Display, Formatter, Error};
use base::code::{BytePos, Span};
use std::vec::Vec;
use std::default::Default;
use std::fmt;
use std::marker;

macro_rules! java_enum { (
    $name:ident { $( $variant:ident => $java_word:expr, )* }
) => {
    #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
    pub enum $name {
        $( $variant, )*
    }

    impl $name {
        pub fn as_java_string(&self) -> &str {
            match *self {
                $( $name::$variant => $java_word , )*
            }
        }
    }

    impl Display for $name {
        fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
            self.as_java_string().fmt(f)
        }
    }
}}

pub type Dims = u16;

// ============================================================================
// Definition of types that are common in AST nodes
// ============================================================================
#[derive(Clone, Debug)]
pub struct Spanned<T: Clone + fmt::Debug> {
    pub inner: T,
    pub span: Span,
}

impl Into<Expr> for Spanned<ExprType> {
    fn into(self) -> Expr {
        Expr {
            expr: self.inner,
            span: self.span,
        }
    }
}

impl Into<Expr> for Spanned<Expr> {
    fn into(self) -> Expr {
        self.inner
    }
}

impl Into<Box<Expr>> for Spanned<ExprType> {
    fn into(self) -> Box<Expr> {
        Box::new(Expr {
            expr: self.inner,
            span: self.span,
        })
    }
}

impl<T> marker::Copy for Spanned<T> where T: Copy + fmt::Debug {}

#[derive(Clone)]
pub struct Ident {
    pub name: String,
    pub span: Span,
}

// custom `Debug` impl to shorten debug output and improve readability
impl fmt::Debug for Ident {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, r#"Ident("{}" @ ({}, {}))"#,
            self.name, self.span.lo.0, self.span.hi.0)
    }
}

#[derive(Debug, Clone)]
pub struct Path {
    pub segments: Vec<Ident>,
}

impl Path {
    pub fn single(name: Ident) -> Path {
        Path {
            segments: vec![name],
        }
    }

    pub fn span(&self) -> Option<Span> {
        match (self.segments.first(), self.segments.last()) {
            (Some(first), Some(last)) => Some(Span {
                lo: first.span.lo,
                hi: last.span.hi
            }),
            _ => None,
        }
    }
}

// pub struct PathSegment {

// }

#[derive(Debug, Clone)]
pub struct Type {
    pub name: Path,
    pub dims: Dims,
}

// ============================================================================
// Top-Down AST definition starting with the goal symbol
// ============================================================================
/// A Java compilation unit. This is the goal symbol for the syntactic grammar.
#[derive(Debug, Clone)]
pub struct CompilationUnit {
    pub package: Option<Path>,
    pub imports: Vec<Import>,
    pub types: Vec<TypeDef>,
}

impl Default for Ident {
    fn default() -> Self {
        Ident {
            name: "".into(),
            span: Span::dummy(),
        }
    }
}

/// A import declaration
#[derive(Debug, Clone)]
pub enum Import {
    /// e.g. `import IO.AlgoTools;`
    SingleType(Path),
    /// called "type-import-on-demand" in specs -- e.g. `import IO.*;`
    TypeOnDemand(Path),
    SingleStatic(Path),
    StaticOnDemand(Path),
}

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

#[derive(Debug, Clone)]
pub struct Name {
    // for qualified names
    pub path: Vec<Ident>,
    pub last: Option<Ident>,
}

java_enum! (Modifier {
    Public => "public",
    Protected => "protected",
    Private => "private",
    Abstract => "abstract",
    Static => "static",
    Final => "final",
    Synchronized => "synchronized",
    Native => "native",
    Strictfp => "strictfp",
    Transient => "transient",
    Volatile => "volatile",
});
<commit_msg>Add custom `Debug` impl for `Path`<commit_after>// TODO: Remove
#![allow(dead_code)]

// TODO: make this private again, once #18241 is fixed
// https://github.com/rust-lang/rust/issues/18241
pub mod item;
pub mod block;

pub use self::item::{
    ItemExt,
    TypeDef,
    Interface,
    Class,
    ClassMember,
    Field,
    Method,
    TypeItem,
    FormalParameter,
};

pub use self::block::{
    Block,
    BlockStatement,
    Statement,
    StatementType,
    Expr,
    ExprType,
    BinOpType,
    UnaryOpType,
};

use std::fmt::{Display, Formatter, Error};
use base::code::{BytePos, Span};
use std::vec::Vec;
use std::default::Default;
use std::fmt;
use std::marker;

macro_rules! java_enum { (
    $name:ident { $( $variant:ident => $java_word:expr, )* }
) => {
    #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
    pub enum $name {
        $( $variant, )*
    }

    impl $name {
        pub fn as_java_string(&self) -> &str {
            match *self {
                $( $name::$variant => $java_word , )*
            }
        }
    }

    impl Display for $name {
        fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
            self.as_java_string().fmt(f)
        }
    }
}}

pub type Dims = u16;

// ============================================================================
// Definition of types that are common in AST nodes
// ============================================================================
#[derive(Clone, Debug)]
pub struct Spanned<T: Clone + fmt::Debug> {
    pub inner: T,
    pub span: Span,
}

impl Into<Expr> for Spanned<ExprType> {
    fn into(self) -> Expr {
        Expr {
            expr: self.inner,
            span: self.span,
        }
    }
}

impl Into<Expr> for Spanned<Expr> {
    fn into(self) -> Expr {
        self.inner
    }
}

impl Into<Box<Expr>> for Spanned<ExprType> {
    fn into(self) -> Box<Expr> {
        Box::new(Expr {
            expr: self.inner,
            span: self.span,
        })
    }
}

impl<T> marker::Copy for Spanned<T> where T: Copy + fmt::Debug {}

#[derive(Clone)]
pub struct Ident {
    pub name: String,
    pub span: Span,
}

// custom `Debug` impl to shorten debug output and improve readability
impl fmt::Debug for Ident {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, r#"Ident("{}" @ ({}, {}))"#,
            self.name, self.span.lo.0, self.span.hi.0)
    }
}

#[derive(Clone)]
pub struct Path {
    pub segments: Vec<Ident>,
}

impl Path {
    pub fn single(name: Ident) -> Path {
        Path {
            segments: vec![name],
        }
    }

    pub fn span(&self) -> Option<Span> {
        match (self.segments.first(), self.segments.last()) {
            (Some(first), Some(last)) => Some(Span {
                lo: first.span.lo,
                hi: last.span.hi
            }),
            _ => None,
        }
    }
}

// custom `Debug` impl to shorten debug output and improve readability
impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(span) = self.span() {
            let mut p = self.segments
                .first()
                .map(|f| f.name.clone())
                .unwrap_or_default();
            for seg in &self.segments[1..] {
                p.push('.');
                p.push_str(&seg.name);
            }

            write!(f, r#"Path("{}" @ ({}, {}))"#, p, span.lo.0, span.hi.0)
        } else {
            write!(f, "Path(EMPTY)")
        }
    }
}

#[derive(Debug, Clone)]
pub struct Type {
    pub name: Path,
    pub dims: Dims,
}

// ============================================================================
// Top-Down AST definition starting with the goal symbol
// ============================================================================
/// A Java compilation unit. This is the goal symbol for the syntactic grammar.
#[derive(Debug, Clone)]
pub struct CompilationUnit {
    pub package: Option<Path>,
    pub imports: Vec<Import>,
    pub types: Vec<TypeDef>,
}

impl Default for Ident {
    fn default() -> Self {
        Ident {
            name: "".into(),
            span: Span::dummy(),
        }
    }
}

/// A import declaration
#[derive(Debug, Clone)]
pub enum Import {
    /// e.g. `import IO.AlgoTools;`
    SingleType(Path),
    /// called "type-import-on-demand" in specs -- e.g. `import IO.*;`
    TypeOnDemand(Path),
    SingleStatic(Path),
    StaticOnDemand(Path),
}

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

#[derive(Debug, Clone)]
pub struct Name {
    // for qualified names
    pub path: Vec<Ident>,
    pub last: Option<Ident>,
}

java_enum! (Modifier {
    Public => "public",
    Protected => "protected",
    Private => "private",
    Abstract => "abstract",
    Static => "static",
    Final => "final",
    Synchronized => "synchronized",
    Native => "native",
    Strictfp => "strictfp",
    Transient => "transient",
    Volatile => "volatile",
});
<|endoftext|>


<commit_before>/// mush graph backend and abstraction to swap out with custom backend

use uuid::Uuid;
use std::collections::{HashMap,HashSet,VecDeque};

/// unidirectional edge, use two edges for bidirectional/undirected graph
//#[derive(Hash, Eq, PartialEq, Debug)]
#[derive(Debug)]
struct GraphEdge<K> {
    to: Uuid,
    maybe_weight: Option<f64>,
    maybe_custom: Option<K>,
}
impl<K> GraphEdge<K> {
    // this should be private, this should be called by a graph method
    fn new (uuid: Uuid, weight: Option<f64>, custom: Option<K>) -> GraphEdge<K> {
        GraphEdge { to: uuid,
                    maybe_weight:weight,
                    maybe_custom: custom }
    }
    pub fn get_directed (&self) -> &Uuid {
        &self.to
    }

    pub fn get_weight (&self) -> Option<&f64> {
        if let Some(ref w) = self.maybe_weight { Some(w) }
        else { None }
    }
    pub fn get_weight_mut (&mut self) -> Option<&mut f64> {
        if let Some(ref mut w) = self.maybe_weight { Some(w) }
        else { None }
    }

    pub fn get_custom (&self) -> Option<&K> {
        if let Some(ref k) = self.maybe_custom { Some(k) }
        else { None }
    }
    pub fn get_custom_mut (&mut self) -> Option<&mut K> {
        if let Some(ref mut k) = self.maybe_custom { Some(k) }
        else { None }
    }
}

//--

#[derive(Debug)]
struct GraphNode {
    uuid: Uuid,
    edges_to: HashMap<Uuid,Option<f64>>, //to,weight //todo: turn to hashset if using graphedge struct
    edges_from: HashMap<Uuid,Option<f64>>,
}
impl GraphNode {
    fn new (uuid: Uuid) -> GraphNode {
        GraphNode { uuid: uuid,
                    edges_to: HashMap::new(),
                    edges_from: HashMap::new(), }
    }
    /// direct the node towards another node
    // todo: rename me! sounds too similar to unidirectional
    fn direct (&mut self, to:&Uuid, weight: Option<f64>) {
        self.edges_to.insert(*to,weight);
    }
    fn undirect (&mut self, to:&Uuid) {
        self.edges_to.remove(to);
    }
}

//----
#[derive(Debug)]
pub struct Graph {
    nodes: HashMap<Uuid,GraphNode>,

    // todo: as traits
    is_weighted: bool,
    is_directed: bool,
    is_tracking: bool,  // tracking from-edges
}

//todo: turn many of these methods into a trait
impl Graph {
    pub fn default() -> Graph {
        Graph { nodes: HashMap::new(),
                is_weighted: false,
                is_directed: true,
                is_tracking: false, }
    }

    /// manual accessors
    fn get_node_mut(&mut self, uuid: &Uuid) -> Option<&mut GraphNode> {
        self.nodes.get_mut(uuid)
    }
    fn get_node(&self, uuid: &Uuid) -> Option<&GraphNode> {
        self.nodes.get(uuid)
    }

    pub fn add (&mut self) -> Uuid { //todo: maybe_edges fn arg
        let uuid = Uuid::new_v4();
        let n = GraphNode::new(uuid);
        self.nodes.insert(uuid,n);
        uuid
    }
    pub fn remove(&mut self, node: &Uuid) -> bool { //Option<GraphNode> {
        self.nodes.remove(node).is_some()
    }

    pub fn direct(&mut self, from: &Uuid, to: &Uuid) -> bool {
        let mut r = true;

        if !self.get_node(to).is_some() { return false } // todo: expand on this, and impl for undirect?

        if let Some(f) = self.nodes.get_mut(from) {
            f.direct(to,None);
        }
        else { r = false; }

        if self.is_directed { return r }
        else if r {
            if let Some(t) = self.nodes.get_mut(to) {
                t.direct(from,None);
            }
            else { r = false; }
        }

        return r
    }
    pub fn undirect(&mut self, from: &Uuid, to: &Uuid) -> bool {
        let mut r = true;
        if let Some(f) = self.nodes.get_mut(from) {
            f.undirect(to);
        }
        else { r = false; }

        if self.is_directed { return r }
        else if r {
            if let Some(t) = self.nodes.get_mut(to) {
                t.undirect(from);
            }
            else { r = false; }
        }

        return r
    }

    // search functions
    // todo: consider weights between nodes to direct search, cycle-detection
    pub fn get_path(&self, s: GraphSearch) -> Option<Vec<Uuid>> {
        let mut visited: HashSet<Uuid> = HashSet::new();
        let mut result = vec!();

        match s {
            GraphSearch::Depth(from,to) => {
                let mut stack = vec!();

                stack.push(from);
                visited.insert(from);
                result.push(from);

                let mut cursor = Some(from);

                while cursor.is_some() {
                    if let Some(ref node) = self.get_node(&cursor.unwrap()) {
                        //get first unvisited node
                        let not_visited = node.edges_to.iter().find(|&(&n,v)| !visited.contains(&n));

                        if let Some((&n,w)) = not_visited {
                            if !self.is_tracking || self.nodes.contains_key(&n) { //node exists?
                                stack.push(n);
                                visited.insert(n);
                                result.push(n);

                                if let Some(to_node) = to {
                                    if n == to_node { break; }
                                }

                                cursor = Some(n);
                            }
                        }
                        else { cursor = stack.pop(); }
                    }
                    else { cursor = stack.pop(); }
                }

                if let Some(to_node) = to {
                    if result.contains(&to_node) {
                        return Some(result)
                    }
                }

                return None
            },
            GraphSearch::Breadth(from,to) => { // breadth first search, uses a queue
                let mut queue = VecDeque::new();

                queue.push_back(from);
                visited.insert(from);
                result.push(from);

                let mut cursor = Some(from);

                while cursor.is_some() {
                    if let Some(ref node) = self.get_node(&cursor.unwrap()) {
                        //get unvisted nodes to queue up
                        let not_visited: Vec<Option<(Uuid,Option<f64>)>> = node.edges_to.iter().map(|(&n,&v)| {
                            if !visited.contains(&n) {
                                Some((n,v))
                            }
                            else { None }
                        }).collect();

                        for maybe_node in not_visited {
                            if let Some((n,w)) = maybe_node {
                                if !self.is_tracking || self.nodes.contains_key(&n) { //node exists?
                                    queue.push_back(n);
                                    visited.insert(n);
                                    result.push(n);

                                    if let Some(to_node) = to {
                                        if n == to_node { break; }
                                    }

                                    cursor = Some(n);
                                }
                            }
                        }

                        cursor = queue.pop_front();
                    }
                    else { cursor = queue.pop_front(); }
                }

                if let Some(to_node) = to {
                    if result.contains(&to_node) {
                        return Some(result)
                    }
                }

                return None
            },
            _ => None, // todo: djk algo
        }

    }

    //this is virtually the same as get_path dfs, should abstract dfs somehow to use it for this
    pub fn get_cycle(&self, from: Uuid) -> HashSet<(Uuid,Uuid)> {
        let mut stack = vec!();
        let mut visited: HashSet<Uuid> = HashSet::new();
        let mut r = HashSet::new(); //Vec::new();

        stack.push(from);

        while stack.len() > 0 {
            let cursor = *stack.last().unwrap();
            visited.insert(cursor);

            if let Some(ref node) = self.get_node(&cursor) {

                //does the cursor point to a node on stack
                for (n,_) in node.edges_to.iter() {
                    if stack.contains(&n) {
                        r.insert((*n,cursor));
                    }
                }

                //get first unvisited node
                let not_visited = node.edges_to.iter().find(|&(n,_)| !visited.contains(n));

                if let Some((&n,_)) = not_visited {
                    if !stack.contains(&n) {
                        stack.push(n); //add node to check
                    }
                }
                else { stack.pop(); } //nothing left, pop off and head back a node
            }
            else { stack.pop(); } //invalid node?
        }

        return r
    }

    /// get immediate next node from list of connected nodes for the current node
    pub fn get_next(&self, from: &Uuid) -> Option<Uuid> {
        if let Some(n) = self.nodes.get(from) {
            if let Some(next_id) = n.edges_to.iter().next() {
                return Some(*next_id.0) // grab uuid key
            }
        }

        return None

    }

    fn is_connected() -> bool { false }
    fn is_complete() -> bool { false }

    fn get_path_shortest(&self) -> bool {
        if !self.is_weighted { false } //must be weighted
        else { false } //todo: use bfs
    }
}

// todo: impl as trait instead?
pub enum GraphSearch {
    Depth(Uuid,Option<Uuid>), // used on part of graph for reachability, and all of graph for cycle-detection
    Breadth(Uuid,Option<Uuid>), // used on part of graph for reachability, and (unweighted) for shortest path
    Dijkstra(Uuid,Uuid), // used on part of graph (weighted) for shortest path
}

pub struct GraphBuilder(Graph);

impl GraphBuilder {
    pub fn new() -> GraphBuilder {
        GraphBuilder(Graph::default())
    }

    pub fn directed(mut self, d: bool) -> GraphBuilder {
        self.0.is_directed = d;
        self
    }
    pub fn weighted(mut self, w: bool) -> GraphBuilder {
        self.0.is_weighted = w;
        self
    }
    pub fn track_from_nodes(mut self, t: bool) -> GraphBuilder {
        self.0.is_tracking = t;
        self
    }
    pub fn build(mut self) -> Graph {
        self.0
    }
}

// ----
// tests
// ----
#[cfg(test)]
mod tests {
    extern crate test;

    use ::{Graph,GraphSearch};

    use uuid::Uuid;
    use std::collections::{HashMap};

    #[test]
    fn test_basic_direct() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        assert!(graph.direct(&nodes[0],&nodes[1]));

        assert!(graph.direct(&nodes[3],&nodes[0]));
        assert!(graph.direct(&nodes[4],&nodes[3]));

        graph.remove(&nodes[2]);
        assert!(!graph.direct(&nodes[4],&nodes[2]));

        let n6 = graph.add();
        assert!(graph.direct(&n6,&nodes[3]));
        assert!(graph.direct(&nodes[0],&n6));
    }

    #[test]
    fn test_basic_paths() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        graph.direct(&nodes[0],&nodes[1]);

        graph.direct(&nodes[3],&nodes[0]);
        graph.direct(&nodes[4],&nodes[3]);

        graph.remove(&nodes[2]);

        let r = graph.get_path(GraphSearch::Depth(nodes[0],Some(nodes[4])));
        assert!(!r.is_some());

        let r = graph.get_path(GraphSearch::Depth(nodes[4],Some(nodes[0])));
        assert!(r.is_some());

        let r = graph.get_path(GraphSearch::Breadth(nodes[4],Some(nodes[0])));
        assert!(r.is_some());
    }

    #[test]
    fn test_basic_cycle() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        graph.direct(&nodes[0],&nodes[1]);

        graph.direct(&nodes[3],&nodes[0]);
        graph.direct(&nodes[4],&nodes[3]);

        graph.remove(&nodes[2]);

        let r = graph.get_cycle(nodes[4]);
        assert_eq!(r.len(),0);

        let n6 = graph.add();
        graph.direct(&n6,&nodes[3]);
        graph.direct(&nodes[0],&n6);

        let n7 = graph.add();
        graph.direct(&n7,&nodes[3]);
        graph.direct(&nodes[0],&n7);

        let r = graph.get_cycle(nodes[4]);
        println!("{:?}",r);
        assert_eq!(r.len(),2);
    }
}
<commit_msg>rewrite search functions<commit_after>/// mush graph backend and abstraction to swap out with custom backend

use uuid::Uuid;
use std::collections::{HashMap,HashSet,VecDeque};

/// unidirectional edge, use two edges for bidirectional/undirected graph
//#[derive(Hash, Eq, PartialEq, Debug)]
#[derive(Debug)]
struct GraphEdge<K> {
    to: Uuid,
    maybe_weight: Option<f64>,
    maybe_custom: Option<K>,
}
impl<K> GraphEdge<K> {
    // this should be private, this should be called by a graph method
    fn new (uuid: Uuid, weight: Option<f64>, custom: Option<K>) -> GraphEdge<K> {
        GraphEdge { to: uuid,
                    maybe_weight:weight,
                    maybe_custom: custom }
    }
    pub fn get_directed (&self) -> &Uuid {
        &self.to
    }

    pub fn get_weight (&self) -> Option<&f64> {
        if let Some(ref w) = self.maybe_weight { Some(w) }
        else { None }
    }
    pub fn get_weight_mut (&mut self) -> Option<&mut f64> {
        if let Some(ref mut w) = self.maybe_weight { Some(w) }
        else { None }
    }

    pub fn get_custom (&self) -> Option<&K> {
        if let Some(ref k) = self.maybe_custom { Some(k) }
        else { None }
    }
    pub fn get_custom_mut (&mut self) -> Option<&mut K> {
        if let Some(ref mut k) = self.maybe_custom { Some(k) }
        else { None }
    }
}

//--

#[derive(Debug)]
struct GraphNode {
    uuid: Uuid,
    edges_to: HashMap<Uuid,Option<f64>>, //to,weight //todo: turn to hashset if using graphedge struct
    edges_from: HashMap<Uuid,Option<f64>>,
}
impl GraphNode {
    fn new (uuid: Uuid) -> GraphNode {
        GraphNode { uuid: uuid,
                    edges_to: HashMap::new(),
                    edges_from: HashMap::new(), }
    }
    /// direct the node towards another node
    // todo: rename me! sounds too similar to unidirectional
    fn direct (&mut self, to:&Uuid, weight: Option<f64>) {
        self.edges_to.insert(*to,weight);
    }
    fn undirect (&mut self, to:&Uuid) {
        self.edges_to.remove(to);
    }
}

//----
#[derive(Debug)]
pub struct Graph {
    nodes: HashMap<Uuid,GraphNode>,

    // todo: as traits
    is_weighted: bool,
    is_directed: bool,
    is_tracking: bool,  // tracking from-edges
}

//todo: turn many of these methods into a trait
impl Graph {
    pub fn default() -> Graph {
        Graph { nodes: HashMap::new(),
                is_weighted: false,
                is_directed: true,
                is_tracking: false, }
    }

    /// manual accessors
    fn get_node_mut(&mut self, uuid: &Uuid) -> Option<&mut GraphNode> {
        self.nodes.get_mut(uuid)
    }
    fn get_node(&self, uuid: &Uuid) -> Option<&GraphNode> {
        self.nodes.get(uuid)
    }

    pub fn add (&mut self) -> Uuid { //todo: maybe_edges fn arg
        let uuid = Uuid::new_v4();
        let n = GraphNode::new(uuid);
        self.nodes.insert(uuid,n);
        uuid
    }
    pub fn remove(&mut self, node: &Uuid) -> bool { //Option<GraphNode> {
        self.nodes.remove(node).is_some()
    }

    pub fn direct(&mut self, from: &Uuid, to: &Uuid) -> bool {
        let mut r = true;

        if !self.get_node(to).is_some() { return false } // todo: expand on this, and impl for undirect?

        if let Some(f) = self.nodes.get_mut(from) {
            f.direct(to,None);
        }
        else { r = false; }

        if self.is_directed { return r }
        else if r {
            if let Some(t) = self.nodes.get_mut(to) {
                t.direct(from,None);
            }
            else { r = false; }
        }

        return r
    }
    pub fn undirect(&mut self, from: &Uuid, to: &Uuid) -> bool {
        let mut r = true;
        if let Some(f) = self.nodes.get_mut(from) {
            f.undirect(to);
        }
        else { r = false; }

        if self.is_directed { return r }
        else if r {
            if let Some(t) = self.nodes.get_mut(to) {
                t.undirect(from);
            }
            else { r = false; }
        }

        return r
    }

    // search functions
    // todo: consider weights between nodes to direct search, cycle-detection
    pub fn get_path(&self, s: GraphSearch) -> Option<Vec<Uuid>> {
        let mut visited: HashSet<Uuid> = HashSet::new();
        let mut result = vec!();

        match s {
            GraphSearch::Depth(from,to) => {
                let mut stack = vec!();

                stack.push(from);

                while stack.len() > 0 {
                    let cursor = *stack.last().unwrap();
                    visited.insert(cursor);

                    if let Some(ref node) = self.get_node(&cursor) {

                        //get first unvisited node
                        let not_visited = node.edges_to.iter().find(|&(n,_)| !visited.contains(n));

                        if let Some((&n,_)) = not_visited {
                            if !self.is_tracking || self.nodes.contains_key(&n) {
                                stack.push(n); //add node to check
                                result.push(n);

                                if let Some(to_node) = to {
                                    if n == to_node { break; }
                                }
                            }
                        }
                        else { stack.pop(); } //nothing left, pop off and head back a node
                    }
                    else { stack.pop(); } //invalid node?
                }

                if let Some(to_node) = to {
                    if result.contains(&to_node) {
                        return Some(result)
                    }
                }

                return None
            },
            GraphSearch::Breadth(from,to) => { // breadth first search, uses a queue
                let mut queue = VecDeque::new();

                queue.push_back(from);
                visited.insert(from);
                result.push(from);

                while queue.len() > 0 {
                    let cursor = *queue.front().unwrap();
                    if let Some(ref node) = self.get_node(&cursor) {
                        //get unvisted nodes to queue up
                        let not_visited: Vec<Option<(Uuid,Option<f64>)>> = node.edges_to.iter().map(|(&n,&v)| {
                            if !visited.contains(&n) {
                                Some((n,v))
                            }
                            else { None }
                        }).collect();

                        for maybe_node in not_visited {
                            if let Some((n,w)) = maybe_node {
                                if !self.is_tracking || self.nodes.contains_key(&n) { //node exists?
                                    queue.push_back(n);
                                    visited.insert(n);
                                    result.push(n);

                                    if let Some(to_node) = to {
                                        if n == to_node { break; }
                                    }
                                }
                            }
                        }

                        queue.pop_front();
                    }
                    else { queue.pop_front(); }
                }

                if let Some(to_node) = to {
                    if result.contains(&to_node) {
                        return Some(result)
                    }
                }

                return None
            },
            _ => None, // todo: djk algo
        }
    }

    //this is virtually the same as get_path dfs, should abstract dfs somehow to use it for this
    pub fn get_cycle(&self, from: Uuid) -> HashSet<(Uuid,Uuid)> {
        let mut stack = vec!();
        let mut visited: HashSet<Uuid> = HashSet::new();
        let mut r = HashSet::new(); //Vec::new();

        stack.push(from);

        while stack.len() > 0 {
            let cursor = *stack.last().unwrap();
            visited.insert(cursor);

            if let Some(ref node) = self.get_node(&cursor) {

                //does the cursor point to a node on stack
                for (n,_) in node.edges_to.iter() {
                    if stack.contains(&n) {
                        r.insert((*n,cursor));
                    }
                }

                //get first unvisited node
                let not_visited = node.edges_to.iter().find(|&(n,_)| !visited.contains(n));

                if let Some((&n,_)) = not_visited {
                    if !stack.contains(&n) {
                        stack.push(n); //add node to check
                    }
                }
                else { stack.pop(); } //nothing left, pop off and head back a node
            }
            else { stack.pop(); } //invalid node?
        }

        return r
    }

    /// get immediate next node from list of connected nodes for the current node
    pub fn get_next(&self, from: &Uuid) -> Option<Uuid> {
        if let Some(n) = self.nodes.get(from) {
            if let Some(next_id) = n.edges_to.iter().next() {
                return Some(*next_id.0) // grab uuid key
            }
        }

        return None

    }

    fn is_connected() -> bool { false }
    fn is_complete() -> bool { false }

    fn get_path_shortest(&self) -> bool {
        if !self.is_weighted { false } //must be weighted
        else { false } //todo: use bfs
    }
}

// todo: impl as trait instead?
pub enum GraphSearch {
    Depth(Uuid,Option<Uuid>), // used on part of graph for reachability, and all of graph for cycle-detection
    Breadth(Uuid,Option<Uuid>), // used on part of graph for reachability, and (unweighted) for shortest path
    Dijkstra(Uuid,Uuid), // used on part of graph (weighted) for shortest path
}

pub struct GraphBuilder(Graph);

impl GraphBuilder {
    pub fn new() -> GraphBuilder {
        GraphBuilder(Graph::default())
    }

    pub fn directed(mut self, d: bool) -> GraphBuilder {
        self.0.is_directed = d;
        self
    }
    pub fn weighted(mut self, w: bool) -> GraphBuilder {
        self.0.is_weighted = w;
        self
    }
    pub fn track_from_nodes(mut self, t: bool) -> GraphBuilder {
        self.0.is_tracking = t;
        self
    }
    pub fn build(mut self) -> Graph {
        self.0
    }
}

// ----
// tests
// ----
#[cfg(test)]
mod tests {
    extern crate test;

    use ::{Graph,GraphSearch};

    use uuid::Uuid;
    use std::collections::{HashMap};

    #[test]
    fn test_basic_direct() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        assert!(graph.direct(&nodes[0],&nodes[1]));

        assert!(graph.direct(&nodes[3],&nodes[0]));
        assert!(graph.direct(&nodes[4],&nodes[3]));

        graph.remove(&nodes[2]);
        assert!(!graph.direct(&nodes[4],&nodes[2]));

        let n6 = graph.add();
        assert!(graph.direct(&n6,&nodes[3]));
        assert!(graph.direct(&nodes[0],&n6));
    }

    #[test]
    fn test_basic_paths() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        graph.direct(&nodes[0],&nodes[1]);

        graph.direct(&nodes[3],&nodes[0]);
        graph.direct(&nodes[4],&nodes[3]);

        graph.remove(&nodes[2]);

        let r = graph.get_path(GraphSearch::Depth(nodes[0],Some(nodes[4])));
        assert!(!r.is_some());

        let r = graph.get_path(GraphSearch::Depth(nodes[4],Some(nodes[0])));
        assert!(r.is_some());

        let r = graph.get_path(GraphSearch::Breadth(nodes[4],Some(nodes[0])));
        assert!(r.is_some());
    }

    #[test]
    fn test_basic_cycle() {
        let mut graph = Graph::default();
        let mut nodes = vec!();
        for _ in 0..5 {
            nodes.push(graph.add());
        }

        graph.direct(&nodes[0],&nodes[1]);

        graph.direct(&nodes[3],&nodes[0]);
        graph.direct(&nodes[4],&nodes[3]);

        graph.remove(&nodes[2]);

        let r = graph.get_cycle(nodes[4]);
        assert_eq!(r.len(),0);

        let n6 = graph.add();
        graph.direct(&n6,&nodes[3]);
        graph.direct(&nodes[0],&n6);

        let n7 = graph.add();
        graph.direct(&n7,&nodes[3]);
        graph.direct(&nodes[0],&n7);

        let r = graph.get_cycle(nodes[4]);
        println!("{:?}",r);
        assert_eq!(r.len(),2);
    }
}
<|endoftext|>


<commit_before>use clap;
use std::io::{self, Write};
use std::process;
use std::path::PathBuf;
use std::error;
use std::result;
use std::fmt;
use structopt::StructOpt;
use mprovision as mp;

#[derive(Debug, PartialEq, StructOpt)]
/// A tool that helps iOS developers to manage mobileprovision files.
pub enum Command {
    #[structopt(name = "list", raw(display_order = "0"))]
    /// Lists provisioning profiles
    List(ListParams),
    #[structopt(name = "show", raw(display_order = "1"))]
    /// Shows details of a provisioning profile using its uuid
    ShowUuid(ShowUuidParams),
    #[structopt(name = "show-file", raw(display_order = "2"))]
    /// Shows details of a provisioning profile
    ShowFile(ShowFileParams),
    #[structopt(name = "remove", raw(display_order = "3"))]
    /// Removes provisioning profiles
    Remove(RemoveParams),
    #[structopt(name = "clean", raw(display_order = "4"))]
    /// Removes expired provisioning profiles
    Clean(CleanParams),
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ListParams {
    #[structopt(short = "t", long = "text", raw(empty_values = "false"))]
    /// Lists provisioning profiles that contain this text
    pub text: Option<String>,
    #[structopt(short = "d", long = "expire-in-days", parse(try_from_str = "parse_days"))]
    /// Lists provisioning profiles that will expire in days
    pub expire_in_days: Option<i64>,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ShowUuidParams {
    #[structopt(raw(empty_values = "false"))]
    /// An uuid of a provisioning profile
    pub uuid: String,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ShowFileParams {
    #[structopt(parse(from_os_str), raw(empty_values = "false"))]
    /// A file path of a provisioning profile
    pub file: PathBuf,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct RemoveParams {
    #[structopt(raw(empty_values = "false"))]
    /// uuid(s) of provisioning profiles
    pub uuids: Vec<String>,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct CleanParams {
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to clean
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

/// Parses arguments and returns a `Command`.
pub fn parse<I, S>(args: I) -> Result
where
    I: IntoIterator<Item = S>,
    S: Clone,
    ::std::ffi::OsString: From<S>,
{
    let app = Command::clap();
    let matches = app.get_matches_from_safe(args)?;
    Ok(Command::from_clap(&matches))
}

/// Parses and validates days argument.
fn parse_days(s: &str) -> result::Result<i64, Error> {
    let days = s.parse::<i64>()
        .map_err(|err| Error::Custom(err.to_string()))?;
    if days < 0 || days > 365 {
        let message = format!("should be between 0 and 365, got {}", days);
        return Err(Error::Custom(message));
    }
    Ok(days)
}

#[cfg(test)]
mod tests {
    use super::*;
    use expectest::prelude::*;

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "--source", "."])).to(be_ok().value(Command::List(
            ListParams {
                text: None,
                expire_in_days: None,
                directory: Some(".".into()),
            },
        )));

        expect!(parse(&["mprovision", "list", "--source", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--text", "abc"])).to(be_ok().value(Command::List(
            ListParams {
                text: Some("abc".to_string()),
                expire_in_days: None,
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "-t", "abc"])).to(be_ok().value(Command::List(
            ListParams {
                text: Some("abc".to_string()),
                expire_in_days: None,
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "--text", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "-t", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expire-in-days", "3"])).to(be_ok().value(
            Command::List(ListParams {
                text: None,
                expire_in_days: Some(3),
                directory: None,
            }),
        ));

        expect!(parse(&["mprovision", "list", "-d", "3"])).to(be_ok().value(Command::List(
            ListParams {
                text: None,
                expire_in_days: Some(3),
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "--expire-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "-d", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expire-in-days", "366"])).to(be_err());
        expect!(parse(&["mprovision", "list", "-d", "366"])).to(be_err());

        expect!(parse(&[
            "mprovision",
            "list",
            "--text",
            "abc",
            "--expire-in-days",
            "3",
            "--source",
            "."
        ])).to(be_ok().value(Command::List(ListParams {
            text: Some("abc".to_string()),
            expire_in_days: Some(3),
            directory: Some(".".into()),
        })));

        expect!(parse(&[
            "mprovision",
            "list",
            "-t",
            "abc",
            "-d",
            "3",
            "--source",
            "."
        ])).to(be_ok().value(Command::List(ListParams {
            text: Some("abc".to_string()),
            expire_in_days: Some(3),
            directory: Some(".".into()),
        })));
    }

    #[test]
    fn show_uuid_command() {
        expect!(parse(&["mprovision", "show", "abcd"])).to(be_ok().value(Command::ShowUuid(
            ShowUuidParams {
                uuid: "abcd".to_string(),
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "show", ""])).to(be_err());

        expect!(parse(&["mprovision", "show", "abcd", "--source", "."])).to(be_ok().value(
            Command::ShowUuid(ShowUuidParams {
                uuid: "abcd".to_string(),
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&["mprovision", "show", "abcd", "--source", ""])).to(be_err());
    }

    #[test]
    fn show_file_command() {
        expect!(parse(&["mprovision", "show-file", "file.mprovision"])).to(be_ok().value(
            Command::ShowFile(ShowFileParams {
                file: "file.mprovision".into(),
            }),
        ));

        expect!(parse(&["mprovision", "show-file", "file.mprovision", "."])).to(be_err());

        expect!(parse(&["mprovision", "show-file", ""])).to(be_err());
    }

    #[test]
    fn remove_uuid_command() {
        expect!(parse(&["mprovision", "remove", "abcd"])).to(be_ok().value(Command::Remove(
            RemoveParams {
                uuids: vec!["abcd".to_string()],
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "remove", "abcd", "ef"])).to(be_ok().value(
            Command::Remove(RemoveParams {
                uuids: vec!["abcd".to_string(), "ef".to_string()],
                directory: None,
            }),
        ));

        expect!(parse(&["mprovision", "remove", ""])).to(be_err());

        expect!(parse(&["mprovision", "remove", "abcd", "--source", "."])).to(be_ok().value(
            Command::Remove(RemoveParams {
                uuids: vec!["abcd".to_string()],
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&[
            "mprovision",
            "remove",
            "abcd",
            "ef",
            "--source",
            "."
        ])).to(be_ok().value(Command::Remove(RemoveParams {
            uuids: vec!["abcd".to_string(), "ef".to_string()],
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "remove", "abcd", "--source", ""])).to(be_err());
    }

    #[test]
    fn clean_command() {
        expect!(parse(&["mprovision", "clean"]))
            .to(be_ok().value(Command::Clean(CleanParams { directory: None })));

        expect!(parse(&["mprovision", "clean", "--source", "."])).to(be_ok().value(
            Command::Clean(CleanParams {
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&["mprovision", "clean", "--source", ""])).to(be_err());
    }
}
<commit_msg>Use clap's DeriveDisplayOrder setting, formatting<commit_after>use clap;
use mprovision as mp;
use std::error;
use std::fmt;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process;
use std::result;
use structopt::StructOpt;

#[derive(Debug, PartialEq, StructOpt)]
#[structopt(raw(global_settings = "&[clap::AppSettings::DeriveDisplayOrder]"))]
/// A tool that helps iOS developers to manage mobileprovision files.
pub enum Command {
    #[structopt(name = "list")]
    /// Lists provisioning profiles
    List(ListParams),
    #[structopt(name = "show")]
    /// Shows details of a provisioning profile using its uuid
    ShowUuid(ShowUuidParams),
    #[structopt(name = "show-file")]
    /// Shows details of a provisioning profile
    ShowFile(ShowFileParams),
    #[structopt(name = "remove")]
    /// Removes provisioning profiles
    Remove(RemoveParams),
    #[structopt(name = "clean")]
    /// Removes expired provisioning profiles
    Clean(CleanParams),
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ListParams {
    #[structopt(short = "t", long = "text", raw(empty_values = "false"))]
    /// Lists provisioning profiles that contain this text
    pub text: Option<String>,
    #[structopt(short = "d", long = "expire-in-days", parse(try_from_str = "parse_days"))]
    /// Lists provisioning profiles that will expire in days
    pub expire_in_days: Option<i64>,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ShowUuidParams {
    #[structopt(raw(empty_values = "false"))]
    /// An uuid of a provisioning profile
    pub uuid: String,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct ShowFileParams {
    #[structopt(parse(from_os_str), raw(empty_values = "false"))]
    /// A file path of a provisioning profile
    pub file: PathBuf,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct RemoveParams {
    #[structopt(raw(empty_values = "false"))]
    /// uuid(s) of provisioning profiles
    pub uuids: Vec<String>,
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to search provisioning profiles
    pub directory: Option<PathBuf>,
}

#[derive(Debug, Default, PartialEq, StructOpt)]
pub struct CleanParams {
    #[structopt(long = "source", parse(from_os_str), raw(empty_values = "false"))]
    /// A directory where to clean
    pub directory: Option<PathBuf>,
}

pub type Result = result::Result<Command, Error>;

#[derive(Debug)]
pub enum Error {
    Lib(mp::Error),
    Clap(clap::Error),
    Custom(String),
}

impl Error {
    pub fn exit(&self) -> ! {
        match *self {
            Error::Clap(ref e) => e.exit(),
            _ => {
                writeln!(&mut io::stderr(), "{}", self).unwrap();
                process::exit(1);
            }
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Lib(ref e) => e.description(),
            Error::Clap(ref e) => e.description(),
            Error::Custom(ref e) => e,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Lib(ref e) => Some(e),
            Error::Clap(ref e) => Some(e),
            Error::Custom(_) => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Lib(ref e) => error::Error::description(e).fmt(f),
            Error::Clap(ref e) => error::Error::description(e).fmt(f),
            Error::Custom(ref e) => e.fmt(f),
        }
    }
}

impl From<mp::Error> for Error {
    fn from(e: mp::Error) -> Self {
        Error::Lib(e)
    }
}

impl From<clap::Error> for Error {
    fn from(e: clap::Error) -> Self {
        Error::Clap(e)
    }
}

/// Parses arguments and returns a `Command`.
pub fn parse<I, S>(args: I) -> Result
where
    I: IntoIterator<Item = S>,
    S: Clone,
    ::std::ffi::OsString: From<S>,
{
    let app = Command::clap();
    let matches = app.get_matches_from_safe(args)?;
    Ok(Command::from_clap(&matches))
}

/// Parses and validates days argument.
fn parse_days(s: &str) -> result::Result<i64, Error> {
    let days = s.parse::<i64>()
        .map_err(|err| Error::Custom(err.to_string()))?;
    if days < 0 || days > 365 {
        let message = format!("should be between 0 and 365, got {}", days);
        return Err(Error::Custom(message));
    }
    Ok(days)
}

#[cfg(test)]
mod tests {
    use super::*;
    use expectest::prelude::*;

    #[test]
    fn list_command() {
        expect!(parse(&["mprovision", "list"]))
            .to(be_ok().value(Command::List(ListParams::default())));

        expect!(parse(&["mprovision", "list", "--source", "."])).to(be_ok().value(Command::List(
            ListParams {
                text: None,
                expire_in_days: None,
                directory: Some(".".into()),
            },
        )));

        expect!(parse(&["mprovision", "list", "--source", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--text", "abc"])).to(be_ok().value(Command::List(
            ListParams {
                text: Some("abc".to_string()),
                expire_in_days: None,
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "-t", "abc"])).to(be_ok().value(Command::List(
            ListParams {
                text: Some("abc".to_string()),
                expire_in_days: None,
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "--text", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "-t", ""])).to(be_err());

        expect!(parse(&["mprovision", "list", "--expire-in-days", "3"])).to(be_ok().value(
            Command::List(ListParams {
                text: None,
                expire_in_days: Some(3),
                directory: None,
            }),
        ));

        expect!(parse(&["mprovision", "list", "-d", "3"])).to(be_ok().value(Command::List(
            ListParams {
                text: None,
                expire_in_days: Some(3),
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "list", "--expire-in-days", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "-d", "-3"])).to(be_err());
        expect!(parse(&["mprovision", "list", "--expire-in-days", "366"])).to(be_err());
        expect!(parse(&["mprovision", "list", "-d", "366"])).to(be_err());

        expect!(parse(&[
            "mprovision",
            "list",
            "--text",
            "abc",
            "--expire-in-days",
            "3",
            "--source",
            ".",
        ])).to(be_ok().value(Command::List(ListParams {
            text: Some("abc".to_string()),
            expire_in_days: Some(3),
            directory: Some(".".into()),
        })));

        expect!(parse(&[
            "mprovision",
            "list",
            "-t",
            "abc",
            "-d",
            "3",
            "--source",
            ".",
        ])).to(be_ok().value(Command::List(ListParams {
            text: Some("abc".to_string()),
            expire_in_days: Some(3),
            directory: Some(".".into()),
        })));
    }

    #[test]
    fn show_uuid_command() {
        expect!(parse(&["mprovision", "show", "abcd"])).to(be_ok().value(Command::ShowUuid(
            ShowUuidParams {
                uuid: "abcd".to_string(),
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "show", ""])).to(be_err());

        expect!(parse(&["mprovision", "show", "abcd", "--source", "."])).to(be_ok().value(
            Command::ShowUuid(ShowUuidParams {
                uuid: "abcd".to_string(),
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&["mprovision", "show", "abcd", "--source", ""])).to(be_err());
    }

    #[test]
    fn show_file_command() {
        expect!(parse(&["mprovision", "show-file", "file.mprovision"])).to(be_ok().value(
            Command::ShowFile(ShowFileParams {
                file: "file.mprovision".into(),
            }),
        ));

        expect!(parse(&["mprovision", "show-file", "file.mprovision", "."])).to(be_err());

        expect!(parse(&["mprovision", "show-file", ""])).to(be_err());
    }

    #[test]
    fn remove_uuid_command() {
        expect!(parse(&["mprovision", "remove", "abcd"])).to(be_ok().value(Command::Remove(
            RemoveParams {
                uuids: vec!["abcd".to_string()],
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "remove", "abcd", "ef"])).to(be_ok().value(Command::Remove(
            RemoveParams {
                uuids: vec!["abcd".to_string(), "ef".to_string()],
                directory: None,
            },
        )));

        expect!(parse(&["mprovision", "remove", ""])).to(be_err());

        expect!(parse(&["mprovision", "remove", "abcd", "--source", "."])).to(be_ok().value(
            Command::Remove(RemoveParams {
                uuids: vec!["abcd".to_string()],
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&[
            "mprovision",
            "remove",
            "abcd",
            "ef",
            "--source",
            ".",
        ])).to(be_ok().value(Command::Remove(RemoveParams {
            uuids: vec!["abcd".to_string(), "ef".to_string()],
            directory: Some(".".into()),
        })));

        expect!(parse(&["mprovision", "remove", "abcd", "--source", ""])).to(be_err());
    }

    #[test]
    fn clean_command() {
        expect!(parse(&["mprovision", "clean"]))
            .to(be_ok().value(Command::Clean(CleanParams { directory: None })));

        expect!(parse(&["mprovision", "clean", "--source", "."])).to(be_ok().value(
            Command::Clean(CleanParams {
                directory: Some(".".into()),
            }),
        ));

        expect!(parse(&["mprovision", "clean", "--source", ""])).to(be_err());
    }
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Gaussian_elimination
#![allow(unused_variables)]
#![allow(dead_code)]

struct Matrix<'a, T:'a> where T: Copy {
	m : &'a mut Vec<T>,
	n : usize
}

struct Idx(usize, usize);

impl<'a, T:'a> Matrix<'a, T> where T: Copy {
	fn new<'b> (_m : &'b mut Vec<T>, _n : usize) -> Matrix<'b, T> {
		Matrix{m : /*&mut*/ _m, n : _n}
	}

	fn val(&self, idx : Idx) -> & T
	{
		& self.m[idx.0 + idx.1 * self.n]
	}

	// fn val_mut<'b>(&'b mut self, idx : Idx) -> &'b mut T
	// {
	// 	let r : &mut T =	& mut (*self.m)[idx.0 + idx.1 * self.n];
	// 	r
	// }

	fn set(&mut self, idx : Idx, v : T)
	{
		self.m[idx.0 + idx.1 * self.n] = v;
	}

	fn get_row_slice(&self, row: usize) -> &[T]
	{
	  & self.m[row * self.n .. row * self.n + self.n ]
	}
}

struct MatrixRowIter<'a, T:'a> where T: Copy {
	m : &'a Matrix<'a, T>,
	row : usize,
	col : usize,
	used: &'a Vec<bool>
}

impl<'a, T> MatrixRowIter<'a, T> where T: Copy {
	fn new (_m : &'a Matrix<'a, T>, _used: &'a mut Vec<bool>, _col : usize) -> MatrixRowIter<'a, T> {
		MatrixRowIter{m : _m, row : 0, col : _col, used : _used }
	}

}

impl<'a, T> Iterator for MatrixRowIter<'a, T> where T: Copy {
    type Item = (T, usize);
    fn next(&mut self) -> Option<(T, usize)> {
    	loop {
	    	if self.row >= self.m.n {
	    	  return None
	    	}
	    	if !self.used[self.row] {
	    		break;
	    	}
    		self.row += 1;
    	}

    	let v = *self.m.val(Idx(self.col, self.row));
    	let ret = self.row;
    	self.row += 1;
      Some((v, ret))
    }
}

fn main() {

	let mut v : Vec<f32> = vec![
		1.00, 0.00, 0.00,  0.00,  0.00, 0.00,
		1.00, 0.63, 0.39,  0.25,  0.16, 0.10,
		1.00, 1.26, 1.58,  1.98,  2.49, 3.13,
		1.00, 1.88, 3.55,  6.70, 12.62, 230.80,
		1.00, 2.51, 6.32, 15.88, 39.90, 100.28,
		1.00, 3.14, 9.87, 31.01, 97.41, 306.02];

	let mat = Matrix::new(&mut v, 6);

	let zero:f32 = 0.0;
	let mut r = Vec::new();
	let mut used: Vec<bool> = vec![false; mat.n];

	for i in 0..mat.n {

		let max;
		{
			let mat_iter = MatrixRowIter::new(&mat, &mut used, i);

			max = mat_iter
						.inspect(|&item| println!("{} {}", item.0, item.1) )
						.fold((0.0, -1),
							|max, item| if item.0.abs() > max.0 { item } else { max } )
						;
		}

		used[max.1] = true;
		println!("max {} {}", max.0, max.1);
		println!("=======");

		r.extend(mat.get_row_slice(max.1).iter());
	}

	let mut mat_out = Matrix::new(&mut r, mat.n);
	for i in 0..mat_out.n {
		for j in 0..mat_out.n {
			print!("{} ", mat_out.val(Idx(j, i)));
		}
		println!("  ");
	}

	let n= mat_out.n;
	for dia in 0..n {
		for row in dia+1..n {
			//let tmp = *mat_out.val(Idx(dia, row)) / *mat_out.val(Idx(dia, dia));
			for col in dia+1..n {
				//(*mat_out.val(Idx(col, row))) -= tmp;// * *mat_out.val(Idx(col, dia));
				//mat_out.val_mut(Idx(dia, row)) = 0.0;
				//let ref mut  rr : &mut &f32 = mat_out.val_mut(Idx(dia, row));
//				let rr : & mut f32   = &mut mat_out.val_mut(Idx(dia, row));
	//			println!("{}", rr);
				//*mat_out.val_mut(Idx(dia, row)) = 0.0;
				mat_out.set(Idx(dia, row), &zero);
				//*rr = 0.0;
			}
		}
	}

}
<commit_msg>reference confusing<commit_after>// http://rosettacode.org/wiki/Gaussian_elimination
#![allow(unused_variables)]
#![allow(dead_code)]

struct Idx(usize, usize);

struct Matrix<'a, T:'a> where T: Copy {
	m : &'a mut Vec<T>,
	n : usize
}

impl<'a, T:'a> Matrix<'a, T> where T: Copy {
	fn new<'b> (_m : &'b mut Vec<T>, _n : usize) -> Matrix<'b, T> {
		Matrix{m : /*&mut*/ _m, n : _n}
	}

	fn val(&self, idx : Idx) -> & T
	{
		& self.m[idx.0 + idx.1 * self.n]
	}

	// fn val_mut<'b>(&'b mut self, idx : Idx) -> &'b mut T
	// {
	// 	let r : &mut T =	& mut (*self.m)[idx.0 + idx.1 * self.n];
	// 	r
	// }

	fn set(&mut self, idx : Idx, v : T) where T: Copy
	{
		self.m[idx.0 + idx.1 * self.n] = *v;
	}

	fn get_row_slice(&self, row: usize) -> &[T]
	{
	  & self.m[row * self.n .. row * self.n + self.n ]
	}
}

struct MatrixRowIter<'a, T:'a> where T: Copy {
	m : &'a Matrix<'a, T>,
	row : usize,
	col : usize,
	used: &'a Vec<bool>
}

impl<'a, T> MatrixRowIter<'a, T> where T: Copy {
	fn new (_m : &'a Matrix<'a, T>, _used: &'a mut Vec<bool>, _col : usize) -> MatrixRowIter<'a, T> {
		MatrixRowIter{m : _m, row : 0, col : _col, used : _used }
	}

}

impl<'a, T> Iterator for MatrixRowIter<'a, T> where T: Copy {
    type Item = (T, usize);
    fn next(&mut self) -> Option<(T, usize)> {
    	loop {
	    	if self.row >= self.m.n {
	    	  return None
	    	}
	    	if !self.used[self.row] {
	    		break;
	    	}
    		self.row += 1;
    	}

    	let v = *self.m.val(Idx(self.col, self.row));
    	let ret = self.row;
    	self.row += 1;
      Some((v, ret))
    }
}

fn main() {

	let mut v : Vec<f32> = vec![
		1.00, 0.00, 0.00,  0.00,  0.00, 0.00,
		1.00, 0.63, 0.39,  0.25,  0.16, 0.10,
		1.00, 1.26, 1.58,  1.98,  2.49, 3.13,
		1.00, 1.88, 3.55,  6.70, 12.62, 230.80,
		1.00, 2.51, 6.32, 15.88, 39.90, 100.28,
		1.00, 3.14, 9.87, 31.01, 97.41, 306.02];

	let mat = Matrix::new(&mut v, 6);

	let zero:f32 = 0.0;
	let mut r = Vec::new();
	let mut used: Vec<bool> = vec![false; mat.n];

	for i in 0..mat.n {

		let max;
		{
			let mat_iter = MatrixRowIter::new(&mat, &mut used, i);

			max = mat_iter
						.inspect(|&item| println!("{} {}", item.0, item.1) )
						.fold((0.0, -1),
							|max, item| if item.0.abs() > max.0 { item } else { max } )
						;
		}

		used[max.1] = true;
		println!("max {} {}", max.0, max.1);
		println!("=======");

		r.extend(mat.get_row_slice(max.1).iter());
	}

	let mut mat_out = Matrix::new(&mut r, mat.n);
	for i in 0..mat_out.n {
		for j in 0..mat_out.n {
			print!("{} ", mat_out.val(Idx(j, i)));
		}
		println!("  ");
	}

	println!("========================");

	let n= mat_out.n;
	for dia in 0..n {
		for row in dia+1..n {
			//let tmp = *mat_out.val(Idx(dia, row)) / *mat_out.val(Idx(dia, dia));
			for col in dia+1..n {
				//(*mat_out.val(Idx(col, row))) -= tmp;// * *mat_out.val(Idx(col, dia));
				//mat_out.val_mut(Idx(dia, row)) = 0.0;
				//let ref mut  rr : &mut &f32 = mat_out.val_mut(Idx(dia, row));
//				let rr : & mut f32   = &mut mat_out.val_mut(Idx(dia, row));
	//			println!("{}", rr);
				//*mat_out.val_mut(Idx(dia, row)) = 0.0;
				//mat_out.set(Idx(dia, row), &zero);
				mat_out.set(Idx(dia, row), 0.0);
				//*rr = 0.0;
			}
		}
	}

	for i in 0..mat_out.n {
		for j in 0..mat_out.n {
			print!("{} ", mat_out.val(Idx(j, i)));
		}
		println!("  ");
	}

}
<|endoftext|>


<commit_before>extern crate errno;
extern crate libc;

#[cfg(target_os = "macos")]
use std::fmt;
#[cfg(target_os = "macos")]
use std::{mem, ptr};

#[cfg(target_os = "macos")]
use crate::libproc::helpers;

#[cfg(target_os = "macos")]
use self::libc::c_int;

#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::{BufRead, BufReader};
#[cfg(target_os = "linux")]
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use std::sync::mpsc::Receiver;
#[cfg(target_os = "linux")]
use std::{thread, time};

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
#[cfg(target_os = "macos")]
const MAX_MSG_BSIZE: c_int = 1024 * 1024;
#[cfg(target_os = "macos")]
const MSG_MAGIC: c_int = 0x063_061;

// See /usr/include/sys/msgbuf.h on your Mac.
#[cfg(target_os = "macos")]
#[repr(C)]
struct MessageBuffer {
    pub msg_magic: c_int,
    pub msg_size: c_int,
    pub msg_bufx: c_int,
    // write pointer
    pub msg_bufr: c_int,
    // read pointer
    pub msg_bufc: *mut u8,     // buffer
}

#[cfg(target_os = "macos")]
impl Default for MessageBuffer {
    fn default() -> MessageBuffer {
        MessageBuffer {
            msg_magic: 0,
            msg_size: 0,
            msg_bufx: 0,
            msg_bufr: 0,
            msg_bufc: ptr::null_mut() as *mut u8,
        }
    }
}

#[cfg(target_os = "macos")]
impl fmt::Debug for MessageBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MessageBuffer {{ magic: 0x{:x}, size: {}, bufx: {}}}", self.msg_magic, self.msg_size, self.msg_bufx)
    }
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer: *mut MessageBuffer, buffersize: u32) -> c_int;
}

/// faclev,seqnum,timestamp[optional, ...];message\n
///  TAGNAME=value
///  TAGNAME=value
/// Get a message from the kernel message buffer - as used by dmesg
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer: MessageBuffer = Default::default();
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(&mut message_buffer, mem::size_of::<MessageBuffer>() as u32);
    }

    if ret <= 0 {
        Err(helpers::get_errno_with_message(ret))
    } else if message_buffer.msg_magic != MSG_MAGIC {
        println!("Message buffer: {:?}", message_buffer);
        Err(format!("The magic number 0x{:x} is incorrect", message_buffer.msg_magic))
    } else {
        // Avoid starting beyond the end of the buffer
        if message_buffer.msg_bufx >= MAX_MSG_BSIZE {
            message_buffer.msg_bufx = 0;
        }
        let mut output: Vec<u8> = Vec::new();

        // The message buffer is circular; start at the read pointer, and go to the write pointer - 1.
        unsafe {
            let mut ch: u8;
            let mut p: *mut u8 = message_buffer.msg_bufc.offset(message_buffer.msg_bufx as isize);
            let ep: *mut u8 = message_buffer.msg_bufc.offset((message_buffer.msg_bufx - 1) as isize);

            while p != ep {
                // If at the end, then loop around to the start
                // TODO should use actual size (from struct element) - not the max size??
                if p == message_buffer.msg_bufc.offset(MAX_MSG_BSIZE as isize) {
                    p = message_buffer.msg_bufc;
                }

                ch = *p;
                output.push(ch);
                p = p.offset(1);
            }

            Ok(String::from_utf8(output).map_err(|_| "Could not convert to UTF-8")?)
        }
    }
}

#[cfg(target_os = "linux")]
pub fn kmsgbuf() -> Result<String, String> {
    let file = File::open("/dev/kmsg").map_err(|_| "Could not open /dev/kmsg file '{}'")?;
    let kmsg_channel = spawn_kmsg_channel(file);
    let duration = time::Duration::from_millis(1);
    let mut buf = String::new();
    while let Ok(line) = kmsg_channel.recv_timeout(duration) {
            buf.push_str(&line)
    }

    Ok(buf)
}

#[cfg(target_os = "linux")]
fn spawn_kmsg_channel(file: File) -> Receiver<String> {
    let mut reader = BufReader::new(file);
    let (tx, rx) = mpsc::channel::<String>();
    thread::spawn(move || loop {
        let mut line = String::new();
        let _len = reader.read_line(&mut line).unwrap();
        println!("{}", line);
        tx.send(line).unwrap();
    });
    rx
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;

    use crate::libproc::proc_pid::am_root;

    use super::kmsgbuf;

    #[test]
    #[ignore]
    // TODO fix on macos: an error message is returned - https://github.com/andrewdavidmackenzie/libproc-rs/issues/39
    // Message buffer: MessageBuffer { magic: 0x3a657461, size: 1986947360, bufx: 1684630625}
    // thread 'libproc::kmesg_buffer::test::kmessagebuffer_test' panicked at 'The magic number 0x3a657461 is incorrect', src/libproc/kmesg_buffer.rs:194:33
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(buffer) => println!("Buffer: {:?}", buffer),
                Err(message) => panic!(message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<commit_msg>Handle mroe errors in kmsg_buf() on linux<commit_after>extern crate errno;
extern crate libc;

#[cfg(target_os = "macos")]
use std::fmt;
#[cfg(target_os = "macos")]
use std::{mem, ptr};

#[cfg(target_os = "macos")]
use crate::libproc::helpers;

#[cfg(target_os = "macos")]
use self::libc::c_int;

#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::{BufRead, BufReader};
#[cfg(target_os = "linux")]
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use std::sync::mpsc::Receiver;
#[cfg(target_os = "linux")]
use std::{thread, time};

// See https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/msgbuf.h
#[cfg(target_os = "macos")]
const MAX_MSG_BSIZE: c_int = 1024 * 1024;
#[cfg(target_os = "macos")]
const MSG_MAGIC: c_int = 0x063_061;

// See /usr/include/sys/msgbuf.h on your Mac.
#[cfg(target_os = "macos")]
#[repr(C)]
struct MessageBuffer {
    pub msg_magic: c_int,
    pub msg_size: c_int,
    pub msg_bufx: c_int,
    // write pointer
    pub msg_bufr: c_int,
    // read pointer
    pub msg_bufc: *mut u8,     // buffer
}

#[cfg(target_os = "macos")]
impl Default for MessageBuffer {
    fn default() -> MessageBuffer {
        MessageBuffer {
            msg_magic: 0,
            msg_size: 0,
            msg_bufx: 0,
            msg_bufr: 0,
            msg_bufc: ptr::null_mut() as *mut u8,
        }
    }
}

#[cfg(target_os = "macos")]
impl fmt::Debug for MessageBuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MessageBuffer {{ magic: 0x{:x}, size: {}, bufx: {}}}", self.msg_magic, self.msg_size, self.msg_bufx)
    }
}

// this extern block links to the libproc library
// Original signatures of functions can be found at http://opensource.apple.com/source/Libc/Libc-594.9.4/darwin/libproc.c
#[cfg(target_os = "macos")]
#[link(name = "proc", kind = "dylib")]
extern {
    fn proc_kmsgbuf(buffer: *mut MessageBuffer, buffersize: u32) -> c_int;
}

()/// Get the contents of the kernel message buffer
///
/// Entries are in the format:
/// faclev,seqnum,timestamp[optional, ...];message\n
///  TAGNAME=value (0 or more Tags)
// See http://opensource.apple.com//source/system_cmds/system_cmds-336.6/dmesg.tproj/dmesg.c
#[cfg(target_os = "macos")]
pub fn kmsgbuf() -> Result<String, String> {
    let mut message_buffer: MessageBuffer = Default::default();
    let ret: i32;

    unsafe {
        ret = proc_kmsgbuf(&mut message_buffer, mem::size_of::<MessageBuffer>() as u32);
    }

    if ret <= 0 {
        Err(helpers::get_errno_with_message(ret))
    } else if message_buffer.msg_magic != MSG_MAGIC {
        println!("Message buffer: {:?}", message_buffer);
        Err(format!("The magic number 0x{:x} is incorrect", message_buffer.msg_magic))
    } else {
        // Avoid starting beyond the end of the buffer
        if message_buffer.msg_bufx >= MAX_MSG_BSIZE {
            message_buffer.msg_bufx = 0;
        }
        let mut output: Vec<u8> = Vec::new();

        // The message buffer is circular; start at the read pointer, and go to the write pointer - 1.
        unsafe {
            let mut ch: u8;
            let mut p: *mut u8 = message_buffer.msg_bufc.offset(message_buffer.msg_bufx as isize);
            let ep: *mut u8 = message_buffer.msg_bufc.offset((message_buffer.msg_bufx - 1) as isize);

            while p != ep {
                // If at the end, then loop around to the start
                // TODO should use actual size (from struct element) - not the max size??
                if p == message_buffer.msg_bufc.offset(MAX_MSG_BSIZE as isize) {
                    p = message_buffer.msg_bufc;
                }

                ch = *p;
                output.push(ch);
                p = p.offset(1);
            }

            Ok(String::from_utf8(output).map_err(|_| "Could not convert to UTF-8")?)
        }
    }
}

// Turns out that reading to the end of an "infinite file" like "/dev/kmsg" with standard file
// reading methods will block at the end of file, so a workaround is required. Do the blocking
// reads on a thread that sends lines read back through a channel, and then return when the thread
// has blocked and can't send anymore. Returning will end the thread and the channel.
#[cfg(target_os = "linux")]
pub fn kmsgbuf() -> Result<String, String> {
    let file = File::open("/dev/kmsg").map_err(|_| "Could not open /dev/kmsg file '{}'")?;
    let kmsg_channel = spawn_kmsg_channel(file);
    let duration = time::Duration::from_millis(1);
    let mut buf = String::new();
    while let Ok(line) = kmsg_channel.recv_timeout(duration) {
            buf.push_str(&line)
    }

    Ok(buf)
}

// Create a channel to return lines read from a file on, then create a thread that reads the lines
// and sends them back on the channel one by one. Eventually it will get to EOF or block
#[cfg(target_os = "linux")]
fn spawn_kmsg_channel(file: File) -> Receiver<String> {
    let mut reader = BufReader::new(file);
    let (tx, rx) = mpsc::channel::<String>();
    thread::spawn(move || loop {
        let mut line = String::new();
        match reader.read_line(&mut line) {
            Ok(_) => {
                if tx.send(line).is_err() { break }
            },
            _ => break
        }
    });

    rx
}

#[cfg(test)]
mod test {
    use std::io;
    use std::io::Write;

    use crate::libproc::proc_pid::am_root;

    use super::kmsgbuf;

    #[test]
    #[ignore]
    // TODO fix on macos: an error message is returned - https://github.com/andrewdavidmackenzie/libproc-rs/issues/39
    // Message buffer: MessageBuffer { magic: 0x3a657461, size: 1986947360, bufx: 1684630625}
    // thread 'libproc::kmesg_buffer::test::kmessagebuffer_test' panicked at 'The magic number 0x3a657461 is incorrect', src/libproc/kmesg_buffer.rs:194:33
    fn kmessagebuffer_test() {
        if am_root() {
            match kmsgbuf() {
                Ok(buffer) => println!("Buffer: {:?}", buffer),
                Err(message) => panic!(message)
            }
        } else {
            writeln!(&mut io::stdout(), "test libproc::kmesg_buffer::kmessagebuffer_test ... skipped as it needs to be run as root").unwrap();
        }
    }
}<|endoftext|>


<commit_before>use std::map::HashMap;

use ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt,
             tt_delim, tt_tok, item_mac};
use fold::*;
use ext::base::*;
use ext::qquote::{qq_helper};
use parse::{parser, parse_expr_from_source_str, new_parser_from_tts};

use codemap::{span, ExpandedFrom};

fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
               e: expr_, s: span, fld: ast_fold,
               orig: fn@(expr_, span, ast_fold) -> (expr_, span))
    -> (expr_, span)
{
    return match e {
      // expr_mac should really be expr_ext or something; it's the
      // entry-point for all syntax extensions.
          expr_mac(mac) => {

            // Old-style macros, for compatibility, will erase this whole
            // block once we've transitioned.
            match mac.node {
              mac_invoc(pth, args, body) => {
                assert (vec::len(pth.idents) > 0u);
                /* using idents and token::special_idents would make the
                the macro names be hygienic */
                let extname = cx.parse_sess().interner.get(pth.idents[0]);
                match exts.find(*extname) {
                  None => {
                    cx.span_fatal(pth.span,
                                  fmt!("macro undefined: '%s'", *extname))
                  }
                  Some(item_decorator(_)) => {
                    cx.span_fatal(
                        pth.span,
                        fmt!("%s can only be used as a decorator", *extname));
                  }
                  Some(normal({expander: exp, span: exp_sp})) => {
                    let expanded = exp(cx, mac.span, args, body);

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  Some(macro_defining(ext)) => {
                    let named_extension = ext(cx, mac.span, args, body);
                    exts.insert(named_extension.name, named_extension.ext);
                    (ast::expr_rec(~[], None), s)
                  }
                  Some(expr_tt(_)) => {
                    cx.span_fatal(pth.span,
                                  fmt!("this tt-style macro should be \
                                        invoked '%s!(...)'", *extname))
                  }
                  Some(item_tt(*)) => {
                    cx.span_fatal(pth.span,
                                  ~"cannot use item macros in this context");
                  }
                }
              }

              // Token-tree macros, these will be the only case when we're
              // finished transitioning.
              mac_invoc_tt(pth, tts) => {
                assert (vec::len(pth.idents) == 1u);
                /* using idents and token::special_idents would make the
                the macro names be hygienic */
                let extname = cx.parse_sess().interner.get(pth.idents[0]);
                match exts.find(*extname) {
                  None => {
                    cx.span_fatal(pth.span,
                                  fmt!("macro undefined: '%s'", *extname))
                  }
                  Some(expr_tt({expander: exp, span: exp_sp})) => {
                    let expanded = match exp(cx, mac.span, tts) {
                      mr_expr(e) => e,
                      mr_expr_or_item(expr_maker,_) => expr_maker(),
                      _ => cx.span_fatal(
                          pth.span, fmt!("non-expr macro in expr pos: %s",
                                         *extname))
                    };

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  Some(normal({expander: exp, span: exp_sp})) => {
                    //convert the new-style invoc for the old-style macro
                    let arg = base::tt_args_to_original_flavor(cx, pth.span,
                                                               tts);
                    let expanded = exp(cx, mac.span, arg, None);

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  _ => {
                    cx.span_fatal(pth.span,
                                  fmt!("'%s' is not a tt-style macro",
                                       *extname))
                  }

                }
              }
              _ => cx.span_bug(mac.span, ~"naked syntactic bit")
            }
          }
          _ => orig(e, s, fld)
        };
}

// This is a secondary mechanism for invoking syntax extensions on items:
// "decorator" attributes, such as #[auto_serialize]. These are invoked by an
// attribute prefixing an item, and are interpreted by feeding the item
// through the named attribute _as a syntax extension_ and splicing in the
// resulting item vec into place in favour of the decorator. Note that
// these do _not_ work for macro extensions, just item_decorator ones.
//
// NB: there is some redundancy between this and expand_item, below, and
// they might benefit from some amount of semantic and language-UI merger.
fn expand_mod_items(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
                    module_: ast::_mod, fld: ast_fold,
                    orig: fn@(ast::_mod, ast_fold) -> ast::_mod)
    -> ast::_mod
{
    // Fold the contents first:
    let module_ = orig(module_, fld);

    // For each item, look through the attributes.  If any of them are
    // decorated with "item decorators", then use that function to transform
    // the item into a new set of items.
    let new_items = do vec::flat_map(module_.items) |item| {
        do vec::foldr(item.attrs, ~[*item]) |attr, items| {
            let mname = match attr.node.value.node {
              ast::meta_word(n) => n,
              ast::meta_name_value(n, _) => n,
              ast::meta_list(n, _) => n
            };
            match exts.find(mname) {
              None | Some(normal(_)) | Some(macro_defining(_))
              | Some(expr_tt(_)) | Some(item_tt(*)) => items,
              Some(item_decorator(dec_fn)) => {
                dec_fn(cx, attr.span, attr.node.value, items)
              }
            }
        }
    };

    return {items: new_items, ..module_};
}

// When we enter a module, record it, for the sake of `module!`
fn expand_item(exts: HashMap<~str, syntax_extension>,
               cx: ext_ctxt, &&it: @ast::item, fld: ast_fold,
               orig: fn@(&&v: @ast::item, ast_fold) -> Option<@ast::item>)
    -> Option<@ast::item>
{
    let is_mod = match it.node {
      ast::item_mod(_) | ast::item_foreign_mod(_) => true,
      _ => false
    };
    let maybe_it = match it.node {
      ast::item_mac(*) => expand_item_mac(exts, cx, it, fld),
      _ => Some(it)
    };

    match maybe_it {
      Some(it) => {
        if is_mod { cx.mod_push(it.ident); }
        let ret_val = orig(it, fld);
        if is_mod { cx.mod_pop(); }
        return ret_val;
      }
      None => return None
    }
}

// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
fn expand_item_mac(exts: HashMap<~str, syntax_extension>,
                   cx: ext_ctxt, &&it: @ast::item,
                   fld: ast_fold) -> Option<@ast::item> {
    match it.node {
      item_mac({node: mac_invoc_tt(pth, tts), _}) => {
        let extname = cx.parse_sess().interner.get(pth.idents[0]);
        match exts.find(*extname) {
          None => {
            cx.span_fatal(pth.span,
                          fmt!("macro undefined: '%s'", *extname))
          }
          Some(item_tt(expand)) => {
            let expanded = expand.expander(cx, it.span, it.ident, tts);
            cx.bt_push(ExpandedFrom({call_site: it.span,
                                      callie: {name: *extname,
                                               span: expand.span}}));
            let maybe_it = match expanded {
              mr_item(it) => fld.fold_item(it),
              mr_expr(_) => cx.span_fatal(pth.span,
                                         ~"expr macro in item position: " +
                                         *extname),
              mr_expr_or_item(_, item_maker) =>
                option::chain(item_maker(), |i| {fld.fold_item(i)}),
              mr_def(mdef) => {
                exts.insert(mdef.name, mdef.ext);
                None
              }
            };
            cx.bt_pop();
            return maybe_it
          }
          _ => cx.span_fatal(it.span,
                            fmt!("%s is not a legal here", *extname))
        }
      }
      _ => cx.span_bug(it.span, ~"invalid item macro invocation")
    }
}

fn new_span(cx: ext_ctxt, sp: span) -> span {
    /* this discards information in the case of macro-defining macros */
    return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
}

// FIXME (#2247): this is a terrible kludge to inject some macros into
// the default compilation environment. When the macro-definition system
// is substantially more mature, these should move from here, into a
// compiled part of libcore at very least.

fn core_macros() -> ~str {
    return
~"{
    macro_rules! ignore (($($x:tt)*) => (()))
    #macro[[#error[f, ...], log(core::error, #fmt[f, ...])]];
    #macro[[#warn[f, ...], log(core::warn, #fmt[f, ...])]];
    #macro[[#info[f, ...], log(core::info, #fmt[f, ...])]];
    #macro[[#debug[f, ...], log(core::debug, #fmt[f, ...])]];

    macro_rules! die(
        ($msg: expr) => (
            {
                do core::str::as_buf($msg) |msg_buf, _msg_len| {
                    do core::str::as_buf(file!()) |file_buf, _file_len| {
                        unsafe {
                            let msg_buf = core::cast::transmute(msg_buf);
                            let file_buf = core::cast::transmute(file_buf);
                            let line = line!() as core::libc::size_t;
                            core::rt::rt_fail_(msg_buf, file_buf, line)
                        }
                    }
                }
            }
        );
        () => (
            die!(\"explicit failure\")
        )
    )
}";
}

fn expand_crate(parse_sess: parse::parse_sess,
                cfg: ast::crate_cfg, c: @crate) -> @crate {
    let exts = syntax_expander_table();
    let afp = default_ast_fold();
    let cx: ext_ctxt = mk_ctxt(parse_sess, cfg);
    let f_pre =
        @{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr),
          fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod),
          fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item),
          new_span: |a|new_span(cx, a),
          .. *afp};
    let f = make_fold(f_pre);
    let cm = parse_expr_from_source_str(~"<core-macros>",
                                        @core_macros(),
                                        cfg,
                                        parse_sess);

    // This is run for its side-effects on the expander env,
    // as it registers all the core macros as expanders.
    f.fold_expr(cm);

    let res = @f.fold_crate(*c);
    return res;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Grammaticalinate an error message.<commit_after>use std::map::HashMap;

use ast::{crate, expr_, expr_mac, mac_invoc, mac_invoc_tt,
             tt_delim, tt_tok, item_mac};
use fold::*;
use ext::base::*;
use ext::qquote::{qq_helper};
use parse::{parser, parse_expr_from_source_str, new_parser_from_tts};

use codemap::{span, ExpandedFrom};

fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
               e: expr_, s: span, fld: ast_fold,
               orig: fn@(expr_, span, ast_fold) -> (expr_, span))
    -> (expr_, span)
{
    return match e {
      // expr_mac should really be expr_ext or something; it's the
      // entry-point for all syntax extensions.
          expr_mac(mac) => {

            // Old-style macros, for compatibility, will erase this whole
            // block once we've transitioned.
            match mac.node {
              mac_invoc(pth, args, body) => {
                assert (vec::len(pth.idents) > 0u);
                /* using idents and token::special_idents would make the
                the macro names be hygienic */
                let extname = cx.parse_sess().interner.get(pth.idents[0]);
                match exts.find(*extname) {
                  None => {
                    cx.span_fatal(pth.span,
                                  fmt!("macro undefined: '%s'", *extname))
                  }
                  Some(item_decorator(_)) => {
                    cx.span_fatal(
                        pth.span,
                        fmt!("%s can only be used as a decorator", *extname));
                  }
                  Some(normal({expander: exp, span: exp_sp})) => {
                    let expanded = exp(cx, mac.span, args, body);

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  Some(macro_defining(ext)) => {
                    let named_extension = ext(cx, mac.span, args, body);
                    exts.insert(named_extension.name, named_extension.ext);
                    (ast::expr_rec(~[], None), s)
                  }
                  Some(expr_tt(_)) => {
                    cx.span_fatal(pth.span,
                                  fmt!("this tt-style macro should be \
                                        invoked '%s!(...)'", *extname))
                  }
                  Some(item_tt(*)) => {
                    cx.span_fatal(pth.span,
                                  ~"cannot use item macros in this context");
                  }
                }
              }

              // Token-tree macros, these will be the only case when we're
              // finished transitioning.
              mac_invoc_tt(pth, tts) => {
                assert (vec::len(pth.idents) == 1u);
                /* using idents and token::special_idents would make the
                the macro names be hygienic */
                let extname = cx.parse_sess().interner.get(pth.idents[0]);
                match exts.find(*extname) {
                  None => {
                    cx.span_fatal(pth.span,
                                  fmt!("macro undefined: '%s'", *extname))
                  }
                  Some(expr_tt({expander: exp, span: exp_sp})) => {
                    let expanded = match exp(cx, mac.span, tts) {
                      mr_expr(e) => e,
                      mr_expr_or_item(expr_maker,_) => expr_maker(),
                      _ => cx.span_fatal(
                          pth.span, fmt!("non-expr macro in expr pos: %s",
                                         *extname))
                    };

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  Some(normal({expander: exp, span: exp_sp})) => {
                    //convert the new-style invoc for the old-style macro
                    let arg = base::tt_args_to_original_flavor(cx, pth.span,
                                                               tts);
                    let expanded = exp(cx, mac.span, arg, None);

                    cx.bt_push(ExpandedFrom({call_site: s,
                                callie: {name: *extname, span: exp_sp}}));
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    (fully_expanded, s)
                  }
                  _ => {
                    cx.span_fatal(pth.span,
                                  fmt!("'%s' is not a tt-style macro",
                                       *extname))
                  }

                }
              }
              _ => cx.span_bug(mac.span, ~"naked syntactic bit")
            }
          }
          _ => orig(e, s, fld)
        };
}

// This is a secondary mechanism for invoking syntax extensions on items:
// "decorator" attributes, such as #[auto_serialize]. These are invoked by an
// attribute prefixing an item, and are interpreted by feeding the item
// through the named attribute _as a syntax extension_ and splicing in the
// resulting item vec into place in favour of the decorator. Note that
// these do _not_ work for macro extensions, just item_decorator ones.
//
// NB: there is some redundancy between this and expand_item, below, and
// they might benefit from some amount of semantic and language-UI merger.
fn expand_mod_items(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
                    module_: ast::_mod, fld: ast_fold,
                    orig: fn@(ast::_mod, ast_fold) -> ast::_mod)
    -> ast::_mod
{
    // Fold the contents first:
    let module_ = orig(module_, fld);

    // For each item, look through the attributes.  If any of them are
    // decorated with "item decorators", then use that function to transform
    // the item into a new set of items.
    let new_items = do vec::flat_map(module_.items) |item| {
        do vec::foldr(item.attrs, ~[*item]) |attr, items| {
            let mname = match attr.node.value.node {
              ast::meta_word(n) => n,
              ast::meta_name_value(n, _) => n,
              ast::meta_list(n, _) => n
            };
            match exts.find(mname) {
              None | Some(normal(_)) | Some(macro_defining(_))
              | Some(expr_tt(_)) | Some(item_tt(*)) => items,
              Some(item_decorator(dec_fn)) => {
                dec_fn(cx, attr.span, attr.node.value, items)
              }
            }
        }
    };

    return {items: new_items, ..module_};
}

// When we enter a module, record it, for the sake of `module!`
fn expand_item(exts: HashMap<~str, syntax_extension>,
               cx: ext_ctxt, &&it: @ast::item, fld: ast_fold,
               orig: fn@(&&v: @ast::item, ast_fold) -> Option<@ast::item>)
    -> Option<@ast::item>
{
    let is_mod = match it.node {
      ast::item_mod(_) | ast::item_foreign_mod(_) => true,
      _ => false
    };
    let maybe_it = match it.node {
      ast::item_mac(*) => expand_item_mac(exts, cx, it, fld),
      _ => Some(it)
    };

    match maybe_it {
      Some(it) => {
        if is_mod { cx.mod_push(it.ident); }
        let ret_val = orig(it, fld);
        if is_mod { cx.mod_pop(); }
        return ret_val;
      }
      None => return None
    }
}

// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
fn expand_item_mac(exts: HashMap<~str, syntax_extension>,
                   cx: ext_ctxt, &&it: @ast::item,
                   fld: ast_fold) -> Option<@ast::item> {
    match it.node {
      item_mac({node: mac_invoc_tt(pth, tts), _}) => {
        let extname = cx.parse_sess().interner.get(pth.idents[0]);
        match exts.find(*extname) {
          None => {
            cx.span_fatal(pth.span,
                          fmt!("macro undefined: '%s'", *extname))
          }
          Some(item_tt(expand)) => {
            let expanded = expand.expander(cx, it.span, it.ident, tts);
            cx.bt_push(ExpandedFrom({call_site: it.span,
                                      callie: {name: *extname,
                                               span: expand.span}}));
            let maybe_it = match expanded {
              mr_item(it) => fld.fold_item(it),
              mr_expr(_) => cx.span_fatal(pth.span,
                                         ~"expr macro in item position: " +
                                         *extname),
              mr_expr_or_item(_, item_maker) =>
                option::chain(item_maker(), |i| {fld.fold_item(i)}),
              mr_def(mdef) => {
                exts.insert(mdef.name, mdef.ext);
                None
              }
            };
            cx.bt_pop();
            return maybe_it
          }
          _ => cx.span_fatal(
              it.span, fmt!("%s! is not legal in item position", *extname))
        }
      }
      _ => cx.span_bug(it.span, ~"invalid item macro invocation")
    }
}

fn new_span(cx: ext_ctxt, sp: span) -> span {
    /* this discards information in the case of macro-defining macros */
    return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
}

// FIXME (#2247): this is a terrible kludge to inject some macros into
// the default compilation environment. When the macro-definition system
// is substantially more mature, these should move from here, into a
// compiled part of libcore at very least.

fn core_macros() -> ~str {
    return
~"{
    macro_rules! ignore (($($x:tt)*) => (()))
    #macro[[#error[f, ...], log(core::error, #fmt[f, ...])]];
    #macro[[#warn[f, ...], log(core::warn, #fmt[f, ...])]];
    #macro[[#info[f, ...], log(core::info, #fmt[f, ...])]];
    #macro[[#debug[f, ...], log(core::debug, #fmt[f, ...])]];

    macro_rules! die(
        ($msg: expr) => (
            {
                do core::str::as_buf($msg) |msg_buf, _msg_len| {
                    do core::str::as_buf(file!()) |file_buf, _file_len| {
                        unsafe {
                            let msg_buf = core::cast::transmute(msg_buf);
                            let file_buf = core::cast::transmute(file_buf);
                            let line = line!() as core::libc::size_t;
                            core::rt::rt_fail_(msg_buf, file_buf, line)
                        }
                    }
                }
            }
        );
        () => (
            die!(\"explicit failure\")
        )
    )
}";
}

fn expand_crate(parse_sess: parse::parse_sess,
                cfg: ast::crate_cfg, c: @crate) -> @crate {
    let exts = syntax_expander_table();
    let afp = default_ast_fold();
    let cx: ext_ctxt = mk_ctxt(parse_sess, cfg);
    let f_pre =
        @{fold_expr: |a,b,c| expand_expr(exts, cx, a, b, c, afp.fold_expr),
          fold_mod: |a,b| expand_mod_items(exts, cx, a, b, afp.fold_mod),
          fold_item: |a,b| expand_item(exts, cx, a, b, afp.fold_item),
          new_span: |a|new_span(cx, a),
          .. *afp};
    let f = make_fold(f_pre);
    let cm = parse_expr_from_source_str(~"<core-macros>",
                                        @core_macros(),
                                        cfg,
                                        parse_sess);

    // This is run for its side-effects on the expander env,
    // as it registers all the core macros as expanders.
    f.fold_expr(cm);

    let res = @f.fold_crate(*c);
    return res;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>