<commit_before>use crate::rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use crate::rustc::lint::LateContext;
use crate::rustc::hir;
use crate::rustc::hir::def::Def;
use crate::syntax::ast;
use crate::utils::{match_qpath, match_trait_method, span_lint};
use crate::utils::paths;
use crate::utils::usage::mutated_variables;

use if_chain::if_chain;

use super::UNNECESSARY_FILTER_MAP;

pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {

    if !match_trait_method(cx, expr, &paths::ITERATOR) {
        return;
    }

    if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].node {

        let body = cx.tcx.hir.body(body_id);
        let arg_id = body.arguments[0].pat.id;
        let mutates_arg = match mutated_variables(&body.value, cx) {
                Some(used_mutably) => used_mutably.contains(&arg_id),
                None => true,
        };

        let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value);

        let mut return_visitor = ReturnVisitor::new(&cx, arg_id);
        return_visitor.visit_expr(&body.value);
        found_mapping |= return_visitor.found_mapping;
        found_filtering |= return_visitor.found_filtering;

        if !found_filtering {
            span_lint(
                cx,
                UNNECESSARY_FILTER_MAP,
                expr.span,
                "this `.filter_map` can be written more simply using `.map`",
            );
            return;
        }

        if !found_mapping && !mutates_arg {
            span_lint(
                cx,
                UNNECESSARY_FILTER_MAP,
                expr.span,
                "this `.filter_map` can be written more simply using `.filter`",
            );
            return;
        }
    }
}

// returns (found_mapping, found_filtering)
fn check_expression<'a, 'tcx: 'a>(cx: &'a LateContext<'a, 'tcx>, arg_id: ast::NodeId, expr: &'tcx hir::Expr) -> (bool, bool) {
    match &expr.node {
        hir::ExprKind::Call(ref func, ref args) => {
            if_chain! {
                if let hir::ExprKind::Path(ref path) = func.node;
                then {
                    if match_qpath(path, &paths::OPTION_SOME) {
                        if_chain! {
                            if let hir::ExprKind::Path(path) = &args[0].node;
                            if let Def::Local(ref local) = cx.tables.qpath_def(path, args[0].hir_id);
                            then {
                                if arg_id == *local {
                                    return (false, false)
                                }
                            }
                        }
                        return (true, false);
                    } else {
                        // We don't know. It might do anything.
                        return (true, true);
                    }
                }
            }
            (true, true)
        },
        hir::ExprKind::Block(ref block, _) => {
            if let Some(expr) = &block.expr {
                check_expression(cx, arg_id, &expr)
            } else {
                (false, false)
            }
        },
        // There must be an else_arm or there will be a type error
        hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => {
            let if_check = check_expression(cx, arg_id, if_arm);
            let else_check = check_expression(cx, arg_id, else_arm);
            (if_check.0 | else_check.0, if_check.1 | else_check.1)
        },
        hir::ExprKind::Match(_, ref arms, _) => {
            let mut found_mapping = false;
            let mut found_filtering = false;
            for arm in arms {
                let (m, f) = check_expression(cx, arg_id, &arm.body);
                found_mapping |= m;
                found_filtering |= f;
            }
            (found_mapping, found_filtering)
        },
        hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
        _ => (true, true)
    }
}

struct ReturnVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    arg_id: ast::NodeId,
    // Found a non-None return that isn't Some(input)
    found_mapping: bool,
    // Found a return that isn't Some
    found_filtering: bool,
}

impl<'a, 'tcx: 'a> ReturnVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: ast::NodeId) -> ReturnVisitor<'a, 'tcx> {
        ReturnVisitor {
            cx,
            arg_id,
            found_mapping: false,
            found_filtering: false,
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        if let hir::ExprKind::Ret(Some(expr)) = &expr.node {
            let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
            self.found_mapping |= found_mapping;
            self.found_filtering |= found_filtering;
        } else {
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<commit_msg>rustfmt<commit_after>use crate::rustc::hir;
use crate::rustc::hir::def::Def;
use crate::rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use crate::rustc::lint::LateContext;
use crate::syntax::ast;
use crate::utils::paths;
use crate::utils::usage::mutated_variables;
use crate::utils::{match_qpath, match_trait_method, span_lint};

use if_chain::if_chain;

use super::UNNECESSARY_FILTER_MAP;

pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr]) {
    if !match_trait_method(cx, expr, &paths::ITERATOR) {
        return;
    }

    if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].node {
        let body = cx.tcx.hir.body(body_id);
        let arg_id = body.arguments[0].pat.id;
        let mutates_arg = match mutated_variables(&body.value, cx) {
            Some(used_mutably) => used_mutably.contains(&arg_id),
            None => true,
        };

        let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value);

        let mut return_visitor = ReturnVisitor::new(&cx, arg_id);
        return_visitor.visit_expr(&body.value);
        found_mapping |= return_visitor.found_mapping;
        found_filtering |= return_visitor.found_filtering;

        if !found_filtering {
            span_lint(
                cx,
                UNNECESSARY_FILTER_MAP,
                expr.span,
                "this `.filter_map` can be written more simply using `.map`",
            );
            return;
        }

        if !found_mapping && !mutates_arg {
            span_lint(
                cx,
                UNNECESSARY_FILTER_MAP,
                expr.span,
                "this `.filter_map` can be written more simply using `.filter`",
            );
            return;
        }
    }
}

// returns (found_mapping, found_filtering)
fn check_expression<'a, 'tcx: 'a>(
    cx: &'a LateContext<'a, 'tcx>,
    arg_id: ast::NodeId,
    expr: &'tcx hir::Expr,
) -> (bool, bool) {
    match &expr.node {
        hir::ExprKind::Call(ref func, ref args) => {
            if_chain! {
                if let hir::ExprKind::Path(ref path) = func.node;
                then {
                    if match_qpath(path, &paths::OPTION_SOME) {
                        if_chain! {
                            if let hir::ExprKind::Path(path) = &args[0].node;
                            if let Def::Local(ref local) = cx.tables.qpath_def(path, args[0].hir_id);
                            then {
                                if arg_id == *local {
                                    return (false, false)
                                }
                            }
                        }
                        return (true, false);
                    } else {
                        // We don't know. It might do anything.
                        return (true, true);
                    }
                }
            }
            (true, true)
        },
        hir::ExprKind::Block(ref block, _) => {
            if let Some(expr) = &block.expr {
                check_expression(cx, arg_id, &expr)
            } else {
                (false, false)
            }
        },
        // There must be an else_arm or there will be a type error
        hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => {
            let if_check = check_expression(cx, arg_id, if_arm);
            let else_check = check_expression(cx, arg_id, else_arm);
            (if_check.0 | else_check.0, if_check.1 | else_check.1)
        },
        hir::ExprKind::Match(_, ref arms, _) => {
            let mut found_mapping = false;
            let mut found_filtering = false;
            for arm in arms {
                let (m, f) = check_expression(cx, arg_id, &arm.body);
                found_mapping |= m;
                found_filtering |= f;
            }
            (found_mapping, found_filtering)
        },
        hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
        _ => (true, true),
    }
}

struct ReturnVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    arg_id: ast::NodeId,
    // Found a non-None return that isn't Some(input)
    found_mapping: bool,
    // Found a return that isn't Some
    found_filtering: bool,
}

impl<'a, 'tcx: 'a> ReturnVisitor<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: ast::NodeId) -> ReturnVisitor<'a, 'tcx> {
        ReturnVisitor {
            cx,
            arg_id,
            found_mapping: false,
            found_filtering: false,
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        if let hir::ExprKind::Ret(Some(expr)) = &expr.node {
            let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
            self.found_mapping |= found_mapping;
            self.found_filtering |= found_filtering;
        } else {
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}
<|endoftext|>


<commit_before>use client::AsyncClient;
use dovahkiin::types::*;
use futures::prelude::*;
use index::btree::external::ExtNode;
use index::btree::external::*;
use index::btree::internal::InNode;
use index::btree::node::{write_node, Node, NodeWriteGuard};
use index::btree::remove::SubNodeStatus::InNodeEmpty;
use index::btree::{external, max_entry_key, BPlusTree, DeletionSetInneer, NodeCellRef};
use index::{EntryKey, Slice};
use parking_lot::RwLock;
use ram::cell::Cell;
use std::cell::RefCell;
use std::cmp::max;
use std::collections::btree_set::BTreeSet;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

pub struct TreeConstructor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    level_guards: Vec<Rc<RefCell<NodeWriteGuard<KS, PS>>>>,
}

impl<KS, PS> TreeConstructor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub fn new() -> Self {
        TreeConstructor {
            level_guards: vec![],
        }
    }

    pub fn push_extnode(&mut self, node: &NodeCellRef, first_key: EntryKey) {
        self.push(0, node, None, first_key);
    }

    fn push(&mut self, level: usize, node: &NodeCellRef, level_first: Option<&NodeCellRef>, left_bound: EntryKey) {
        let mut new_tree = false;
        debug!("Push node at {}", level);
        if self.level_guards.len() < level + 1 {
            debug!("Creating new level {}", level);
            let mut new_root_innode = InNode::<KS, PS>::new(0, max_entry_key());
            if level > 0 {
                new_root_innode.ptrs.as_slice()[0] = level_first.unwrap().clone();
            } else {
                new_tree = true;
            }
            let new_root_ref = NodeCellRef::new(Node::with_internal(new_root_innode));
            self.level_guards
                .push(Rc::new(RefCell::new(write_node::<KS, PS>(&new_root_ref))));
        }
        let parent_page_ref = self.level_guards[level].clone();
        let mut parent_guard = parent_page_ref.borrow_mut();
        let cap = KS::slice_len();
        if parent_guard.len() >= cap {
            // current page overflowed, need a new page
            debug!("Creating new node at level {}", level);
            let mut new_innode = InNode::<KS, PS>::new(1, max_entry_key());
            let parent_right_bound = parent_guard.last_key().clone();
            let new_innode_head_ptr = {
                // take a key and a ptr from current page to new page
                // reset current page right bound to the taken key
                // return the taken ptr
                let mut parent_innode = parent_guard.innode_mut();
                parent_innode.len -= 1;
                parent_innode.right_bound = parent_right_bound.clone();
                mem::replace(
                    &mut parent_innode.ptrs.as_slice()[cap],
                    NodeCellRef::default(),
                )
            };
            // arrange a valid new page by putting the ptr from current page at 1st
            new_innode.ptrs.as_slice()[0] = new_innode_head_ptr;
            new_innode.ptrs.as_slice()[1] = node.clone();
            new_innode.keys.as_slice()[0] = left_bound;
            let new_node = NodeCellRef::new(Node::with_internal(new_innode));
            self.push(level + 1, &new_node, Some(parent_guard.node_ref()), parent_right_bound);
            *parent_guard = write_node::<KS, PS>(&new_node);
        } else {
            let mut parent_innode = parent_guard.innode_mut();
            let new_len = if new_tree {
                0
            } else {
                let len = parent_innode.len;
                parent_innode.keys.as_slice()[len] = left_bound;
                len + 1
            };
            parent_innode.ptrs.as_slice()[new_len] = node.clone();
            parent_innode.len = new_len;
        }
    }

    pub fn root(&self) -> NodeCellRef {
        debug_assert!(self.level_guards.len() > 0, "reconstructed levels is zero");
        debug!("The tree have {} levels", self.level_guards.len());
        let last_ref = self.level_guards.last().unwrap().clone();
        let last_guard = last_ref.borrow();
        if last_guard.len() == 0 {
            debug!("Taking root from first ptr of overprovisioned level root");
            last_guard.innode().ptrs.as_slice_immute()[0].clone()
        } else {
            debug!("Taking level root");
            last_guard.node_ref().clone()
        }
    }
}

pub fn reconstruct_from_head_id<KS, PS>(head_id: Id, neb: &AsyncClient) -> BPlusTree<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut len = 0;
    let mut constructor = TreeConstructor::<KS, PS>::new();
    let mut prev_ref = NodeCellRef::new_none::<KS, PS>();
    let mut id = head_id;
    let mut at_end = false;
    while !at_end {
        let cell = neb.read_cell(id).wait().unwrap().unwrap();
        let page = ExtNode::<KS, PS>::from_cell(&cell);
        let next_id = page.next_id;
        let prev_id = page.prev_id;
        let mut node = page.node;
        at_end = next_id.is_unit_id();
        if at_end {
            node.next = NodeCellRef::new_none::<KS, PS>();
        }
        let mut prev_lock = write_node::<KS, PS>(&prev_ref);
        if node.len == 0 {
            // skip this empty node and make it deleted
            external::make_deleted(&node.id);
            if at_end {
                // if the empty node is the last node, assign the right none node to previous node
                *prev_lock.right_ref_mut().unwrap() = node.next.clone();
            }
            continue;
        }
        let first_key = node.keys.as_slice_immute()[0].clone();
        len += node.len;
        node.prev = prev_ref.clone();
        let node_ref = NodeCellRef::new(Node::with_external(box node));
        if !prev_lock.is_none() {
            *prev_lock.right_bound_mut() = first_key.clone();
            *prev_lock.right_ref_mut().unwrap() = node_ref.clone();
        } else {
            assert_eq!(prev_id, Id::unit_id());
        }
        constructor.push_extnode(&node_ref, first_key);
        prev_ref = node_ref;
        id = next_id;
    }
    let root = constructor.root();
    BPlusTree::from_root(root, head_id, len)
}
<commit_msg>reconstruct right bound<commit_after>use client::AsyncClient;
use dovahkiin::types::*;
use futures::prelude::*;
use index::btree::external::ExtNode;
use index::btree::external::*;
use index::btree::internal::InNode;
use index::btree::node::{write_node, Node, NodeWriteGuard};
use index::btree::remove::SubNodeStatus::InNodeEmpty;
use index::btree::{external, max_entry_key, BPlusTree, DeletionSetInneer, NodeCellRef};
use index::{EntryKey, Slice};
use parking_lot::RwLock;
use ram::cell::Cell;
use std::cell::RefCell;
use std::cmp::max;
use std::collections::btree_set::BTreeSet;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use utils::chashmap::WriteGuard;

pub struct TreeConstructor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    level_guards: Vec<Rc<RefCell<NodeWriteGuard<KS, PS>>>>,
}

impl<KS, PS> TreeConstructor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub fn new() -> Self {
        TreeConstructor {
            level_guards: vec![],
        }
    }

    pub fn push_extnode(&mut self, node: &NodeCellRef, first_key: EntryKey) {
        self.push(0, node, None, first_key);
    }

    fn push(&mut self, level: usize, node: &NodeCellRef, left_node: Option<&mut NodeWriteGuard<KS, PS>>, left_bound: EntryKey) {
        let mut new_tree = false;
        debug!("Push node at {}", level);
        if self.level_guards.len() < level + 1 {
            debug!("Creating new level {}", level);
            let mut new_root_innode = InNode::<KS, PS>::new(0, max_entry_key());
            if level > 0 {
                let mut left_node = left_node.unwrap();
                new_root_innode.ptrs.as_slice()[0] = left_node.node_ref().clone();
            } else {
                new_tree = true;
            }
            let new_root_ref = NodeCellRef::new(Node::with_internal(new_root_innode));
            self.level_guards
                .push(Rc::new(RefCell::new(write_node::<KS, PS>(&new_root_ref))));
        }
        let parent_page_ref = self.level_guards[level].clone();
        let mut node_guard = parent_page_ref.borrow_mut();
        let cap = KS::slice_len();
        if node_guard.len() >= cap {
            // current page overflowed, need a new page
            debug!("Creating new node at level {}", level);
            let mut new_innode = InNode::<KS, PS>::new(1, max_entry_key());
            let parent_right_bound = node_guard.last_key().clone();
            let new_innode_head_ptr = {
                // take a key and a ptr from current page to new page
                // reset current page right bound to the taken key
                // return the taken ptr
                let mut node_innode = node_guard.innode_mut();
                node_innode.len -= 1;
                node_innode.right_bound = parent_right_bound.clone();
                mem::replace(
                    &mut node_innode.ptrs.as_slice()[cap],
                    NodeCellRef::default(),
                )
            };
            // arrange a valid new page by putting the ptr from current page at 1st
            new_innode.ptrs.as_slice()[0] = new_innode_head_ptr;
            new_innode.ptrs.as_slice()[1] = node.clone();
            new_innode.keys.as_slice()[0] = left_bound;
            let new_node = NodeCellRef::new(Node::with_internal(new_innode));
            self.push(level + 1, &new_node, Some(&mut*node_guard), parent_right_bound);
            node_guard.right_ref_mut().map(|rn| *rn = new_node.clone());
            *node_guard = write_node::<KS, PS>(&new_node);
        } else {
            let mut parent_innode = node_guard.innode_mut();
            let new_len = if new_tree {
                0
            } else {
                let len = parent_innode.len;
                parent_innode.keys.as_slice()[len] = left_bound;
                len + 1
            };
            parent_innode.ptrs.as_slice()[new_len] = node.clone();
            parent_innode.len = new_len;
        }
    }

    pub fn root(&self) -> NodeCellRef {
        debug_assert!(self.level_guards.len() > 0, "reconstructed levels is zero");
        debug!("The tree have {} levels", self.level_guards.len());
        let last_ref = self.level_guards.last().unwrap().clone();
        let last_guard = last_ref.borrow();
        if last_guard.len() == 0 {
            debug!("Taking root from first ptr of overprovisioned level root");
            last_guard.innode().ptrs.as_slice_immute()[0].clone()
        } else {
            debug!("Taking level root");
            last_guard.node_ref().clone()
        }
    }
}

pub fn reconstruct_from_head_id<KS, PS>(head_id: Id, neb: &AsyncClient) -> BPlusTree<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut len = 0;
    let mut constructor = TreeConstructor::<KS, PS>::new();
    let mut prev_ref = NodeCellRef::new_none::<KS, PS>();
    let mut id = head_id;
    let mut at_end = false;
    while !at_end {
        let cell = neb.read_cell(id).wait().unwrap().unwrap();
        let page = ExtNode::<KS, PS>::from_cell(&cell);
        let next_id = page.next_id;
        let prev_id = page.prev_id;
        let mut node = page.node;
        at_end = next_id.is_unit_id();
        if at_end {
            node.next = NodeCellRef::new_none::<KS, PS>();
        }
        let mut prev_lock = write_node::<KS, PS>(&prev_ref);
        if node.len == 0 {
            // skip this empty node and make it deleted
            external::make_deleted(&node.id);
            if at_end {
                // if the empty node is the last node, assign the right none node to previous node
                *prev_lock.right_ref_mut().unwrap() = node.next.clone();
            }
            continue;
        }
        let first_key = node.keys.as_slice_immute()[0].clone();
        len += node.len;
        node.prev = prev_ref.clone();
        let node_ref = NodeCellRef::new(Node::with_external(box node));
        if !prev_lock.is_none() {
            *prev_lock.right_bound_mut() = first_key.clone();
            *prev_lock.right_ref_mut().unwrap() = node_ref.clone();
        } else {
            assert_eq!(prev_id, Id::unit_id());
        }
        constructor.push_extnode(&node_ref, first_key);
        prev_ref = node_ref;
        id = next_id;
    }
    let root = constructor.root();
    BPlusTree::from_root(root, head_id, len)
}
<|endoftext|>


<commit_before>use jsonpath_lib::select;
use lazy_static::lazy_static;
use regex::{Regex, RegexBuilder};
use serde_json::Value;
use std::borrow::Cow;
use std::{env, fmt, fs};

mod cache;
mod config;
mod error;

use cache::Cache;
use config::parse_config;
use error::CkError;

fn main() -> Result<(), String> {
    let config = parse_config(env::args().collect());

    let mut failed = Vec::new();
    let mut cache = Cache::new(&config.doc_dir);
    let commands = get_commands(&config.template)
        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;

    for command in commands {
        if let Err(e) = check_command(command, &mut cache) {
            failed.push(e);
        }
    }

    if failed.is_empty() {
        Ok(())
    } else {
        for i in failed {
            eprintln!("{}", i);
        }
        Err(format!("Jsondocck failed for {}", &config.template))
    }
}

#[derive(Debug)]
pub struct Command {
    negated: bool,
    kind: CommandKind,
    args: Vec<String>,
    lineno: usize,
}

#[derive(Debug)]
pub enum CommandKind {
    Has,
    Count,
    Is,
    Set,
}

impl CommandKind {
    fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
        let count = match self {
            CommandKind::Has => (1..=3).contains(&args.len()),
            CommandKind::Count | CommandKind::Is => 3 == args.len(),
            CommandKind::Set => 4 == args.len(),
        };

        if !count {
            print_err(&format!("Incorrect number of arguments to `@{}`", self), lineno);
            return false;
        }

        if args[0] == "-" && command_num == 0 {
            print_err(&format!("Tried to use the previous path in the first command"), lineno);
            return false;
        }

        if let CommandKind::Count = self {
            if args[2].parse::<usize>().is_err() {
                print_err(&format!("Third argument to @count must be a valid usize"), lineno);
                return false;
            }
        }

        true
    }
}

impl fmt::Display for CommandKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            CommandKind::Has => "has",
            CommandKind::Count => "count",
            CommandKind::Is => "is",
            CommandKind::Set => "set",
        };
        write!(f, "{}", text)
    }
}

lazy_static! {
    static ref LINE_PATTERN: Regex = RegexBuilder::new(
        r#"
        \s(?P<invalid>!?)@(?P<negated>!?)
        (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
        (?P<args>.*)$
    "#
    )
    .ignore_whitespace(true)
    .unicode(true)
    .build()
    .unwrap();
}

fn print_err(msg: &str, lineno: usize) {
    eprintln!("Invalid command: {} on line {}", msg, lineno)
}

/// Get a list of commands from a file. Does the work of ensuring the commands
/// are syntactically valid.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
    let mut commands = Vec::new();
    let mut errors = false;
    let file = fs::read_to_string(template).unwrap();

    for (lineno, line) in file.split('\n').enumerate() {
        let lineno = lineno + 1;

        let cap = match LINE_PATTERN.captures(line) {
            Some(c) => c,
            None => continue,
        };

        let negated = cap.name("negated").unwrap().as_str() == "!";
        let cmd = cap.name("cmd").unwrap().as_str();

        let cmd = match cmd {
            "has" => CommandKind::Has,
            "count" => CommandKind::Count,
            "is" => CommandKind::Is,
            "set" => CommandKind::Set,
            _ => {
                print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
                errors = true;
                continue;
            }
        };

        if let Some(m) = cap.name("invalid") {
            if m.as_str() == "!" {
                print_err(
                    &format!(
                        "`!@{0}{1}`, (help: try with `@!{1}`)",
                        if negated { "!" } else { "" },
                        cmd,
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        }

        let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));

        let args = match args {
            Some(args) => args,
            None => {
                print_err(
                    &format!(
                        "Invalid arguments to shlex::split: `{}`",
                        cap.name("args").unwrap().as_str()
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        };

        if !cmd.validate(&args, commands.len(), lineno) {
            errors = true;
            continue;
        }

        commands.push(Command { negated, kind: cmd, args, lineno })
    }

    if !errors { Ok(commands) } else { Err(()) }
}

/// Performs the actual work of ensuring a command passes. Generally assumes the command
/// is syntactically valid.
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
    // FIXME: Be more granular about why, (e.g. syntax error, count not equal)
    let result = match command.kind {
        CommandKind::Has => {
            match command.args.len() {
                // @has <path> = file existence
                1 => cache.get_file(&command.args[0]).is_ok(),
                // @has <path> <jsonpath> = check path exists
                2 => {
                    let val = cache.get_value(&command.args[0])?;

                    match select(&val, &command.args[1]) {
                        Ok(results) => !results.is_empty(),
                        Err(_) => false,
                    }
                }
                // @has <path> <jsonpath> <value> = check *any* item matched by path equals value
                3 => {
                    let val = cache.get_value(&command.args[0])?;
                    match select(&val, &command.args[1]) {
                        Ok(results) => {
                            let pat = string_to_value(&command.args[2], cache);
                            results.contains(&pat.as_ref())
                        }
                        Err(_) => false,
                    }
                }
                _ => unreachable!(),
            }
        }
        CommandKind::Count => {
            // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
            assert_eq!(command.args.len(), 3);
            let expected: usize = command.args[2].parse().unwrap();

            let val = cache.get_value(&command.args[0])?;
            match select(&val, &command.args[1]) {
                Ok(results) => results.len() == expected,
                Err(_) => false,
            }
        }
        CommandKind::Is => {
            // @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
            assert_eq!(command.args.len(), 3);
            let val = cache.get_value(&command.args[0])?;
            match select(&val, &command.args[1]) {
                Ok(results) => {
                    let pat = string_to_value(&command.args[2], cache);
                    results.len() == 1 && results[0] == pat.as_ref()
                }
                Err(_) => false,
            }
        }
        // FIXME, Figure out semantics for @!set
        CommandKind::Set => {
            // @set <name> = <path> <jsonpath>
            assert_eq!(command.args.len(), 4);
            assert_eq!(command.args[1], "=", "Expected an `=`");
            let val = cache.get_value(&command.args[2])?;

            match select(&val, &command.args[3]) {
                Ok(results) => {
                    assert_eq!(results.len(), 1);
                    let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
                    assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
                    true
                }
                Err(_) => false,
            }
        }
    };

    if result == command.negated {
        if command.negated {
            Err(CkError::FailedCheck(
                format!(
                    "`@!{} {}` matched when it shouldn't",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        } else {
            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
            Err(CkError::FailedCheck(
                format!(
                    "`@{} {}` didn't match when it should",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        }
    } else {
        Ok(())
    }
}

fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
    if s.starts_with("$") {
        Cow::Borrowed(&cache.variables[&s[1..]])
    } else {
        Cow::Owned(serde_json::from_str(s).unwrap())
    }
}
<commit_msg>Simplify Error Handling.<commit_after>use jsonpath_lib::select;
use lazy_static::lazy_static;
use regex::{Regex, RegexBuilder};
use serde_json::Value;
use std::borrow::Cow;
use std::{env, fmt, fs};

mod cache;
mod config;
mod error;

use cache::Cache;
use config::parse_config;
use error::CkError;

fn main() -> Result<(), String> {
    let config = parse_config(env::args().collect());

    let mut failed = Vec::new();
    let mut cache = Cache::new(&config.doc_dir);
    let commands = get_commands(&config.template)
        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;

    for command in commands {
        if let Err(e) = check_command(command, &mut cache) {
            failed.push(e);
        }
    }

    if failed.is_empty() {
        Ok(())
    } else {
        for i in failed {
            eprintln!("{}", i);
        }
        Err(format!("Jsondocck failed for {}", &config.template))
    }
}

#[derive(Debug)]
pub struct Command {
    negated: bool,
    kind: CommandKind,
    args: Vec<String>,
    lineno: usize,
}

#[derive(Debug)]
pub enum CommandKind {
    Has,
    Count,
    Is,
    Set,
}

impl CommandKind {
    fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
        let count = match self {
            CommandKind::Has => (1..=3).contains(&args.len()),
            CommandKind::Count | CommandKind::Is => 3 == args.len(),
            CommandKind::Set => 4 == args.len(),
        };

        if !count {
            print_err(&format!("Incorrect number of arguments to `@{}`", self), lineno);
            return false;
        }

        if args[0] == "-" && command_num == 0 {
            print_err(&format!("Tried to use the previous path in the first command"), lineno);
            return false;
        }

        if let CommandKind::Count = self {
            if args[2].parse::<usize>().is_err() {
                print_err(&format!("Third argument to @count must be a valid usize"), lineno);
                return false;
            }
        }

        true
    }
}

impl fmt::Display for CommandKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = match self {
            CommandKind::Has => "has",
            CommandKind::Count => "count",
            CommandKind::Is => "is",
            CommandKind::Set => "set",
        };
        write!(f, "{}", text)
    }
}

lazy_static! {
    static ref LINE_PATTERN: Regex = RegexBuilder::new(
        r#"
        \s(?P<invalid>!?)@(?P<negated>!?)
        (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
        (?P<args>.*)$
    "#
    )
    .ignore_whitespace(true)
    .unicode(true)
    .build()
    .unwrap();
}

fn print_err(msg: &str, lineno: usize) {
    eprintln!("Invalid command: {} on line {}", msg, lineno)
}

/// Get a list of commands from a file. Does the work of ensuring the commands
/// are syntactically valid.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
    let mut commands = Vec::new();
    let mut errors = false;
    let file = fs::read_to_string(template).unwrap();

    for (lineno, line) in file.split('\n').enumerate() {
        let lineno = lineno + 1;

        let cap = match LINE_PATTERN.captures(line) {
            Some(c) => c,
            None => continue,
        };

        let negated = cap.name("negated").unwrap().as_str() == "!";
        let cmd = cap.name("cmd").unwrap().as_str();

        let cmd = match cmd {
            "has" => CommandKind::Has,
            "count" => CommandKind::Count,
            "is" => CommandKind::Is,
            "set" => CommandKind::Set,
            _ => {
                print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
                errors = true;
                continue;
            }
        };

        if let Some(m) = cap.name("invalid") {
            if m.as_str() == "!" {
                print_err(
                    &format!(
                        "`!@{0}{1}`, (help: try with `@!{1}`)",
                        if negated { "!" } else { "" },
                        cmd,
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        }

        let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));

        let args = match args {
            Some(args) => args,
            None => {
                print_err(
                    &format!(
                        "Invalid arguments to shlex::split: `{}`",
                        cap.name("args").unwrap().as_str()
                    ),
                    lineno,
                );
                errors = true;
                continue;
            }
        };

        if !cmd.validate(&args, commands.len(), lineno) {
            errors = true;
            continue;
        }

        commands.push(Command { negated, kind: cmd, args, lineno })
    }

    if !errors { Ok(commands) } else { Err(()) }
}

/// Performs the actual work of ensuring a command passes. Generally assumes the command
/// is syntactically valid.
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
    // FIXME: Be more granular about why, (e.g. syntax error, count not equal)
    let result = match command.kind {
        CommandKind::Has => {
            match command.args.len() {
                // @has <path> = file existence
                1 => cache.get_file(&command.args[0]).is_ok(),
                // @has <path> <jsonpath> = check path exists
                2 => {
                    let val = cache.get_value(&command.args[0])?;
                    let results = select(&val, &command.args[1]).unwrap();
                    !results.is_empty()
                }
                // @has <path> <jsonpath> <value> = check *any* item matched by path equals value
                3 => {
                    let val = cache.get_value(&command.args[0])?;
                    let results = select(&val, &command.args[1]).unwrap();
                    let pat = string_to_value(&command.args[2], cache);
                    results.contains(&pat.as_ref())
                }
                _ => unreachable!(),
            }
        }
        CommandKind::Count => {
            // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
            assert_eq!(command.args.len(), 3);
            let expected: usize = command.args[2].parse().unwrap();

            let val = cache.get_value(&command.args[0])?;
            let results = select(&val, &command.args[1]).unwrap();
            results.len() == expected
        }
        CommandKind::Is => {
            // @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
            assert_eq!(command.args.len(), 3);
            let val = cache.get_value(&command.args[0])?;
            let results = select(&val, &command.args[1]).unwrap();
            let pat = string_to_value(&command.args[2], cache);
            results.len() == 1 && results[0] == pat.as_ref()
        }
        CommandKind::Set => {
            // @set <name> = <path> <jsonpath>
            assert_eq!(command.args.len(), 4);
            assert_eq!(command.args[1], "=", "Expected an `=`");
            let val = cache.get_value(&command.args[2])?;
            let results = select(&val, &command.args[3]).unwrap();
            assert_eq!(results.len(), 1);
            match results.len() {
                0 => false,
                1 => {
                    let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
                    assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
                    true
                }
                _ => {
                    panic!(
                        "Got multiple results in `@set` for `{}`: {:?}",
                        &command.args[3], results
                    );
                }
            }
        }
    };

    if result == command.negated {
        if command.negated {
            Err(CkError::FailedCheck(
                format!(
                    "`@!{} {}` matched when it shouldn't",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        } else {
            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
            Err(CkError::FailedCheck(
                format!(
                    "`@{} {}` didn't match when it should",
                    command.kind,
                    command.args.join(" ")
                ),
                command,
            ))
        }
    } else {
        Ok(())
    }
}

fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
    if s.starts_with("$") {
        Cow::Borrowed(&cache.variables[&s[1..]])
    } else {
        Cow::Owned(serde_json::from_str(s).unwrap())
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![crate_name = "devtools_traits"]
#![crate_type = "rlib"]

#![allow(non_snake_case)]
#![allow(missing_copy_implementations)]
#![feature(globs)]

extern crate "msg" as servo_msg;
extern crate serialize;
extern crate url;
extern crate "util" as servo_util;

/// This module contains shared types and messages for use by devtools/script.
/// The traits are here instead of in script so that the devtools crate can be
/// modified independently of the rest of Servo.

pub use self::DevtoolsControlMsg::*;
pub use self::DevtoolScriptControlMsg::*;
pub use self::EvaluateJSReply::*;

use serialize::{Decodable, Decoder};
use servo_msg::constellation_msg::PipelineId;
use servo_util::str::DOMString;
use url::Url;

pub type DevtoolsControlChan = Sender<DevtoolsControlMsg>;
pub type DevtoolsControlPort = Receiver<DevtoolScriptControlMsg>;

// Information would be attached to NewGlobal to be received and show in devtools.
// Extend these fields if we need more information.
pub struct DevtoolsPageInfo {
    pub title: DOMString,
    pub url: Url
}

/// Messages to the instruct the devtools server to update its known actors/state
/// according to changes in the browser.
pub enum DevtoolsControlMsg {
    NewGlobal(PipelineId, Sender<DevtoolScriptControlMsg>, DevtoolsPageInfo),
    ServerExitMsg
}

/// Serialized JS return values
/// TODO: generalize this beyond the EvaluateJS message?
pub enum EvaluateJSReply {
    VoidValue,
    NullValue,
    BooleanValue(bool),
    NumberValue(f64),
    StringValue(String),
    ActorValue(String),
}

pub struct AttrInfo {
    pub namespace: String,
    pub name: String,
    pub value: String,
}

pub struct NodeInfo {
    pub uniqueId: String,
    pub baseURI: String,
    pub parent: String,
    pub nodeType: uint,
    pub namespaceURI: String,
    pub nodeName: String,
    pub numChildren: uint,

    pub name: String,
    pub publicId: String,
    pub systemId: String,

    pub attrs: Vec<AttrInfo>,

    pub isDocumentElement: bool,

    pub shortValue: String,
    pub incompleteValue: bool,
}

/// Messages to process in a particular script task, as instructed by a devtools client.
pub enum DevtoolScriptControlMsg {
    EvaluateJS(PipelineId, String, Sender<EvaluateJSReply>),
    GetRootNode(PipelineId, Sender<NodeInfo>),
    GetDocumentElement(PipelineId, Sender<NodeInfo>),
    GetChildren(PipelineId, String, Sender<Vec<NodeInfo>>),
    GetLayout(PipelineId, String, Sender<(f32, f32)>),
    ModifyAttribute(PipelineId, String, Vec<Modification>),
}

/// Messages to instruct devtools server to update its state relating to a particular
/// tab.
pub enum ScriptDevtoolControlMsg {
    /// Report a new JS error message
    ReportConsoleMsg(String),
}

#[deriving(Encodable)]
pub struct Modification{
    pub attributeName: String,
    pub newValue: Option<String>,
}

impl<D:Decoder<E>, E> Decodable<D, E> for Modification {
    fn decode(d: &mut D) -> Result<Modification, E> {
        d.read_struct("Modification", 2u, |d|
            Ok(Modification {
                attributeName: try!(d.read_struct_field("attributeName", 0u, |d| Decodable::decode(d))),
                newValue: match d.read_struct_field("newValue", 1u, |d| Decodable::decode(d)) {
                    Ok(opt) => opt,
                    Err(_) => None
                }
            })
        )
    }
}
<commit_msg>Move the documentation comment for the devtools_traits crate to have it picked up.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module contains shared types and messages for use by devtools/script.
//! The traits are here instead of in script so that the devtools crate can be
//! modified independently of the rest of Servo.

#![crate_name = "devtools_traits"]
#![crate_type = "rlib"]

#![allow(non_snake_case)]
#![allow(missing_copy_implementations)]
#![feature(globs)]

extern crate "msg" as servo_msg;
extern crate serialize;
extern crate url;
extern crate "util" as servo_util;

pub use self::DevtoolsControlMsg::*;
pub use self::DevtoolScriptControlMsg::*;
pub use self::EvaluateJSReply::*;

use serialize::{Decodable, Decoder};
use servo_msg::constellation_msg::PipelineId;
use servo_util::str::DOMString;
use url::Url;

pub type DevtoolsControlChan = Sender<DevtoolsControlMsg>;
pub type DevtoolsControlPort = Receiver<DevtoolScriptControlMsg>;

// Information would be attached to NewGlobal to be received and show in devtools.
// Extend these fields if we need more information.
pub struct DevtoolsPageInfo {
    pub title: DOMString,
    pub url: Url
}

/// Messages to the instruct the devtools server to update its known actors/state
/// according to changes in the browser.
pub enum DevtoolsControlMsg {
    NewGlobal(PipelineId, Sender<DevtoolScriptControlMsg>, DevtoolsPageInfo),
    ServerExitMsg
}

/// Serialized JS return values
/// TODO: generalize this beyond the EvaluateJS message?
pub enum EvaluateJSReply {
    VoidValue,
    NullValue,
    BooleanValue(bool),
    NumberValue(f64),
    StringValue(String),
    ActorValue(String),
}

pub struct AttrInfo {
    pub namespace: String,
    pub name: String,
    pub value: String,
}

pub struct NodeInfo {
    pub uniqueId: String,
    pub baseURI: String,
    pub parent: String,
    pub nodeType: uint,
    pub namespaceURI: String,
    pub nodeName: String,
    pub numChildren: uint,

    pub name: String,
    pub publicId: String,
    pub systemId: String,

    pub attrs: Vec<AttrInfo>,

    pub isDocumentElement: bool,

    pub shortValue: String,
    pub incompleteValue: bool,
}

/// Messages to process in a particular script task, as instructed by a devtools client.
pub enum DevtoolScriptControlMsg {
    EvaluateJS(PipelineId, String, Sender<EvaluateJSReply>),
    GetRootNode(PipelineId, Sender<NodeInfo>),
    GetDocumentElement(PipelineId, Sender<NodeInfo>),
    GetChildren(PipelineId, String, Sender<Vec<NodeInfo>>),
    GetLayout(PipelineId, String, Sender<(f32, f32)>),
    ModifyAttribute(PipelineId, String, Vec<Modification>),
}

/// Messages to instruct devtools server to update its state relating to a particular
/// tab.
pub enum ScriptDevtoolControlMsg {
    /// Report a new JS error message
    ReportConsoleMsg(String),
}

#[deriving(Encodable)]
pub struct Modification{
    pub attributeName: String,
    pub newValue: Option<String>,
}

impl<D:Decoder<E>, E> Decodable<D, E> for Modification {
    fn decode(d: &mut D) -> Result<Modification, E> {
        d.read_struct("Modification", 2u, |d|
            Ok(Modification {
                attributeName: try!(d.read_struct_field("attributeName", 0u, |d| Decodable::decode(d))),
                newValue: match d.read_struct_field("newValue", 1u, |d| Decodable::decode(d)) {
                    Ok(opt) => opt,
                    Err(_) => None
                }
            })
        )
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

use crate::error::Result;
use clap::{App,
           Arg};
use env_logger;
use habitat_butterfly::rumor::{dat_file,
                               Departure,
                               Election,
                               ElectionUpdate,
                               Service,
                               ServiceConfig,
                               ServiceFile};
use log::error;
use std::{path::PathBuf,
          process};

pub mod error;

fn main() {
    env_logger::init();
    let matches =
        App::new("Habitat RST Reader").about("Introspection for the butterfly RST file")
                                      .arg(Arg::with_name("FILE").required(true)
                                                                 .index(1)
                                                                 .help("Path to the RST file"))
                                      .arg(Arg::with_name("STATS").short("s")
                                                                  .long("stats")
                                                                  .conflicts_with("FOLLOW")
                                                                  .help("Display statistics \
                                                                         about the contents of \
                                                                         the file"))
                                      .get_matches();

    let file = matches.value_of("FILE").unwrap();
    let stats = matches.is_present("STATS");
    let dat_file = dat_file::DatFileReader::read(PathBuf::from(file)).unwrap_or_else(|e| {
                                                                         error!("Could not read \
                                                                                dat file {}: {}",
                                                                                file, e);
                                                                         process::exit(1);
                                                                     });

    let result = if stats {
        output_stats(dat_file)
    } else {
        output_rumors(dat_file)
    };

    if result.is_err() {
        error!("Error processing dat file: {:?}", result);
        process::exit(1);
    }
}

fn output_rumors(mut dat_file: dat_file::DatFileReader) -> Result<()> {
    for member in dat_file.read_members()? {
        println!("{}", member);
    }

    for service in dat_file.read_rumors::<Service>()? {
        println!("{}", service);
    }

    for service_config in dat_file.read_rumors::<ServiceConfig>()? {
        println!("{}", service_config);
    }

    for service_file in dat_file.read_rumors::<ServiceFile>()? {
        println!("{}", service_file);
    }

    for election in dat_file.read_rumors::<Election>()? {
        println!("{}", election);
    }

    for update_election in dat_file.read_rumors::<ElectionUpdate>()? {
        println!("{}", update_election);
    }

    for departure in dat_file.read_rumors::<Departure>()? {
        println!("{}", departure);
    }

    Ok(())
}

fn output_stats(mut dat_file: dat_file::DatFileReader) -> Result<()> {
    let mut membership = 0;
    let mut services = 0;
    let mut service_configs = 0;
    let mut service_files = 0;
    let mut elections = 0;
    let mut update_elections = 0;
    let mut departures = 0;

    membership += dat_file.read_members()?.len();
    services += dat_file.read_rumors::<Service>()?.len();
    service_configs += dat_file.read_rumors::<ServiceConfig>()?.len();
    service_files += dat_file.read_rumors::<ServiceFile>()?.len();
    elections += dat_file.read_rumors::<Election>()?.len();
    update_elections += dat_file.read_rumors::<ElectionUpdate>()?.len();
    departures += dat_file.read_rumors::<Departure>()?.len();

    println!("Summary:");
    println!();
    println!("Membership: {}", membership);
    println!("Services: {}", services);
    println!("Service Configs: {}", service_configs);
    println!("Service Files: {}", service_files);
    println!("Elections: {}", elections);
    println!("Update Elections: {}", update_elections);
    println!("Departures: {}", departures);

    Ok(())
}
<commit_msg>Remove unused macro_use attribute<commit_after>extern crate log;

use crate::error::Result;
use clap::{App,
           Arg};
use env_logger;
use habitat_butterfly::rumor::{dat_file,
                               Departure,
                               Election,
                               ElectionUpdate,
                               Service,
                               ServiceConfig,
                               ServiceFile};
use log::error;
use std::{path::PathBuf,
          process};

pub mod error;

fn main() {
    env_logger::init();
    let matches =
        App::new("Habitat RST Reader").about("Introspection for the butterfly RST file")
                                      .arg(Arg::with_name("FILE").required(true)
                                                                 .index(1)
                                                                 .help("Path to the RST file"))
                                      .arg(Arg::with_name("STATS").short("s")
                                                                  .long("stats")
                                                                  .conflicts_with("FOLLOW")
                                                                  .help("Display statistics \
                                                                         about the contents of \
                                                                         the file"))
                                      .get_matches();

    let file = matches.value_of("FILE").unwrap();
    let stats = matches.is_present("STATS");
    let dat_file = dat_file::DatFileReader::read(PathBuf::from(file)).unwrap_or_else(|e| {
                                                                         error!("Could not read \
                                                                                dat file {}: {}",
                                                                                file, e);
                                                                         process::exit(1);
                                                                     });

    let result = if stats {
        output_stats(dat_file)
    } else {
        output_rumors(dat_file)
    };

    if result.is_err() {
        error!("Error processing dat file: {:?}", result);
        process::exit(1);
    }
}

fn output_rumors(mut dat_file: dat_file::DatFileReader) -> Result<()> {
    for member in dat_file.read_members()? {
        println!("{}", member);
    }

    for service in dat_file.read_rumors::<Service>()? {
        println!("{}", service);
    }

    for service_config in dat_file.read_rumors::<ServiceConfig>()? {
        println!("{}", service_config);
    }

    for service_file in dat_file.read_rumors::<ServiceFile>()? {
        println!("{}", service_file);
    }

    for election in dat_file.read_rumors::<Election>()? {
        println!("{}", election);
    }

    for update_election in dat_file.read_rumors::<ElectionUpdate>()? {
        println!("{}", update_election);
    }

    for departure in dat_file.read_rumors::<Departure>()? {
        println!("{}", departure);
    }

    Ok(())
}

fn output_stats(mut dat_file: dat_file::DatFileReader) -> Result<()> {
    let mut membership = 0;
    let mut services = 0;
    let mut service_configs = 0;
    let mut service_files = 0;
    let mut elections = 0;
    let mut update_elections = 0;
    let mut departures = 0;

    membership += dat_file.read_members()?.len();
    services += dat_file.read_rumors::<Service>()?.len();
    service_configs += dat_file.read_rumors::<ServiceConfig>()?.len();
    service_files += dat_file.read_rumors::<ServiceFile>()?.len();
    elections += dat_file.read_rumors::<Election>()?.len();
    update_elections += dat_file.read_rumors::<ElectionUpdate>()?.len();
    departures += dat_file.read_rumors::<Departure>()?.len();

    println!("Summary:");
    println!();
    println!("Membership: {}", membership);
    println!("Services: {}", services);
    println!("Service Configs: {}", service_configs);
    println!("Service Files: {}", service_files);
    println!("Elections: {}", elections);
    println!("Update Elections: {}", update_elections);
    println!("Departures: {}", departures);

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::{ast, codemap};
use syntax::ast::node_id;
use codemap::span;
use syntax::ast::{int_ty, uint_ty, float_ty};
use syntax::parse::parse_sess;
use metadata::filesearch;
use back::target_strs;
use back::link;
use middle::lint;

enum os { os_win32, os_macos, os_linux, os_freebsd, }

impl os : cmp::Eq {
    pure fn eq(&self, other: &os) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &os) -> bool { !(*self).eq(other) }
}

enum arch { arch_x86, arch_x86_64, arch_arm, }

impl arch : cmp::Eq {
    pure fn eq(&self, other: &arch) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &arch) -> bool { !(*self).eq(other) }
}

enum crate_type { bin_crate, lib_crate, unknown_crate, }

type config =
    {os: os,
     arch: arch,
     target_strs: target_strs::t,
     int_type: int_ty,
     uint_type: uint_ty,
     float_type: float_ty};

const verbose: uint = 1 << 0;
const time_passes: uint = 1 << 1;
const count_llvm_insns: uint = 1 << 2;
const time_llvm_passes: uint = 1 << 3;
const trans_stats: uint = 1 << 4;
const no_asm_comments: uint = 1 << 5;
const no_verify: uint = 1 << 6;
const trace: uint = 1 << 7;
const coherence: uint = 1 << 8;
const borrowck_stats: uint = 1 << 9;
const borrowck_note_pure: uint = 1 << 10;
const borrowck_note_loan: uint = 1 << 11;
const no_landing_pads: uint = 1 << 12;
const debug_llvm: uint = 1 << 13;
const count_type_sizes: uint = 1 << 14;
const meta_stats: uint = 1 << 15;
const no_opt: uint = 1 << 16;
const no_monomorphic_collapse: uint = 1 << 17;

fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
    ~[(~"verbose", ~"in general, enable more debug printouts", verbose),
     (~"time-passes", ~"measure time of each rustc pass", time_passes),
     (~"count-llvm-insns", ~"count where LLVM \
                           instrs originate", count_llvm_insns),
     (~"time-llvm-passes", ~"measure time of each LLVM pass",
      time_llvm_passes),
     (~"trans-stats", ~"gather trans statistics", trans_stats),
     (~"no-asm-comments", ~"omit comments when using -S", no_asm_comments),
     (~"no-verify", ~"skip LLVM verification", no_verify),
     (~"trace", ~"emit trace logs", trace),
     (~"coherence", ~"perform coherence checking", coherence),
     (~"borrowck-stats", ~"gather borrowck statistics",  borrowck_stats),
     (~"borrowck-note-pure", ~"note where purity is req'd",
      borrowck_note_pure),
     (~"borrowck-note-loan", ~"note where loans are req'd",
      borrowck_note_loan),
     (~"no-landing-pads", ~"omit landing pads for unwinding",
      no_landing_pads),
     (~"debug-llvm", ~"enable debug output from LLVM", debug_llvm),
     (~"count-type-sizes", ~"count the sizes of aggregate types",
      count_type_sizes),
     (~"meta-stats", ~"gather metadata statistics", meta_stats),
     (~"no-opt", ~"do not optimize, even if -O is passed", no_opt),
     (~"no-monomorphic-collapse", ~"do not collapse template instantiations",
      no_monomorphic_collapse),
    ]
}

enum OptLevel {
    No, // -O0
    Less, // -O1
    Default, // -O2
    Aggressive // -O3
}

impl OptLevel : cmp::Eq {
    pure fn eq(&self, other: &OptLevel) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &OptLevel) -> bool { !(*self).eq(other) }
}

type options =
    // The crate config requested for the session, which may be combined
    // with additional crate configurations during the compile process
    {crate_type: crate_type,
     static: bool,
     gc: bool,
     optimize: OptLevel,
     debuginfo: bool,
     extra_debuginfo: bool,
     lint_opts: ~[(lint::lint, lint::level)],
     save_temps: bool,
     jit: bool,
     output_type: back::link::output_type,
     addl_lib_search_paths: ~[Path],
     maybe_sysroot: Option<Path>,
     target_triple: ~str,
     // User-specified cfg meta items. The compiler itself will add additional
     // items to the crate config, and during parsing the entire crate config
     // will be added to the crate AST node.  This should not be used for
     // anything except building the full crate config prior to parsing.
     cfg: ast::crate_cfg,
     binary: ~str,
     test: bool,
     parse_only: bool,
     no_trans: bool,
     debugging_opts: uint,
    };

type crate_metadata = {name: ~str, data: ~[u8]};

type Session_ = {targ_cfg: @config,
                 opts: @options,
                 cstore: metadata::cstore::CStore,
                 parse_sess: parse_sess,
                 codemap: @codemap::CodeMap,
                 // For a library crate, this is always none
                 mut main_fn: Option<(node_id, codemap::span)>,
                 span_diagnostic: diagnostic::span_handler,
                 filesearch: filesearch::FileSearch,
                 mut building_library: bool,
                 working_dir: Path,
                 lint_settings: lint::lint_settings};

enum Session {
    Session_(@Session_)
}

impl Session {
    fn span_fatal(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_fatal(sp, msg)
    }
    fn fatal(msg: ~str) -> ! {
        self.span_diagnostic.handler().fatal(msg)
    }
    fn span_err(sp: span, msg: ~str) {
        self.span_diagnostic.span_err(sp, msg)
    }
    fn err(msg: ~str) {
        self.span_diagnostic.handler().err(msg)
    }
    fn has_errors() -> bool {
        self.span_diagnostic.handler().has_errors()
    }
    fn abort_if_errors() {
        self.span_diagnostic.handler().abort_if_errors()
    }
    fn span_warn(sp: span, msg: ~str) {
        self.span_diagnostic.span_warn(sp, msg)
    }
    fn warn(msg: ~str) {
        self.span_diagnostic.handler().warn(msg)
    }
    fn span_note(sp: span, msg: ~str) {
        self.span_diagnostic.span_note(sp, msg)
    }
    fn note(msg: ~str) {
        self.span_diagnostic.handler().note(msg)
    }
    fn span_bug(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_bug(sp, msg)
    }
    fn bug(msg: ~str) -> ! {
        self.span_diagnostic.handler().bug(msg)
    }
    fn span_unimpl(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_unimpl(sp, msg)
    }
    fn unimpl(msg: ~str) -> ! {
        self.span_diagnostic.handler().unimpl(msg)
    }
    fn span_lint_level(level: lint::level,
                       sp: span, msg: ~str) {
        match level {
          lint::allow => { },
          lint::warn => self.span_warn(sp, msg),
          lint::deny | lint::forbid => {
            self.span_err(sp, msg);
          }
        }
    }
    fn span_lint(lint_mode: lint::lint,
                 expr_id: ast::node_id, item_id: ast::node_id,
                 span: span, msg: ~str) {
        let level = lint::get_lint_settings_level(
            self.lint_settings, lint_mode, expr_id, item_id);
        self.span_lint_level(level, span, msg);
    }
    fn next_node_id() -> ast::node_id {
        return syntax::parse::next_node_id(self.parse_sess);
    }
    fn diagnostic() -> diagnostic::span_handler {
        self.span_diagnostic
    }
    fn debugging_opt(opt: uint) -> bool {
        (self.opts.debugging_opts & opt) != 0u
    }
    // This exists to help with refactoring to eliminate impossible
    // cases later on
    fn impossible_case(sp: span, msg: &str) -> ! {
        self.span_bug(sp, fmt!("Impossible case reached: %s", msg));
    }
    fn verbose() -> bool { self.debugging_opt(verbose) }
    fn time_passes() -> bool { self.debugging_opt(time_passes) }
    fn count_llvm_insns() -> bool { self.debugging_opt(count_llvm_insns) }
    fn count_type_sizes() -> bool { self.debugging_opt(count_type_sizes) }
    fn time_llvm_passes() -> bool { self.debugging_opt(time_llvm_passes) }
    fn trans_stats() -> bool { self.debugging_opt(trans_stats) }
    fn meta_stats() -> bool { self.debugging_opt(meta_stats) }
    fn no_asm_comments() -> bool { self.debugging_opt(no_asm_comments) }
    fn no_verify() -> bool { self.debugging_opt(no_verify) }
    fn trace() -> bool { self.debugging_opt(trace) }
    fn coherence() -> bool { self.debugging_opt(coherence) }
    fn borrowck_stats() -> bool { self.debugging_opt(borrowck_stats) }
    fn borrowck_note_pure() -> bool { self.debugging_opt(borrowck_note_pure) }
    fn borrowck_note_loan() -> bool { self.debugging_opt(borrowck_note_loan) }
    fn no_monomorphic_collapse() -> bool {
        // FIXME #4127: Type use is causing mysterious bustage on 32-bit archs
        let type_use_unreliable = self.targ_cfg.arch == arch_x86;

        self.debugging_opt(no_monomorphic_collapse) || type_use_unreliable
    }

    fn str_of(id: ast::ident) -> ~str {
        *self.parse_sess.interner.get(id)
    }
    fn ident_of(st: ~str) -> ast::ident {
        self.parse_sess.interner.intern(@st)
    }
    fn intr() -> @syntax::parse::token::ident_interner {
        self.parse_sess.interner
    }
}

/// Some reasonable defaults
fn basic_options() -> @options {
    @{
        crate_type: session::lib_crate,
        static: false,
        gc: false,
        optimize: No,
        debuginfo: false,
        extra_debuginfo: false,
        lint_opts: ~[],
        save_temps: false,
        jit: false,
        output_type: link::output_type_exe,
        addl_lib_search_paths: ~[],
        maybe_sysroot: None,
        target_triple: driver::host_triple(),
        cfg: ~[],
        binary: ~"rustc",
        test: false,
        parse_only: false,
        no_trans: false,
        debugging_opts: 0u
    }
}

// Seems out of place, but it uses session, so I'm putting it here
fn expect<T: Copy>(sess: Session, opt: Option<T>, msg: fn() -> ~str) -> T {
    diagnostic::expect(sess.diagnostic(), opt, msg)
}

fn building_library(req_crate_type: crate_type, crate: @ast::crate,
                    testing: bool) -> bool {
    match req_crate_type {
      bin_crate => false,
      lib_crate => true,
      unknown_crate => {
        if testing {
            false
        } else {
            match syntax::attr::first_attr_value_str_by_name(
                crate.node.attrs,
                ~"crate_type") {
              option::Some(~"lib") => true,
              _ => false
            }
        }
      }
    }
}

fn sess_os_to_meta_os(os: os) -> metadata::loader::os {
    use metadata::loader;

    match os {
      os_win32 => loader::os_win32,
      os_linux => loader::os_linux,
      os_macos => loader::os_macos,
      os_freebsd => loader::os_freebsd
    }
}

#[cfg(test)]
mod test {
    #[legacy_exports];
    use syntax::ast_util;

    fn make_crate_type_attr(t: ~str) -> ast::attribute {
        ast_util::respan(ast_util::dummy_sp(), {
            style: ast::attr_outer,
            value: ast_util::respan(ast_util::dummy_sp(),
                ast::meta_name_value(
                    ~"crate_type",
                    ast_util::respan(ast_util::dummy_sp(),
                                     ast::lit_str(@t)))),
            is_sugared_doc: false
        })
    }

    fn make_crate(with_bin: bool, with_lib: bool) -> @ast::crate {
        let mut attrs = ~[];
        if with_bin { attrs += ~[make_crate_type_attr(~"bin")]; }
        if with_lib { attrs += ~[make_crate_type_attr(~"lib")]; }
        @ast_util::respan(ast_util::dummy_sp(), {
            module: {view_items: ~[], items: ~[]},
            attrs: attrs,
            config: ~[]
        })
    }

    #[test]
    fn bin_crate_type_attr_results_in_bin_output() {
        let crate = make_crate(true, false);
        assert !building_library(unknown_crate, crate, false);
    }

    #[test]
    fn lib_crate_type_attr_results_in_lib_output() {
        let crate = make_crate(false, true);
        assert building_library(unknown_crate, crate, false);
    }

    #[test]
    fn bin_option_overrides_lib_crate_type() {
        let crate = make_crate(false, true);
        assert !building_library(bin_crate, crate, false);
    }

    #[test]
    fn lib_option_overrides_bin_crate_type() {
        let crate = make_crate(true, false);
        assert building_library(lib_crate, crate, false);
    }

    #[test]
    fn bin_crate_type_is_default() {
        let crate = make_crate(false, false);
        assert !building_library(unknown_crate, crate, false);
    }

    #[test]
    fn test_option_overrides_lib_crate_type() {
        let crate = make_crate(false, true);
        assert !building_library(unknown_crate, crate, true);
    }

    #[test]
    fn test_option_does_not_override_requested_lib_type() {
        let crate = make_crate(false, false);
        assert building_library(lib_crate, crate, true);
    }
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Remove hack to not use type_use on x86<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::{ast, codemap};
use syntax::ast::node_id;
use codemap::span;
use syntax::ast::{int_ty, uint_ty, float_ty};
use syntax::parse::parse_sess;
use metadata::filesearch;
use back::target_strs;
use back::link;
use middle::lint;

enum os { os_win32, os_macos, os_linux, os_freebsd, }

impl os : cmp::Eq {
    pure fn eq(&self, other: &os) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &os) -> bool { !(*self).eq(other) }
}

enum arch { arch_x86, arch_x86_64, arch_arm, }

impl arch : cmp::Eq {
    pure fn eq(&self, other: &arch) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &arch) -> bool { !(*self).eq(other) }
}

enum crate_type { bin_crate, lib_crate, unknown_crate, }

type config =
    {os: os,
     arch: arch,
     target_strs: target_strs::t,
     int_type: int_ty,
     uint_type: uint_ty,
     float_type: float_ty};

const verbose: uint = 1 << 0;
const time_passes: uint = 1 << 1;
const count_llvm_insns: uint = 1 << 2;
const time_llvm_passes: uint = 1 << 3;
const trans_stats: uint = 1 << 4;
const no_asm_comments: uint = 1 << 5;
const no_verify: uint = 1 << 6;
const trace: uint = 1 << 7;
const coherence: uint = 1 << 8;
const borrowck_stats: uint = 1 << 9;
const borrowck_note_pure: uint = 1 << 10;
const borrowck_note_loan: uint = 1 << 11;
const no_landing_pads: uint = 1 << 12;
const debug_llvm: uint = 1 << 13;
const count_type_sizes: uint = 1 << 14;
const meta_stats: uint = 1 << 15;
const no_opt: uint = 1 << 16;
const no_monomorphic_collapse: uint = 1 << 17;

fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
    ~[(~"verbose", ~"in general, enable more debug printouts", verbose),
     (~"time-passes", ~"measure time of each rustc pass", time_passes),
     (~"count-llvm-insns", ~"count where LLVM \
                           instrs originate", count_llvm_insns),
     (~"time-llvm-passes", ~"measure time of each LLVM pass",
      time_llvm_passes),
     (~"trans-stats", ~"gather trans statistics", trans_stats),
     (~"no-asm-comments", ~"omit comments when using -S", no_asm_comments),
     (~"no-verify", ~"skip LLVM verification", no_verify),
     (~"trace", ~"emit trace logs", trace),
     (~"coherence", ~"perform coherence checking", coherence),
     (~"borrowck-stats", ~"gather borrowck statistics",  borrowck_stats),
     (~"borrowck-note-pure", ~"note where purity is req'd",
      borrowck_note_pure),
     (~"borrowck-note-loan", ~"note where loans are req'd",
      borrowck_note_loan),
     (~"no-landing-pads", ~"omit landing pads for unwinding",
      no_landing_pads),
     (~"debug-llvm", ~"enable debug output from LLVM", debug_llvm),
     (~"count-type-sizes", ~"count the sizes of aggregate types",
      count_type_sizes),
     (~"meta-stats", ~"gather metadata statistics", meta_stats),
     (~"no-opt", ~"do not optimize, even if -O is passed", no_opt),
     (~"no-monomorphic-collapse", ~"do not collapse template instantiations",
      no_monomorphic_collapse),
    ]
}

enum OptLevel {
    No, // -O0
    Less, // -O1
    Default, // -O2
    Aggressive // -O3
}

impl OptLevel : cmp::Eq {
    pure fn eq(&self, other: &OptLevel) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &OptLevel) -> bool { !(*self).eq(other) }
}

type options =
    // The crate config requested for the session, which may be combined
    // with additional crate configurations during the compile process
    {crate_type: crate_type,
     static: bool,
     gc: bool,
     optimize: OptLevel,
     debuginfo: bool,
     extra_debuginfo: bool,
     lint_opts: ~[(lint::lint, lint::level)],
     save_temps: bool,
     jit: bool,
     output_type: back::link::output_type,
     addl_lib_search_paths: ~[Path],
     maybe_sysroot: Option<Path>,
     target_triple: ~str,
     // User-specified cfg meta items. The compiler itself will add additional
     // items to the crate config, and during parsing the entire crate config
     // will be added to the crate AST node.  This should not be used for
     // anything except building the full crate config prior to parsing.
     cfg: ast::crate_cfg,
     binary: ~str,
     test: bool,
     parse_only: bool,
     no_trans: bool,
     debugging_opts: uint,
    };

type crate_metadata = {name: ~str, data: ~[u8]};

type Session_ = {targ_cfg: @config,
                 opts: @options,
                 cstore: metadata::cstore::CStore,
                 parse_sess: parse_sess,
                 codemap: @codemap::CodeMap,
                 // For a library crate, this is always none
                 mut main_fn: Option<(node_id, codemap::span)>,
                 span_diagnostic: diagnostic::span_handler,
                 filesearch: filesearch::FileSearch,
                 mut building_library: bool,
                 working_dir: Path,
                 lint_settings: lint::lint_settings};

enum Session {
    Session_(@Session_)
}

impl Session {
    fn span_fatal(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_fatal(sp, msg)
    }
    fn fatal(msg: ~str) -> ! {
        self.span_diagnostic.handler().fatal(msg)
    }
    fn span_err(sp: span, msg: ~str) {
        self.span_diagnostic.span_err(sp, msg)
    }
    fn err(msg: ~str) {
        self.span_diagnostic.handler().err(msg)
    }
    fn has_errors() -> bool {
        self.span_diagnostic.handler().has_errors()
    }
    fn abort_if_errors() {
        self.span_diagnostic.handler().abort_if_errors()
    }
    fn span_warn(sp: span, msg: ~str) {
        self.span_diagnostic.span_warn(sp, msg)
    }
    fn warn(msg: ~str) {
        self.span_diagnostic.handler().warn(msg)
    }
    fn span_note(sp: span, msg: ~str) {
        self.span_diagnostic.span_note(sp, msg)
    }
    fn note(msg: ~str) {
        self.span_diagnostic.handler().note(msg)
    }
    fn span_bug(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_bug(sp, msg)
    }
    fn bug(msg: ~str) -> ! {
        self.span_diagnostic.handler().bug(msg)
    }
    fn span_unimpl(sp: span, msg: ~str) -> ! {
        self.span_diagnostic.span_unimpl(sp, msg)
    }
    fn unimpl(msg: ~str) -> ! {
        self.span_diagnostic.handler().unimpl(msg)
    }
    fn span_lint_level(level: lint::level,
                       sp: span, msg: ~str) {
        match level {
          lint::allow => { },
          lint::warn => self.span_warn(sp, msg),
          lint::deny | lint::forbid => {
            self.span_err(sp, msg);
          }
        }
    }
    fn span_lint(lint_mode: lint::lint,
                 expr_id: ast::node_id, item_id: ast::node_id,
                 span: span, msg: ~str) {
        let level = lint::get_lint_settings_level(
            self.lint_settings, lint_mode, expr_id, item_id);
        self.span_lint_level(level, span, msg);
    }
    fn next_node_id() -> ast::node_id {
        return syntax::parse::next_node_id(self.parse_sess);
    }
    fn diagnostic() -> diagnostic::span_handler {
        self.span_diagnostic
    }
    fn debugging_opt(opt: uint) -> bool {
        (self.opts.debugging_opts & opt) != 0u
    }
    // This exists to help with refactoring to eliminate impossible
    // cases later on
    fn impossible_case(sp: span, msg: &str) -> ! {
        self.span_bug(sp, fmt!("Impossible case reached: %s", msg));
    }
    fn verbose() -> bool { self.debugging_opt(verbose) }
    fn time_passes() -> bool { self.debugging_opt(time_passes) }
    fn count_llvm_insns() -> bool { self.debugging_opt(count_llvm_insns) }
    fn count_type_sizes() -> bool { self.debugging_opt(count_type_sizes) }
    fn time_llvm_passes() -> bool { self.debugging_opt(time_llvm_passes) }
    fn trans_stats() -> bool { self.debugging_opt(trans_stats) }
    fn meta_stats() -> bool { self.debugging_opt(meta_stats) }
    fn no_asm_comments() -> bool { self.debugging_opt(no_asm_comments) }
    fn no_verify() -> bool { self.debugging_opt(no_verify) }
    fn trace() -> bool { self.debugging_opt(trace) }
    fn coherence() -> bool { self.debugging_opt(coherence) }
    fn borrowck_stats() -> bool { self.debugging_opt(borrowck_stats) }
    fn borrowck_note_pure() -> bool { self.debugging_opt(borrowck_note_pure) }
    fn borrowck_note_loan() -> bool { self.debugging_opt(borrowck_note_loan) }
    fn no_monomorphic_collapse() -> bool {
        self.debugging_opt(no_monomorphic_collapse)
    }

    fn str_of(id: ast::ident) -> ~str {
        *self.parse_sess.interner.get(id)
    }
    fn ident_of(st: ~str) -> ast::ident {
        self.parse_sess.interner.intern(@st)
    }
    fn intr() -> @syntax::parse::token::ident_interner {
        self.parse_sess.interner
    }
}

/// Some reasonable defaults
fn basic_options() -> @options {
    @{
        crate_type: session::lib_crate,
        static: false,
        gc: false,
        optimize: No,
        debuginfo: false,
        extra_debuginfo: false,
        lint_opts: ~[],
        save_temps: false,
        jit: false,
        output_type: link::output_type_exe,
        addl_lib_search_paths: ~[],
        maybe_sysroot: None,
        target_triple: driver::host_triple(),
        cfg: ~[],
        binary: ~"rustc",
        test: false,
        parse_only: false,
        no_trans: false,
        debugging_opts: 0u
    }
}

// Seems out of place, but it uses session, so I'm putting it here
fn expect<T: Copy>(sess: Session, opt: Option<T>, msg: fn() -> ~str) -> T {
    diagnostic::expect(sess.diagnostic(), opt, msg)
}

fn building_library(req_crate_type: crate_type, crate: @ast::crate,
                    testing: bool) -> bool {
    match req_crate_type {
      bin_crate => false,
      lib_crate => true,
      unknown_crate => {
        if testing {
            false
        } else {
            match syntax::attr::first_attr_value_str_by_name(
                crate.node.attrs,
                ~"crate_type") {
              option::Some(~"lib") => true,
              _ => false
            }
        }
      }
    }
}

fn sess_os_to_meta_os(os: os) -> metadata::loader::os {
    use metadata::loader;

    match os {
      os_win32 => loader::os_win32,
      os_linux => loader::os_linux,
      os_macos => loader::os_macos,
      os_freebsd => loader::os_freebsd
    }
}

#[cfg(test)]
mod test {
    #[legacy_exports];
    use syntax::ast_util;

    fn make_crate_type_attr(t: ~str) -> ast::attribute {
        ast_util::respan(ast_util::dummy_sp(), {
            style: ast::attr_outer,
            value: ast_util::respan(ast_util::dummy_sp(),
                ast::meta_name_value(
                    ~"crate_type",
                    ast_util::respan(ast_util::dummy_sp(),
                                     ast::lit_str(@t)))),
            is_sugared_doc: false
        })
    }

    fn make_crate(with_bin: bool, with_lib: bool) -> @ast::crate {
        let mut attrs = ~[];
        if with_bin { attrs += ~[make_crate_type_attr(~"bin")]; }
        if with_lib { attrs += ~[make_crate_type_attr(~"lib")]; }
        @ast_util::respan(ast_util::dummy_sp(), {
            module: {view_items: ~[], items: ~[]},
            attrs: attrs,
            config: ~[]
        })
    }

    #[test]
    fn bin_crate_type_attr_results_in_bin_output() {
        let crate = make_crate(true, false);
        assert !building_library(unknown_crate, crate, false);
    }

    #[test]
    fn lib_crate_type_attr_results_in_lib_output() {
        let crate = make_crate(false, true);
        assert building_library(unknown_crate, crate, false);
    }

    #[test]
    fn bin_option_overrides_lib_crate_type() {
        let crate = make_crate(false, true);
        assert !building_library(bin_crate, crate, false);
    }

    #[test]
    fn lib_option_overrides_bin_crate_type() {
        let crate = make_crate(true, false);
        assert building_library(lib_crate, crate, false);
    }

    #[test]
    fn bin_crate_type_is_default() {
        let crate = make_crate(false, false);
        assert !building_library(unknown_crate, crate, false);
    }

    #[test]
    fn test_option_overrides_lib_crate_type() {
        let crate = make_crate(false, true);
        assert !building_library(unknown_crate, crate, true);
    }

    #[test]
    fn test_option_does_not_override_requested_lib_type() {
        let crate = make_crate(false, false);
        assert building_library(lib_crate, crate, true);
    }
}

// Local Variables:
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>