<commit_before>// Licensed under the Apache License, Version 2.0
// <http://www.apache.org/licenses/LICENSE-2.0>

//! # Rust Random Choice
//! This is an implementation of the stochastic universal sampling algorithm:
//! https://en.wikipedia.org/wiki/Stochastic_universal_sampling
//!
//! ## Advantages
//! - Blazingly fast: O(n) (Roulette wheel selection algorithm: O(n * log n))
//! - Low Memory Usage: O(n); in place variant: O(1)
//! - There is a good diversity for the case, that all weights are equally distributed (in contrast to the roulette wheel selection algorithm which tends to select the same sample n times)
//! - The sum of the weights don't have to be 1.0, but must not overflow
//!
//! ## Applications
//! - **Evolutionary algorithms**: Choose the _n_ fittest populations by their fitness **_fi_**
//! - **Monte Carlo Localization**: Resampling of _n_ particles by their weight **_w_**
//!
//! # Examples
//! ## In Place Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! random_choice().random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```
//! ## N Selection Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let capacity: usize = 500;
//! let mut samples: Vec<usize> = Vec::with_capacity(capacity);
//! let mut weights: Vec<f64> = Vec::with_capacity(capacity);
//!
//! for i in 0..capacity {
//!     samples.push(i);
//!     weights.push(i as f64);
//! }
//!
//! let number_choices = 10000;
//! let choices = random_choice().random_choice_f64(&samples, &weights, number_choices);
//!
//! assert!(choices.len() == number_choices);
//!
//! for choice in choices {
//!     print!("{}, ", choice);
//! }
//! # }
//! ```

extern crate rand;

use self::rand::{thread_rng, ThreadRng, Rng};

pub struct RandomChoice<RNG: Rng> {
    rng: RNG,
}

/// Creates a new RandomChoice struct using the ThreadRng
pub fn random_choice() -> RandomChoice<ThreadRng> {
    RandomChoice::new(thread_rng())
}

impl<RNG: Rng> RandomChoice<RNG> {
    /// Creates a new RandomChoice struct.
    /// @param rng the random number generator to use with this stuct.
    pub fn new(rng: RNG) -> Self {
        RandomChoice { rng: rng }
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice_f64<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f64],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place_f64<T: Clone>(&mut self, samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }

    }

    pub fn random_choice_f32<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f32],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i] as f64;
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    pub fn random_choice_in_place_f32<T: Clone>(&mut self, samples: &mut [T], weights: &[f32]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j] as f64;
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }
    }
}<commit_msg>Doc extended with example with custom seed<commit_after>// Licensed under the Apache License, Version 2.0
// <http://www.apache.org/licenses/LICENSE-2.0>

//! # Rust Random Choice
//! This is an implementation of the stochastic universal sampling algorithm:
//! https://en.wikipedia.org/wiki/Stochastic_universal_sampling
//!
//! ## Advantages
//! - Blazingly fast: O(n) (Roulette wheel selection algorithm: O(n * log n))
//! - Low Memory Usage: O(n); in place variant: O(1)
//! - There is a good diversity for the case, that all weights are equally distributed (in contrast to the roulette wheel selection algorithm which tends to select the same sample n times)
//! - The sum of the weights don't have to be 1.0, but must not overflow
//!
//! ## Applications
//! - **Evolutionary algorithms**: Choose the _n_ fittest populations by their fitness **_fi_**
//! - **Monte Carlo Localization**: Resampling of _n_ particles by their weight **_w_**
//!
//! # Examples
//! ## In Place Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! random_choice().random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```
//! ## N Selection Variant
//! ```
//! extern crate random_choice;
//! use self::random_choice::random_choice;
//!
//! # fn main() {
//! let capacity: usize = 500;
//! let mut samples: Vec<usize> = Vec::with_capacity(capacity);
//! let mut weights: Vec<f64> = Vec::with_capacity(capacity);
//!
//! for i in 0..capacity {
//!     samples.push(i);
//!     weights.push(i as f64);
//! }
//!
//! let number_choices = 10000;
//! let choices = random_choice().random_choice_f64(&samples, &weights, number_choices);
//!
//! assert!(choices.len() == number_choices);
//!
//! for choice in choices {
//!     print!("{}, ", choice);
//! }
//! # }
//! ```
//! ## With custom seed
//! ```
//! extern crate random_choice;
//! extern crate rand;
//!
//! use self::random_choice::RandomChoice;
//! use self::rand::thread_rng;
//!
//! # fn main() {
//! let mut samples = vec!["hi", "this", "is", "a", "test!"];
//! let weights: Vec<f64> = vec![5.6, 7.8, 9.7, 1.1, 2.0];
//!
//! let mut random_choice = RandomChoice::new(thread_rng());
//! random_choice.random_choice_in_place_f64(&mut samples, &weights);
//!
//! for sample in samples {
//!     print!("{}, ", sample);
//! }
//! # }
//! ```

extern crate rand;

use self::rand::{thread_rng, ThreadRng, Rng};

pub struct RandomChoice<RNG: Rng> {
    rng: RNG,
}

/// Creates a new RandomChoice struct using the ThreadRng
pub fn random_choice() -> RandomChoice<ThreadRng> {
    RandomChoice::new(thread_rng())
}

impl<RNG: Rng> RandomChoice<RNG> {
    /// Creates a new RandomChoice struct.
    /// @param rng the random number generator to use with this stuct.
    pub fn new(rng: RNG) -> Self {
        RandomChoice { rng: rng }
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    ///
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    /// @param n Number of randomly chosen samples by weight.
    /// @return randomly selected samples by their weights
    pub fn random_choice_f64<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f64],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i];
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    /// Chooses n samples by their weights. The greater their weights the more likely they get chosen.
    /// The result gets saved directly in the samples argument.
    /// @invariant sum of weights must not overflow.
    /// @param samples The to be selected samples
    /// @param weights Weights that get chosen by their weight/probability. One weight can be greater 1.
    pub fn random_choice_in_place_f64<T: Clone>(&mut self, samples: &mut [T], weights: &[f64]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0];
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j];
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }

    }

    pub fn random_choice_f32<'a, T>(&mut self,
                                    samples: &'a [T],
                                    weights: &[f32],
                                    n: usize)
                                    -> Vec<&'a T> {
        if weights.len() == 0 || n == 0 {
            return Vec::new();
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut choices: Vec<&T> = Vec::with_capacity(n);
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                i += 1;
                accumulated_weights += weights[i] as f64;
            }
            choices.push(&samples[i]);
            current_spoke += spoke_gap;
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while choices.len() < weights.len() {
            choices.push(&samples[i]);
        }

        choices
    }

    pub fn random_choice_in_place_f32<T: Clone>(&mut self, samples: &mut [T], weights: &[f32]) {
        if weights.len() < 2 {
            return;
        }

        let sum: f64 = weights.iter().fold(0.0, |acc, &i| acc + i as f64);
        let n: usize = weights.len();
        let spoke_gap: f64 = sum / n as f64;

        // next_f64() ∈ [0.0, 1.0)
        let spin = self.rng.next_f64() * spoke_gap;

        let mut i: usize = 0;
        let mut j: usize = 0;
        let mut accumulated_weights = weights[0] as f64;
        let mut current_spoke: f64 = spin;

        while current_spoke < sum {
            while accumulated_weights < current_spoke {
                j += 1;
                accumulated_weights += weights[j] as f64;
            }
            samples[i] = samples[j].clone();
            current_spoke += spoke_gap;
            i += 1
        }

        // add this condition, because float leads to inaccurate
        // calculations which can lead to i >= weights.len()
        while i < weights.len() {
            samples[i] = samples[j].clone();
            i += 1
        }
    }
}<|endoftext|>


<commit_before>// tag_safe
//
// A linting plugin to flag calls to methods not marked "tag_safe"
// from methods marked "tag_safe".
//
// Author: John Hodge (thePowersGang/Mutabah)
//
// TODO: Support '#[tag_unsafe(type)]' which is used when a method has no marker
// - Allows default safe fallback, with upwards propagation.
//
#![crate_name="tag_safe"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private)]

#[macro_use]
extern crate log;

extern crate syntax;
#[macro_use]
extern crate rustc;

use syntax::ast;
use syntax::visit;
use syntax::codemap::Span;
use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use rustc::lint::{Context, LintPass, LintArray};
use syntax::attr::AttrMetaMethods;
use rustc::middle::{def,ty};

declare_lint!(NOT_TAGGED_SAFE, Warn, "Warn about use of non-tagged methods within tagged function");

#[derive(Copy,Clone,Debug)]
enum SafetyType
{
    Safe,
    Unsafe,
    Unknown,
}

#[derive(Default)]
struct Pass
{
    /// Cache of flag types
    flag_types: Vec<String>,
    /// Node => (Type => IsSafe)
    flag_cache: ::rustc::util::nodemap::NodeMap< ::rustc::util::nodemap::FnvHashMap<usize, SafetyType> >,

    lvl: usize,
}

struct Visitor<'a, 'tcx: 'a, F: FnMut(&Span) + 'a>
{
    pass: &'a mut Pass,
    tcx: &'a ty::ctxt<'tcx>,
    name: &'a str,
    unknown_assume: bool,
    cb: F,
}

// Hack to provide indenting in debug calls
struct Indent(usize);
impl ::std::fmt::Display for Indent {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        for s in ::std::iter::repeat(" ").take(self.0) {
            try!(write!(f, "{}", s));
        }
        Ok( () )
    }
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NOT_TAGGED_SAFE)
    }

    fn check_fn(&mut self, cx: &Context, _kind: visit::FnKind, _decl: &ast::FnDecl, body: &ast::Block, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);
        for ty in attrs.iter()
            .filter(|a| a.check_name("tag_safe"))
            .filter_map(|a| a.meta_item_list())
            .flat_map(|x| x.iter())
        {
            // Search body for calls to non safe methods
            let mut v = Visitor{
                    pass: self, tcx: cx.tcx, name: &ty.name(),
                    // - Assumes an untagged method is unsafe
                    unknown_assume: false,
                    cb: |span| {
                            cx.span_lint(NOT_TAGGED_SAFE, *span,
                                &format!("Calling {}-unsafe method from a #[tag_safe({})] method", ty.name(), ty.name())[..]
                                );
                        },
                    };
            debug!("Method {:?} is marked safe '{}'", id, ty.name());
            visit::walk_block(&mut v, body);
        }
    }
}

impl Pass
{
    // Searches for the relevant marker
    fn check_for_marker(tcx: &ty::ctxt, id: ast::NodeId, marker: &str, name: &str) -> bool
    {
        debug!("Checking for marker {}({}) on {:?}", marker, name, id);
        tcx.map.attrs(id).iter()
            .filter_map( |a| if a.check_name(marker) { a.meta_item_list() } else { None })
            .flat_map(|x| x.iter())
            .any(|a| a.name() == name)
    }

    /// Recursively check that the provided function is either safe or unsafe.
    // Used to avoid excessive annotating
    fn recurse_fcn_body(&mut self, tcx: &ty::ctxt, node_id: ast::NodeId, name_id: usize, name: &str, unknown_assume: bool) -> bool
    {
        // Cache this method as unknown (to prevent infinite recursion)
        self.flag_cache.entry(node_id)
            .or_insert(Default::default())
            .insert(name_id, SafetyType::Unknown)
            ;

        // and apply a visitor to all
        match tcx.map.get(node_id)
        {
        syntax::ast_map::NodeItem(i) =>
            match i.node {
            ast::ItemFn(_, _, _, _, _, ref body) => {
                // Enumerate this function's code, recursively checking for a call to an unsafe method
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, name: name,
                        unknown_assume: true,
                        cb: |_| { is_safe = false; }
                        };
                    visit::walk_block(&mut v, body);
                }
                is_safe
                },
            _ => unknown_assume,
            },
        syntax::ast_map::NodeImplItem(i) =>
            match i.node {
            ast::MethodImplItem(_, ref body) => {
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, name: name,
                        unknown_assume: true,
                        cb: |_| { is_safe = false; }
                        };
                    visit::walk_block(&mut v, body);
                }
                is_safe
                },
            _ => unknown_assume,
            },
        v @ _ => {
            error!("Node ID {} points to non-item {:?}", node_id, v);
            unknown_assume
            }
        }
    }

    /// Check that a method within this crate is safe with the provided tag
    fn crate_method_is_safe(&mut self, tcx: &ty::ctxt, node_id: ast::NodeId, name: &str, unknown_assume: bool) -> bool
    {
        // Obtain tag name ID (avoids storing a string in the map)
        let name_id =
            match self.flag_types.iter().position(|a| *a == name)
            {
            Some(v) => v,
            None => {
                self.flag_types.push( String::from(name) );
                self.flag_types.len() - 1
                },
            };

        // Check cache first
        if let Some(&st) = self.flag_cache.get(&node_id).and_then(|a| a.get(&name_id))
        {
            match st
            {
            SafetyType::Safe => true,
            SafetyType::Unsafe => false,
            SafetyType::Unknown => unknown_assume,
            }
        }
        else
        {
            // Search for a safety marker, possibly recursing
            let is_safe =
                if Self::check_for_marker(tcx, node_id, "tag_safe", name) {
                    true
                }
                else if Self::check_for_marker(tcx, node_id, "tag_unsafe", name) {
                    false
                }
                else {
                    self.recurse_fcn_body(tcx, node_id, name_id, name, unknown_assume)
                };
            // Save resultant value
            self.flag_cache.entry(node_id)
                .or_insert(Default::default())
                .insert(name_id, if is_safe { SafetyType::Safe } else { SafetyType::Unsafe })
                ;
            is_safe
        }
    }

    /// Locate a #[tag_safe(<name>)] attribute on the passed item
    pub fn method_is_safe(&mut self, tcx: &ty::ctxt, id: ast::DefId, name: &str, unknown_assume: bool) -> bool
    {
        debug!("{}Checking method {:?} (A {})", Indent(self.lvl), id, unknown_assume);
        self.lvl += 1;
        let rv = if id.krate == 0 {
                self.crate_method_is_safe(tcx, id.node, name, unknown_assume)
            }
            else {
                for a in ty::get_attrs(tcx, id).iter()
                {
                    if a.check_name("tag_safe") {
                        if a.meta_item_list().iter().flat_map(|a| a.iter()).any(|a| a.name() == name) {
                            return true;
                        }
                    }
                    if a.check_name("tag_unsafe") {
                        if a.meta_item_list().iter().flat_map(|a| a.iter()).any(|a| a.name() == name) {
                            return false;
                        }
                    }
                }
                warn!("TODO: Crate ID non-zero {:?} (assuming safe)", id);
                // TODO: Check the crate import for an annotation
                true
            };
        self.lvl -= 1;
        debug!("{}Checking method {:?} = {}", Indent(self.lvl), id, rv);
        rv
    }
}

impl<'a, 'tcx: 'a, F: FnMut(&Span)> visit::Visitor<'a> for Visitor<'a,'tcx, F>
{
    // Locate function/method calls in a code block
    // - uses visit_expr_post because it doesn't _need_ to do anything
    fn visit_expr_post(&mut self, ex: &'a ast::Expr) {
        match ex.node
        {
        // Call expressions - check that it's a path call
        ast::ExprCall(ref fcn, _) =>
            match fcn.node
            {
            ast::ExprPath(ref _qs, ref _p) => {
                    if let def::DefFn(did, _) = ty::resolve_expr(self.tcx, &fcn) {
                        // Check for a safety tag
                        if !self.pass.method_is_safe(self.tcx, did, self.name, self.unknown_assume)
                        {
                            (self.cb)(&ex.span);
                        }
                    }
                },
            _ => {},
            },

        // Method call expressions - get the relevant method
        ast::ExprMethodCall(ref _id, ref _tys, ref _exprs) =>
            {
                let mm = self.tcx.method_map.borrow();
                match mm.get( &ty::MethodCall::expr(ex.id) ).unwrap().origin
                {
                ty::MethodStatic(id) => {
                        // Check for a safety tag
                        if !self.pass.method_is_safe(self.tcx, id, self.name, self.unknown_assume) {
                            (self.cb)(&ex.span);
                        }
                    },
                _ => {},
                }
            },

        // Ignore any other type of node
        _ => {},
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    use syntax::feature_gate::AttributeType;
    reg.register_lint_pass(Box::new(Pass::default()) as LintPassObject);

    reg.register_attribute(String::from("tag_safe"),   AttributeType::Whitelisted);
    reg.register_attribute(String::from("tag_unsafe"), AttributeType::Whitelisted);
}

// vim: ts=4 expandtab sw=4
<commit_msg>Update to latest nightly (which moved ast_map to librustc)<commit_after>// tag_safe
//
// A linting plugin to flag calls to methods not marked "tag_safe"
// from methods marked "tag_safe".
//
// Author: John Hodge (thePowersGang/Mutabah)
//
// TODO: Support '#[tag_unsafe(type)]' which is used when a method has no marker
// - Allows default safe fallback, with upwards propagation.
//
#![crate_name="tag_safe"]
#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private)]

#[macro_use]
extern crate log;

extern crate syntax;
#[macro_use]
extern crate rustc;

use syntax::ast;
use syntax::visit;
use syntax::codemap::Span;
use rustc::lint::LintPassObject;
use rustc::plugin::Registry;
use rustc::lint::{Context, LintPass, LintArray};
use syntax::attr::AttrMetaMethods;
use rustc::middle::{def,ty};

declare_lint!(NOT_TAGGED_SAFE, Warn, "Warn about use of non-tagged methods within tagged function");

#[derive(Copy,Clone,Debug)]
enum SafetyType
{
    Safe,
    Unsafe,
    Unknown,
}

#[derive(Default)]
struct Pass
{
    /// Cache of flag types
    flag_types: Vec<String>,
    /// Node => (Type => IsSafe)
    flag_cache: ::rustc::util::nodemap::NodeMap< ::rustc::util::nodemap::FnvHashMap<usize, SafetyType> >,

    lvl: usize,
}

struct Visitor<'a, 'tcx: 'a, F: FnMut(&Span) + 'a>
{
    pass: &'a mut Pass,
    tcx: &'a ty::ctxt<'tcx>,
    name: &'a str,
    unknown_assume: bool,
    cb: F,
}

// Hack to provide indenting in debug calls
struct Indent(usize);
impl ::std::fmt::Display for Indent {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        for s in ::std::iter::repeat(" ").take(self.0) {
            try!(write!(f, "{}", s));
        }
        Ok( () )
    }
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NOT_TAGGED_SAFE)
    }

    fn check_fn(&mut self, cx: &Context, _kind: visit::FnKind, _decl: &ast::FnDecl, body: &ast::Block, _: Span, id: ast::NodeId) {
        let attrs = cx.tcx.map.attrs(id);
        for ty in attrs.iter()
            .filter(|a| a.check_name("tag_safe"))
            .filter_map(|a| a.meta_item_list())
            .flat_map(|x| x.iter())
        {
            // Search body for calls to non safe methods
            let mut v = Visitor{
                    pass: self, tcx: cx.tcx, name: &ty.name(),
                    // - Assumes an untagged method is unsafe
                    unknown_assume: false,
                    cb: |span| {
                            cx.span_lint(NOT_TAGGED_SAFE, *span,
                                &format!("Calling {}-unsafe method from a #[tag_safe({})] method", ty.name(), ty.name())[..]
                                );
                        },
                    };
            debug!("Method {:?} is marked safe '{}'", id, ty.name());
            visit::walk_block(&mut v, body);
        }
    }
}

impl Pass
{
    // Searches for the relevant marker
    fn check_for_marker(tcx: &ty::ctxt, id: ast::NodeId, marker: &str, name: &str) -> bool
    {
        debug!("Checking for marker {}({}) on {:?}", marker, name, id);
        tcx.map.attrs(id).iter()
            .filter_map( |a| if a.check_name(marker) { a.meta_item_list() } else { None })
            .flat_map(|x| x.iter())
            .any(|a| a.name() == name)
    }

    /// Recursively check that the provided function is either safe or unsafe.
    // Used to avoid excessive annotating
    fn recurse_fcn_body(&mut self, tcx: &ty::ctxt, node_id: ast::NodeId, name_id: usize, name: &str, unknown_assume: bool) -> bool
    {
        // Cache this method as unknown (to prevent infinite recursion)
        self.flag_cache.entry(node_id)
            .or_insert(Default::default())
            .insert(name_id, SafetyType::Unknown)
            ;

        // and apply a visitor to all
        match tcx.map.get(node_id)
        {
        rustc::ast_map::NodeItem(i) =>
            match i.node {
            ast::ItemFn(_, _, _, _, _, ref body) => {
                // Enumerate this function's code, recursively checking for a call to an unsafe method
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, name: name,
                        unknown_assume: true,
                        cb: |_| { is_safe = false; }
                        };
                    visit::walk_block(&mut v, body);
                }
                is_safe
                },
            _ => unknown_assume,
            },
        rustc::ast_map::NodeImplItem(i) =>
            match i.node {
            ast::MethodImplItem(_, ref body) => {
                let mut is_safe = true;
                {
                    let mut v = Visitor {
                        pass: self, tcx: tcx, name: name,
                        unknown_assume: true,
                        cb: |_| { is_safe = false; }
                        };
                    visit::walk_block(&mut v, body);
                }
                is_safe
                },
            _ => unknown_assume,
            },
        v @ _ => {
            error!("Node ID {} points to non-item {:?}", node_id, v);
            unknown_assume
            }
        }
    }

    /// Check that a method within this crate is safe with the provided tag
    fn crate_method_is_safe(&mut self, tcx: &ty::ctxt, node_id: ast::NodeId, name: &str, unknown_assume: bool) -> bool
    {
        // Obtain tag name ID (avoids storing a string in the map)
        let name_id =
            match self.flag_types.iter().position(|a| *a == name)
            {
            Some(v) => v,
            None => {
                self.flag_types.push( String::from(name) );
                self.flag_types.len() - 1
                },
            };

        // Check cache first
        if let Some(&st) = self.flag_cache.get(&node_id).and_then(|a| a.get(&name_id))
        {
            match st
            {
            SafetyType::Safe => true,
            SafetyType::Unsafe => false,
            SafetyType::Unknown => unknown_assume,
            }
        }
        else
        {
            // Search for a safety marker, possibly recursing
            let is_safe =
                if Self::check_for_marker(tcx, node_id, "tag_safe", name) {
                    true
                }
                else if Self::check_for_marker(tcx, node_id, "tag_unsafe", name) {
                    false
                }
                else {
                    self.recurse_fcn_body(tcx, node_id, name_id, name, unknown_assume)
                };
            // Save resultant value
            self.flag_cache.entry(node_id)
                .or_insert(Default::default())
                .insert(name_id, if is_safe { SafetyType::Safe } else { SafetyType::Unsafe })
                ;
            is_safe
        }
    }

    /// Locate a #[tag_safe(<name>)] attribute on the passed item
    pub fn method_is_safe(&mut self, tcx: &ty::ctxt, id: ast::DefId, name: &str, unknown_assume: bool) -> bool
    {
        debug!("{}Checking method {:?} (A {})", Indent(self.lvl), id, unknown_assume);
        self.lvl += 1;
        let rv = if id.krate == 0 {
                self.crate_method_is_safe(tcx, id.node, name, unknown_assume)
            }
            else {
                for a in ty::get_attrs(tcx, id).iter()
                {
                    if a.check_name("tag_safe") {
                        if a.meta_item_list().iter().flat_map(|a| a.iter()).any(|a| a.name() == name) {
                            return true;
                        }
                    }
                    if a.check_name("tag_unsafe") {
                        if a.meta_item_list().iter().flat_map(|a| a.iter()).any(|a| a.name() == name) {
                            return false;
                        }
                    }
                }
                warn!("TODO: Crate ID non-zero {:?} (assuming safe)", id);
                // TODO: Check the crate import for an annotation
                true
            };
        self.lvl -= 1;
        debug!("{}Checking method {:?} = {}", Indent(self.lvl), id, rv);
        rv
    }
}

impl<'a, 'tcx: 'a, F: FnMut(&Span)> visit::Visitor<'a> for Visitor<'a,'tcx, F>
{
    // Locate function/method calls in a code block
    // - uses visit_expr_post because it doesn't _need_ to do anything
    fn visit_expr_post(&mut self, ex: &'a ast::Expr) {
        match ex.node
        {
        // Call expressions - check that it's a path call
        ast::ExprCall(ref fcn, _) =>
            match fcn.node
            {
            ast::ExprPath(ref _qs, ref _p) => {
                    if let def::DefFn(did, _) = ty::resolve_expr(self.tcx, &fcn) {
                        // Check for a safety tag
                        if !self.pass.method_is_safe(self.tcx, did, self.name, self.unknown_assume)
                        {
                            (self.cb)(&ex.span);
                        }
                    }
                },
            _ => {},
            },

        // Method call expressions - get the relevant method
        ast::ExprMethodCall(ref _id, ref _tys, ref _exprs) =>
            {
                let mm = self.tcx.method_map.borrow();
                match mm.get( &ty::MethodCall::expr(ex.id) ).unwrap().origin
                {
                ty::MethodStatic(id) => {
                        // Check for a safety tag
                        if !self.pass.method_is_safe(self.tcx, id, self.name, self.unknown_assume) {
                            (self.cb)(&ex.span);
                        }
                    },
                _ => {},
                }
            },

        // Ignore any other type of node
        _ => {},
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    use syntax::feature_gate::AttributeType;
    reg.register_lint_pass(Box::new(Pass::default()) as LintPassObject);

    reg.register_attribute(String::from("tag_safe"),   AttributeType::Whitelisted);
    reg.register_attribute(String::from("tag_unsafe"), AttributeType::Whitelisted);
}

// vim: ts=4 expandtab sw=4
<|endoftext|>


<commit_before>#![feature(convert)]

extern crate html5ever;
extern crate string_cache;

use std::convert::AsRef;
use std::path::Path;
use std::fs::{File, read_dir};
use std::io::{self, Read, Write};
use std::error::FromError;
use std::default::Default;
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fmt;
use std::mem::swap;

use html5ever::Attribute;
use html5ever::tokenizer::{Tokenizer, TokenSink, Tag, TagKind};
use html5ever::tokenizer::Token as HtmlToken;

use string_cache::atom::Atom;

use codegen::{Token, Content, Codegen, ContentType, Scope};
use fragments::{Fragment, ReturnType};

use parser::ExtensibleMap;

#[macro_use]
pub mod codegen;
pub mod fragments;
pub mod javascript;
pub mod rust;
mod parser;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Format(fmt::Error),
    Parse(Vec<Cow<'static, str>>)
}

impl FromError<io::Error> for Error {
    fn from_error(e: io::Error) -> Error {
        Error::Io(e)
    }
}

impl FromError<fmt::Error> for Error {
    fn from_error(e: fmt::Error) -> Error {
        Error::Format(e)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::Io(ref e) => e.fmt(f),
            &Error::Format(ref e) => e.fmt(f),
            &Error::Parse(ref e) => {
                try!(write!(f, "parse errors: "));
                for (i, error) in e.iter().enumerate() {
                    if i == 0 {
                        try!(error.fmt(f));
                    } else {
                        try!(write!(f, ", {}", error));
                    }
                }
                Ok(())
            }
        }
    }
}

///A collection of templates.
pub struct TemplateGroup<'a> {
    templates: Vec<ParsedTemplate>,
    fragments: HashMap<&'static str, Box<Fragment + 'a>>
}

impl<'a> TemplateGroup<'a> {
    pub fn new() -> TemplateGroup<'a> {
        TemplateGroup {
            templates: vec![],
            fragments: init_fragments()
        }
    }

    ///Add a template from a string.
    pub fn parse_string(&mut self, name: String, source: String) -> Result<(), Error> {
        let mut template = Template::extending(&self.fragments);
        try!(template.parse_string(source));
        self.templates.push(ParsedTemplate {
            name: name,
            parameters: template.parameters,
            tokens: template.tokens
        });

        Ok(())
    }

    ///Add every `*.html` and `*.htm` file in a directory to the template group.
    pub fn parse_directory<P: AsRef<Path>>(&mut self, dir: P) -> Result<(), Error> {
        let directory = try!(read_dir(dir));

        for path in directory {
            let path = try!(path).path();
            let info = (
                path.file_stem().and_then(|n| n.to_str()),
                path.extension().and_then(|e| e.to_str())
            );
            if let (Some(name), Some(ext)) = info {
                if ext == "html" || ext == "htm" {
                    let mut source = String::new();
                    try!(File::open(&path).and_then(|mut f| f.read_to_string(&mut source)));
                    try!(self.parse_string(name.to_string(), source));
                }
            }
        }

        Ok(())
    }

    ///Add an already parsed template to the group.
    pub fn add_template(&mut self, name: String, template: Template) {
        self.templates.push(ParsedTemplate {
            name: name,
            parameters: template.parameters,
            tokens: template.tokens
        });
    }

    ///Register a custom fragment.
    pub fn register_fragment<F: Fragment + 'a>(&mut self, fragment: F) {
        let fragment = Box::new(fragment);
        self.fragments.insert(fragment.identifier(), fragment);
    }

    ///Write template code.
    pub fn emit_code<W: Write, C: Codegen>(&self, writer: &mut W, codegen: &C) -> Result<(), Error> {
        codegen.build_module(writer, |w, indent| {
            for template in &self.templates {
                try!(codegen.build_template(w, &template.name, indent, &template.parameters, &template.tokens));
            }
            Ok(())
        })
    }
}

struct ParsedTemplate {
    name: String,
    parameters: HashMap<String, ContentType>,
    tokens: Vec<codegen::Token>
}

///A single template.
pub struct Template<'a> {
    fragments: ExtensibleMap<'a, &'static str, Box<Fragment + 'a>>,
    parameters: HashMap<String, ContentType>,
    tokens: Vec<codegen::Token>,
    errors: Vec<Cow<'static, str>>
}

impl<'a> Template<'a> {
    ///Create an empty template.
    pub fn new() -> Template<'a> {
        Template {
            fragments: ExtensibleMap::Owned(init_fragments()),
            parameters: HashMap::new(),
            tokens: vec![],
            errors: vec![]
        }
    }

    fn extending(fragments: &'a HashMap<&'static str, Box<Fragment + 'a>>) -> Template<'a> {
        Template {
            fragments: ExtensibleMap::extend(fragments),
            parameters: HashMap::new(),
            tokens: vec![],
            errors: vec![]
        }
    }

    ///Add content from a string to the template.
    pub fn parse_string(&mut self, source: String) -> Result<(), Error> {
        {
            let mut tokenizer = Tokenizer::new(&mut*self, Default::default());
            tokenizer.feed(source);
        }

        if self.errors.len() > 0 {
            let mut e = vec![];
            swap(&mut e, &mut self.errors);
            Err(Error::Parse(e))
        } else {
            Ok(())
        }
    }

    ///Register a custom fragment.
    pub fn register_fragment<F: Fragment + 'a>(&mut self, fragment: F) {
        let fragment = Box::new(fragment);
        self.fragments.insert(fragment.identifier(), fragment);
    }

    ///Write template code.
    pub fn emit_code<W: Write, C: Codegen>(&self, template_name: &str, writer: &mut W, codegen: &C) -> Result<(), Error> {
        codegen.build_template(writer, template_name, 0, &self.parameters, &self.tokens)
    }

    fn open_tag(&mut self, name: Atom, attributes: Vec<Attribute>, self_closing: bool) -> Result<(), Cow<'static, str>> {
        let void = is_void(name.as_slice());
        self.tokens.push(Token::BeginTag(name));

        for attribute in attributes {
            let mut content = try!(parser::parse_content(&attribute.value, &self.fragments)).into_iter();
            match content.next() {
                Some(ReturnType::Content(Content::String(text))) => self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::String(text))),
                Some(ReturnType::Content(Content::Placeholder(name))) => {
                    self.reg_string(name.clone());
                    self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::Placeholder(name)));
                },
                Some(ReturnType::Logic(_)) => return Err(Cow::Borrowed("logic can not be used as text")),
                Some(ReturnType::Scope(scope)) => {
                    self.reg_scope_vars(&scope);
                    self.tokens.push(Token::Scope(scope));
                },
                Some(ReturnType::End) => self.tokens.push(Token::End),
                None => self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::String(String::new())))
            }

            for part in content {
                match part {
                    ReturnType::Content(Content::String(text)) => self.tokens.push(Token::AppendToAttribute(Content::String(text))),
                    ReturnType::Content(Content::Placeholder(name)) => {
                        self.reg_string(name.clone());
                        self.tokens.push(Token::AppendToAttribute(Content::Placeholder(name)));
                    },
                    ReturnType::Logic(_) => return Err(Cow::Borrowed("logic can not be used as text")),
                    ReturnType::Scope(scope) => {
                        self.reg_scope_vars(&scope);
                        self.tokens.push(Token::Scope(scope));
                    },
                    ReturnType::End => self.tokens.push(Token::End),
                }
            }

            self.tokens.push(Token::EndAttribute);
        }

        self.tokens.push(Token::EndTag(void || self_closing));

        Ok(())
    }

    fn add_text(&mut self, text: String) -> Result<(), Cow<'static, str>> {
        let mut content = try!(parser::parse_content(&text, &self.fragments)).into_iter();
        match content.next() {
            Some(ReturnType::Content(Content::String(text))) => self.tokens.push(Token::BeginText(Content::String(text))),
            Some(ReturnType::Content(Content::Placeholder(name))) => {
                self.reg_string(name.clone());
                self.tokens.push(Token::BeginText(Content::Placeholder(name)));
            },
            Some(ReturnType::Logic(_)) => return Err(Cow::Borrowed("logic can not be used as text")),
            Some(ReturnType::Scope(scope)) => {
                self.reg_scope_vars(&scope);
                self.tokens.push(Token::Scope(scope));
            },
            Some(ReturnType::End) => self.tokens.push(Token::End),
            None => return Ok(())
        }

        for part in content {
            match part {
                ReturnType::Content(Content::String(text)) => self.tokens.push(Token::AppendToText(Content::String(text))),
                ReturnType::Content(Content::Placeholder(name)) => {
                    self.reg_string(name.clone());
                    self.tokens.push(Token::AppendToText(Content::Placeholder(name)));
                },
                ReturnType::Logic(_) => return Err(Cow::Borrowed("logic can not be used as text")),
                ReturnType::Scope(scope) => {
                    self.reg_scope_vars(&scope);
                    self.tokens.push(Token::Scope(scope));
                },
                ReturnType::End => self.tokens.push(Token::End),
            }
        }
        self.tokens.push(Token::EndText);

        Ok(())
    }

    fn close_tag(&mut self, tag: Atom) {
        self.tokens.push(Token::CloseTag(tag));
    }

    fn reg_scope_vars(&mut self, scope: &Scope) {
        match scope {
            &Scope::If(ref logic) => logic.parameters(&mut |param| self.reg_bool(param.clone()))
        }
    }

    fn reg_string(&mut self, param: String) {
        match self.parameters.entry(param) {
            Entry::Occupied(mut entry) => match entry.get_mut() {
                &mut ContentType::String => {},
                entry => *entry = ContentType::OptionalString
            },
            Entry::Vacant(entry) => { entry.insert(ContentType::String); },
        }
    }

    fn reg_bool(&mut self, param: String) {
        match self.parameters.entry(param) {
            Entry::Occupied(mut entry) => match entry.get_mut() {
                &mut ContentType::Bool => {},
                entry => *entry = ContentType::OptionalString
            },
            Entry::Vacant(entry) => { entry.insert(ContentType::Bool); },
        }
    }
}

impl<'a, 'b: 'a> TokenSink for &'a mut Template<'b> {
    fn process_token(&mut self, token: HtmlToken) {
        match token {
            HtmlToken::TagToken(Tag {
                kind: TagKind::StartTag,
                name,
                attrs,
                self_closing
            }) => if let Err(e) = self.open_tag(name, attrs, self_closing) {
                self.errors.push(e);
            },
            HtmlToken::TagToken(Tag {
                kind: TagKind::EndTag,
                name,
                ..
            }) => self.close_tag(name),
            HtmlToken::CharacterTokens(text) => if let Err(e) = self.add_text(text) {
                self.errors.push(e);
            },

            HtmlToken::DoctypeToken(_) => {},
            HtmlToken::CommentToken(_) => {},
            HtmlToken::NullCharacterToken => {},
            HtmlToken::EOFToken => {},

            HtmlToken::ParseError(e) => self.errors.push(e)
        }
    }
}

// http://www.w3.org/TR/html5/syntax.html#void-elements
fn is_void(tag: &str) -> bool {
    match tag {
        "area" |
        "base" |
        "br" |
        "col" |
        "embed" |
        "hr" |
        "img" |
        "input" |
        "keygen" |
        "link" |
        "meta" |
        "param" |
        "source" |
        "track" |
        "wbr" => true,
        _ => false
    }
}

fn init_fragments<'a>() -> HashMap<&'static str, Box<Fragment + 'a>> {
    let mut map = HashMap::new();

    let f: Box<Fragment + 'a> = Box::new(fragments::If);
    map.insert(f.identifier(), f);

    let f = fragments::And;
    map.insert(f.identifier(), Box::new(f));

    let f = fragments::Or;
    map.insert(f.identifier(), Box::new(f));

    let f = fragments::Not;
    map.insert(f.identifier(), Box::new(f));

    map
}<commit_msg>Rust update<commit_after>extern crate html5ever;
extern crate string_cache;

use std::path::Path;
use std::fs::{File, read_dir};
use std::io::{self, Read, Write};
use std::default::Default;
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fmt;
use std::mem::swap;

use html5ever::Attribute;
use html5ever::tokenizer::{Tokenizer, TokenSink, Tag, TagKind};
use html5ever::tokenizer::Token as HtmlToken;

use string_cache::atom::Atom;

use codegen::{Token, Content, Codegen, ContentType, Scope};
use fragments::{Fragment, ReturnType};

use parser::ExtensibleMap;

#[macro_use]
pub mod codegen;
pub mod fragments;
pub mod javascript;
pub mod rust;
mod parser;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Format(fmt::Error),
    Parse(Vec<Cow<'static, str>>)
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::Io(e)
    }
}

impl From<fmt::Error> for Error {
    fn from(e: fmt::Error) -> Error {
        Error::Format(e)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::Io(ref e) => e.fmt(f),
            &Error::Format(ref e) => e.fmt(f),
            &Error::Parse(ref e) => {
                try!(write!(f, "parse errors: "));
                for (i, error) in e.iter().enumerate() {
                    if i == 0 {
                        try!(error.fmt(f));
                    } else {
                        try!(write!(f, ", {}", error));
                    }
                }
                Ok(())
            }
        }
    }
}

///A collection of templates.
pub struct TemplateGroup<'a> {
    templates: Vec<ParsedTemplate>,
    fragments: HashMap<&'static str, Box<Fragment + 'a>>
}

impl<'a> TemplateGroup<'a> {
    pub fn new() -> TemplateGroup<'a> {
        TemplateGroup {
            templates: vec![],
            fragments: init_fragments()
        }
    }

    ///Add a template from a string.
    pub fn parse_string(&mut self, name: String, source: String) -> Result<(), Error> {
        let mut template = Template::extending(&self.fragments);
        try!(template.parse_string(source));
        self.templates.push(ParsedTemplate {
            name: name,
            parameters: template.parameters,
            tokens: template.tokens
        });

        Ok(())
    }

    ///Add every `*.html` and `*.htm` file in a directory to the template group.
    pub fn parse_directory<P: AsRef<Path>>(&mut self, dir: P) -> Result<(), Error> {
        let directory = try!(read_dir(dir));

        for path in directory {
            let path = try!(path).path();
            let info = (
                path.file_stem().and_then(|n| n.to_str()),
                path.extension().and_then(|e| e.to_str())
            );
            if let (Some(name), Some(ext)) = info {
                if ext == "html" || ext == "htm" {
                    let mut source = String::new();
                    try!(File::open(&path).and_then(|mut f| f.read_to_string(&mut source)));
                    try!(self.parse_string(name.to_string(), source));
                }
            }
        }

        Ok(())
    }

    ///Add an already parsed template to the group.
    pub fn add_template(&mut self, name: String, template: Template) {
        self.templates.push(ParsedTemplate {
            name: name,
            parameters: template.parameters,
            tokens: template.tokens
        });
    }

    ///Register a custom fragment.
    pub fn register_fragment<F: Fragment + 'a>(&mut self, fragment: F) {
        let fragment = Box::new(fragment);
        self.fragments.insert(fragment.identifier(), fragment);
    }

    ///Write template code.
    pub fn emit_code<W: Write, C: Codegen>(&self, writer: &mut W, codegen: &C) -> Result<(), Error> {
        codegen.build_module(writer, |w, indent| {
            for template in &self.templates {
                try!(codegen.build_template(w, &template.name, indent, &template.parameters, &template.tokens));
            }
            Ok(())
        })
    }
}

struct ParsedTemplate {
    name: String,
    parameters: HashMap<String, ContentType>,
    tokens: Vec<codegen::Token>
}

///A single template.
pub struct Template<'a> {
    fragments: ExtensibleMap<'a, &'static str, Box<Fragment + 'a>>,
    parameters: HashMap<String, ContentType>,
    tokens: Vec<codegen::Token>,
    errors: Vec<Cow<'static, str>>
}

impl<'a> Template<'a> {
    ///Create an empty template.
    pub fn new() -> Template<'a> {
        Template {
            fragments: ExtensibleMap::Owned(init_fragments()),
            parameters: HashMap::new(),
            tokens: vec![],
            errors: vec![]
        }
    }

    fn extending(fragments: &'a HashMap<&'static str, Box<Fragment + 'a>>) -> Template<'a> {
        Template {
            fragments: ExtensibleMap::extend(fragments),
            parameters: HashMap::new(),
            tokens: vec![],
            errors: vec![]
        }
    }

    ///Add content from a string to the template.
    pub fn parse_string(&mut self, source: String) -> Result<(), Error> {
        {
            let mut tokenizer = Tokenizer::new(&mut*self, Default::default());
            tokenizer.feed(source);
        }

        if self.errors.len() > 0 {
            let mut e = vec![];
            swap(&mut e, &mut self.errors);
            Err(Error::Parse(e))
        } else {
            Ok(())
        }
    }

    ///Register a custom fragment.
    pub fn register_fragment<F: Fragment + 'a>(&mut self, fragment: F) {
        let fragment = Box::new(fragment);
        self.fragments.insert(fragment.identifier(), fragment);
    }

    ///Write template code.
    pub fn emit_code<W: Write, C: Codegen>(&self, template_name: &str, writer: &mut W, codegen: &C) -> Result<(), Error> {
        codegen.build_template(writer, template_name, 0, &self.parameters, &self.tokens)
    }

    fn open_tag(&mut self, name: Atom, attributes: Vec<Attribute>, self_closing: bool) -> Result<(), Cow<'static, str>> {
        let void = is_void(name.as_slice());
        self.tokens.push(Token::BeginTag(name));

        for attribute in attributes {
            let mut content = try!(parser::parse_content(&attribute.value, &self.fragments)).into_iter();
            match content.next() {
                Some(ReturnType::Content(Content::String(text))) => self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::String(text))),
                Some(ReturnType::Content(Content::Placeholder(name))) => {
                    self.reg_string(name.clone());
                    self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::Placeholder(name)));
                },
                Some(ReturnType::Logic(_)) => return Err(Cow::Borrowed("logic can not be used as text")),
                Some(ReturnType::Scope(scope)) => {
                    self.reg_scope_vars(&scope);
                    self.tokens.push(Token::Scope(scope));
                },
                Some(ReturnType::End) => self.tokens.push(Token::End),
                None => self.tokens.push(Token::BeginAttribute(attribute.name.local, Content::String(String::new())))
            }

            for part in content {
                match part {
                    ReturnType::Content(Content::String(text)) => self.tokens.push(Token::AppendToAttribute(Content::String(text))),
                    ReturnType::Content(Content::Placeholder(name)) => {
                        self.reg_string(name.clone());
                        self.tokens.push(Token::AppendToAttribute(Content::Placeholder(name)));
                    },
                    ReturnType::Logic(_) => return Err(Cow::Borrowed("logic can not be used as text")),
                    ReturnType::Scope(scope) => {
                        self.reg_scope_vars(&scope);
                        self.tokens.push(Token::Scope(scope));
                    },
                    ReturnType::End => self.tokens.push(Token::End),
                }
            }

            self.tokens.push(Token::EndAttribute);
        }

        self.tokens.push(Token::EndTag(void || self_closing));

        Ok(())
    }

    fn add_text(&mut self, text: String) -> Result<(), Cow<'static, str>> {
        let mut content = try!(parser::parse_content(&text, &self.fragments)).into_iter();
        match content.next() {
            Some(ReturnType::Content(Content::String(text))) => self.tokens.push(Token::BeginText(Content::String(text))),
            Some(ReturnType::Content(Content::Placeholder(name))) => {
                self.reg_string(name.clone());
                self.tokens.push(Token::BeginText(Content::Placeholder(name)));
            },
            Some(ReturnType::Logic(_)) => return Err(Cow::Borrowed("logic can not be used as text")),
            Some(ReturnType::Scope(scope)) => {
                self.reg_scope_vars(&scope);
                self.tokens.push(Token::Scope(scope));
            },
            Some(ReturnType::End) => self.tokens.push(Token::End),
            None => return Ok(())
        }

        for part in content {
            match part {
                ReturnType::Content(Content::String(text)) => self.tokens.push(Token::AppendToText(Content::String(text))),
                ReturnType::Content(Content::Placeholder(name)) => {
                    self.reg_string(name.clone());
                    self.tokens.push(Token::AppendToText(Content::Placeholder(name)));
                },
                ReturnType::Logic(_) => return Err(Cow::Borrowed("logic can not be used as text")),
                ReturnType::Scope(scope) => {
                    self.reg_scope_vars(&scope);
                    self.tokens.push(Token::Scope(scope));
                },
                ReturnType::End => self.tokens.push(Token::End),
            }
        }
        self.tokens.push(Token::EndText);

        Ok(())
    }

    fn close_tag(&mut self, tag: Atom) {
        self.tokens.push(Token::CloseTag(tag));
    }

    fn reg_scope_vars(&mut self, scope: &Scope) {
        match scope {
            &Scope::If(ref logic) => logic.parameters(&mut |param| self.reg_bool(param.clone()))
        }
    }

    fn reg_string(&mut self, param: String) {
        match self.parameters.entry(param) {
            Entry::Occupied(mut entry) => match entry.get_mut() {
                &mut ContentType::String => {},
                entry => *entry = ContentType::OptionalString
            },
            Entry::Vacant(entry) => { entry.insert(ContentType::String); },
        }
    }

    fn reg_bool(&mut self, param: String) {
        match self.parameters.entry(param) {
            Entry::Occupied(mut entry) => match entry.get_mut() {
                &mut ContentType::Bool => {},
                entry => *entry = ContentType::OptionalString
            },
            Entry::Vacant(entry) => { entry.insert(ContentType::Bool); },
        }
    }
}

impl<'a, 'b: 'a> TokenSink for &'a mut Template<'b> {
    fn process_token(&mut self, token: HtmlToken) {
        match token {
            HtmlToken::TagToken(Tag {
                kind: TagKind::StartTag,
                name,
                attrs,
                self_closing
            }) => if let Err(e) = self.open_tag(name, attrs, self_closing) {
                self.errors.push(e);
            },
            HtmlToken::TagToken(Tag {
                kind: TagKind::EndTag,
                name,
                ..
            }) => self.close_tag(name),
            HtmlToken::CharacterTokens(text) => if let Err(e) = self.add_text(text) {
                self.errors.push(e);
            },

            HtmlToken::DoctypeToken(_) => {},
            HtmlToken::CommentToken(_) => {},
            HtmlToken::NullCharacterToken => {},
            HtmlToken::EOFToken => {},

            HtmlToken::ParseError(e) => self.errors.push(e)
        }
    }
}

// http://www.w3.org/TR/html5/syntax.html#void-elements
fn is_void(tag: &str) -> bool {
    match tag {
        "area" |
        "base" |
        "br" |
        "col" |
        "embed" |
        "hr" |
        "img" |
        "input" |
        "keygen" |
        "link" |
        "meta" |
        "param" |
        "source" |
        "track" |
        "wbr" => true,
        _ => false
    }
}

fn init_fragments<'a>() -> HashMap<&'static str, Box<Fragment + 'a>> {
    let mut map = HashMap::new();

    let f: Box<Fragment + 'a> = Box::new(fragments::If);
    map.insert(f.identifier(), f);

    let f = fragments::And;
    map.insert(f.identifier(), Box::new(f));

    let f = fragments::Or;
    map.insert(f.identifier(), Box::new(f));

    let f = fragments::Not;
    map.insert(f.identifier(), Box::new(f));

    map
}<|endoftext|>


<commit_before>//! Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
//!
//! ## Example
//!
//! ```no_run
//! use actix_web::{get, web, App, HttpServer, Responder};
//!
//! #[get("/{id}/{name}/index.html")]
//! async fn index(path: web::Path<(u32, String)>) -> impl Responder {
//!     let (id, name) = path.into_inner();
//!     format!("Hello {}! id:{}", name, id)
//! }
//!
//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
//!     HttpServer::new(|| App::new().service(index))
//!         .bind("127.0.0.1:8080")?
//!         .run()
//!         .await
//! }
//! ```
//!
//! ## Documentation & Community Resources
//!
//! In addition to this API documentation, several other resources are available:
//!
//! * [Website & User Guide](https://actix.rs/)
//! * [Examples Repository](https://github.com/actix/examples)
//! * [Community Chat on Gitter](https://gitter.im/actix/actix-web)
//!
//! To get started navigating the API docs, you may consider looking at the following pages first:
//!
//! * [`App`]: This struct represents an Actix Web application and is used to
//!   configure routes and other common application settings.
//!
//! * [`HttpServer`]: This struct represents an HTTP server instance and is
//!   used to instantiate and configure servers.
//!
//! * [`web`]: This module provides essential types for route registration as well as
//!   common utilities for request handlers.
//!
//! * [`HttpRequest`] and [`HttpResponse`]: These
//!   structs represent HTTP requests and responses and expose methods for creating, inspecting,
//!   and otherwise utilizing them.
//!
//! ## Features
//!
//! * Supports *HTTP/1.x* and *HTTP/2*
//! * Streaming and pipelining
//! * Keep-alive and slow requests handling
//! * Client/server [WebSockets](https://actix.rs/docs/websockets/) support
//! * Transparent content compression/decompression (br, gzip, deflate)
//! * Powerful [request routing](https://actix.rs/docs/url-dispatch/)
//! * Multipart streams
//! * Static assets
//! * SSL support using OpenSSL or Rustls
//! * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
//! * Includes an async [HTTP client](https://docs.rs/awc/)
//! * Runs on stable Rust 1.46+
//!
//! ## Crate Features
//!
//! * `compress` - content encoding compression support (enabled by default)
//! * `cookies` - cookies support (enabled by default)
//! * `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`
//! * `rustls` - HTTPS support via `rustls` crate, supports `HTTP/2`
//! * `secure-cookies` - secure cookies support

#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]

mod app;
mod app_service;
mod config;
mod data;
pub mod error;
mod extract;
pub mod guard;
mod handler;
pub mod http;
mod info;
pub mod middleware;
mod request;
mod request_data;
mod resource;
mod responder;
mod response;
mod rmap;
mod route;
mod scope;
mod server;
mod service;
pub mod test;
pub(crate) mod types;
pub mod web;

pub use actix_http::Response as BaseHttpResponse;
pub use actix_http::{body, Error, HttpMessage, ResponseError};
#[doc(inline)]
pub use actix_rt as rt;
pub use actix_web_codegen::*;
#[cfg(feature = "cookies")]
pub use cookie;

pub use crate::app::App;
pub use crate::error::Result;
pub use crate::extract::FromRequest;
pub use crate::request::HttpRequest;
pub use crate::resource::Resource;
pub use crate::responder::Responder;
pub use crate::response::{HttpResponse, HttpResponseBuilder};
pub use crate::route::Route;
pub use crate::scope::Scope;
pub use crate::server::HttpServer;
// TODO: is exposing the error directly really needed
pub use crate::types::{Either, EitherExtractError};

pub mod dev {
    //! The `actix-web` prelude for library developers
    //!
    //! The purpose of this module is to alleviate imports of many common actix
    //! traits by adding a glob import to the top of actix heavy modules:
    //!
    //! ```
    //! # #![allow(unused_imports)]
    //! use actix_web::dev::*;
    //! ```

    pub use crate::config::{AppConfig, AppService};
    #[doc(hidden)]
    pub use crate::handler::Handler;
    pub use crate::info::ConnectionInfo;
    pub use crate::rmap::ResourceMap;
    pub use crate::service::{HttpServiceFactory, ServiceRequest, ServiceResponse, WebService};

    pub use crate::types::form::UrlEncoded;
    pub use crate::types::json::JsonBody;
    pub use crate::types::readlines::Readlines;

    pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream};
    #[cfg(feature = "compress")]
    pub use actix_http::encoding::Decoder as Decompress;
    pub use actix_http::ResponseBuilder as BaseHttpResponseBuilder;
    pub use actix_http::{Extensions, Payload, PayloadStream, RequestHead, ResponseHead};
    pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
    pub use actix_server::Server;
    pub use actix_service::{always_ready, forward_ready, Service, Transform};

    pub(crate) fn insert_slash(mut patterns: Vec<String>) -> Vec<String> {
        for path in &mut patterns {
            if !path.is_empty() && !path.starts_with('/') {
                path.insert(0, '/');
            };
        }
        patterns
    }

    use crate::http::header::ContentEncoding;
    use actix_http::{Response, ResponseBuilder};

    struct Enc(ContentEncoding);

    /// Helper trait that allows to set specific encoding for response.
    pub trait BodyEncoding {
        /// Get content encoding
        fn get_encoding(&self) -> Option<ContentEncoding>;

        /// Set content encoding
        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self;
    }

    impl BodyEncoding for ResponseBuilder {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl<B> BodyEncoding for Response<B> {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl BodyEncoding for crate::HttpResponseBuilder {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl<B> BodyEncoding for crate::HttpResponse<B> {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }
}
<commit_msg>Add information about Actix discord server (#2247)<commit_after>//! Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
//!
//! ## Example
//!
//! ```no_run
//! use actix_web::{get, web, App, HttpServer, Responder};
//!
//! #[get("/{id}/{name}/index.html")]
//! async fn index(path: web::Path<(u32, String)>) -> impl Responder {
//!     let (id, name) = path.into_inner();
//!     format!("Hello {}! id:{}", name, id)
//! }
//!
//! #[actix_web::main]
//! async fn main() -> std::io::Result<()> {
//!     HttpServer::new(|| App::new().service(index))
//!         .bind("127.0.0.1:8080")?
//!         .run()
//!         .await
//! }
//! ```
//!
//! ## Documentation & Community Resources
//!
//! In addition to this API documentation, several other resources are available:
//!
//! * [Website & User Guide](https://actix.rs/)
//! * [Examples Repository](https://github.com/actix/examples)
//! * [Community Chat on Discord](https://discord.gg/NWpN5mmg3x)
//! * [Community Chat on Gitter](https://gitter.im/actix/actix-web)
//!
//! To get started navigating the API docs, you may consider looking at the following pages first:
//!
//! * [`App`]: This struct represents an Actix Web application and is used to
//!   configure routes and other common application settings.
//!
//! * [`HttpServer`]: This struct represents an HTTP server instance and is
//!   used to instantiate and configure servers.
//!
//! * [`web`]: This module provides essential types for route registration as well as
//!   common utilities for request handlers.
//!
//! * [`HttpRequest`] and [`HttpResponse`]: These
//!   structs represent HTTP requests and responses and expose methods for creating, inspecting,
//!   and otherwise utilizing them.
//!
//! ## Features
//!
//! * Supports *HTTP/1.x* and *HTTP/2*
//! * Streaming and pipelining
//! * Keep-alive and slow requests handling
//! * Client/server [WebSockets](https://actix.rs/docs/websockets/) support
//! * Transparent content compression/decompression (br, gzip, deflate)
//! * Powerful [request routing](https://actix.rs/docs/url-dispatch/)
//! * Multipart streams
//! * Static assets
//! * SSL support using OpenSSL or Rustls
//! * Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/))
//! * Includes an async [HTTP client](https://docs.rs/awc/)
//! * Runs on stable Rust 1.46+
//!
//! ## Crate Features
//!
//! * `compress` - content encoding compression support (enabled by default)
//! * `cookies` - cookies support (enabled by default)
//! * `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`
//! * `rustls` - HTTPS support via `rustls` crate, supports `HTTP/2`
//! * `secure-cookies` - secure cookies support

#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::needless_doctest_main, clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]

mod app;
mod app_service;
mod config;
mod data;
pub mod error;
mod extract;
pub mod guard;
mod handler;
pub mod http;
mod info;
pub mod middleware;
mod request;
mod request_data;
mod resource;
mod responder;
mod response;
mod rmap;
mod route;
mod scope;
mod server;
mod service;
pub mod test;
pub(crate) mod types;
pub mod web;

pub use actix_http::Response as BaseHttpResponse;
pub use actix_http::{body, Error, HttpMessage, ResponseError};
#[doc(inline)]
pub use actix_rt as rt;
pub use actix_web_codegen::*;
#[cfg(feature = "cookies")]
pub use cookie;

pub use crate::app::App;
pub use crate::error::Result;
pub use crate::extract::FromRequest;
pub use crate::request::HttpRequest;
pub use crate::resource::Resource;
pub use crate::responder::Responder;
pub use crate::response::{HttpResponse, HttpResponseBuilder};
pub use crate::route::Route;
pub use crate::scope::Scope;
pub use crate::server::HttpServer;
// TODO: is exposing the error directly really needed
pub use crate::types::{Either, EitherExtractError};

pub mod dev {
    //! The `actix-web` prelude for library developers
    //!
    //! The purpose of this module is to alleviate imports of many common actix
    //! traits by adding a glob import to the top of actix heavy modules:
    //!
    //! ```
    //! # #![allow(unused_imports)]
    //! use actix_web::dev::*;
    //! ```

    pub use crate::config::{AppConfig, AppService};
    #[doc(hidden)]
    pub use crate::handler::Handler;
    pub use crate::info::ConnectionInfo;
    pub use crate::rmap::ResourceMap;
    pub use crate::service::{HttpServiceFactory, ServiceRequest, ServiceResponse, WebService};

    pub use crate::types::form::UrlEncoded;
    pub use crate::types::json::JsonBody;
    pub use crate::types::readlines::Readlines;

    pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream};
    #[cfg(feature = "compress")]
    pub use actix_http::encoding::Decoder as Decompress;
    pub use actix_http::ResponseBuilder as BaseHttpResponseBuilder;
    pub use actix_http::{Extensions, Payload, PayloadStream, RequestHead, ResponseHead};
    pub use actix_router::{Path, ResourceDef, ResourcePath, Url};
    pub use actix_server::Server;
    pub use actix_service::{always_ready, forward_ready, Service, Transform};

    pub(crate) fn insert_slash(mut patterns: Vec<String>) -> Vec<String> {
        for path in &mut patterns {
            if !path.is_empty() && !path.starts_with('/') {
                path.insert(0, '/');
            };
        }
        patterns
    }

    use crate::http::header::ContentEncoding;
    use actix_http::{Response, ResponseBuilder};

    struct Enc(ContentEncoding);

    /// Helper trait that allows to set specific encoding for response.
    pub trait BodyEncoding {
        /// Get content encoding
        fn get_encoding(&self) -> Option<ContentEncoding>;

        /// Set content encoding
        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self;
    }

    impl BodyEncoding for ResponseBuilder {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl<B> BodyEncoding for Response<B> {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl BodyEncoding for crate::HttpResponseBuilder {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }

    impl<B> BodyEncoding for crate::HttpResponse<B> {
        fn get_encoding(&self) -> Option<ContentEncoding> {
            self.extensions().get::<Enc>().map(|enc| enc.0)
        }

        fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self {
            self.extensions_mut().insert(Enc(encoding));
            self
        }
    }
}
<|endoftext|>


<commit_before>use components::ConstantPoolItem;
use primitives::{PrimitiveIterator, U1, U2, U4};

use std::fs::File;
use std::io::{Error as IoError, Read};
use std::string::FromUtf8Error;

pub mod components;
pub mod primitives;

pub type ParserResult<T> = Result<T, ParserError>;

#[derive(Debug)]
pub enum ParserError {
    UnknownConstantPoolTag(U1),
    UnexpectedConstantPoolItem(&'static str),
    ConstantPoolIndexOutOfBounds(usize),
    InvalidUtf8(FromUtf8Error),
    Io(IoError),
}

impl From<IoError> for ParserError {
    fn from(error: IoError) -> ParserError {
        ParserError::Io(error)
    }
}

impl From<FromUtf8Error> for ParserError {
    fn from(error: FromUtf8Error) -> ParserError {
        ParserError::InvalidUtf8(error)
    }
}

#[derive(Debug)]
pub struct ClassFile {
    pub magic: U4,
    pub minor_version: U2,
    pub major_version: U2,
    pub constant_pool_count: U2,
    pub constant_pool: Vec<ConstantPoolItem>,
    pub access_flags: U2,
    pub this_class: U2,
    pub super_class: U2,
}

impl ClassFile {
    pub fn from(file: File) -> ParserResult<ClassFile> {
        let mut bytes = file.bytes();

        let magic = try!(bytes.next_u4());
        let minor_version = try!(bytes.next_u2());
        let major_version = try!(bytes.next_u2());

        let constant_pool_count = try!(bytes.next_u2());
        let mut constant_pool = vec![];
        for _ in 1..constant_pool_count {
            constant_pool.push(try!(ConstantPoolItem::from(&mut bytes)));
        }

        let access_flags = try!(bytes.next_u2());
        let this_class = try!(bytes.next_u2());
        let super_class = try!(bytes.next_u2());

        Ok(ClassFile {
            magic: magic,
            minor_version: minor_version,
            major_version: major_version,
            constant_pool_count: constant_pool_count,
            constant_pool: constant_pool,
            access_flags: access_flags,
            this_class: this_class,
            super_class: super_class,
        })
    }
}

#[cfg(test)]
mod tests {

    extern crate spectral;

    use self::spectral::prelude::*;

    use super::ClassFile;
    use super::components::{AccessFlags, ConstantPoolItem};

    use std::fs::File;
    use std::path::PathBuf;

    const MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR");

    #[test]
    fn can_successfully_parse_magic() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.magic).is_equal_to(&0xCAFEBABE);
    }

    #[test]
    fn can_successfully_parse_version() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.minor_version).is_equal_to(&0);
        assert_that(&classfile.major_version).is_equal_to(&52);
    }

    #[test]
    fn can_successfully_parse_constant_pool() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.constant_pool_count).is_equal_to(&26);
        assert_that(&classfile.constant_pool).has_length(25);
    }

    #[test]
    fn can_successfully_parse_access_flags() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        let access_flags = classfile.access_flags;
        asserting("class is public")
            .that(&access_flags)
            .matches(|val| AccessFlags::is_public(*val));
        asserting("class is super").that(&access_flags).matches(|val| AccessFlags::is_super(*val));
    }

    #[test]
    fn can_successfully_parse_class_references() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        let this_class = classfile.this_class;
        let super_class = classfile.super_class;
        let constant_pool = classfile.constant_pool;

        assert_that(&ConstantPoolItem::retrieve_class_info(this_class as usize, &constant_pool))
            .is_ok();
        assert_that(&ConstantPoolItem::retrieve_class_info(super_class as usize, &constant_pool))
            .is_ok();
    }

    fn open_test_resource(resource_path: &str) -> File {
        let mut file_path = PathBuf::from(MANIFEST_DIR);
        file_path.push("test-resources/");
        file_path.push(resource_path);

        File::open(file_path).unwrap()
    }

}
<commit_msg>Can now parse interface references<commit_after>use components::ConstantPoolItem;
use primitives::{PrimitiveIterator, U1, U2, U4};

use std::fs::File;
use std::io::{Error as IoError, Read};
use std::string::FromUtf8Error;

pub mod components;
pub mod primitives;

pub type ParserResult<T> = Result<T, ParserError>;

#[derive(Debug)]
pub enum ParserError {
    UnknownConstantPoolTag(U1),
    UnexpectedConstantPoolItem(&'static str),
    ConstantPoolIndexOutOfBounds(usize),
    InvalidUtf8(FromUtf8Error),
    Io(IoError),
}

impl From<IoError> for ParserError {
    fn from(error: IoError) -> ParserError {
        ParserError::Io(error)
    }
}

impl From<FromUtf8Error> for ParserError {
    fn from(error: FromUtf8Error) -> ParserError {
        ParserError::InvalidUtf8(error)
    }
}

#[derive(Debug)]
pub struct ClassFile {
    pub magic: U4,
    pub minor_version: U2,
    pub major_version: U2,
    pub constant_pool_count: U2,
    pub constant_pool: Vec<ConstantPoolItem>,
    pub access_flags: U2,
    pub this_class: U2,
    pub super_class: U2,
    pub interfaces_count: U2,
    pub interfaces: Vec<U2>,
}

impl ClassFile {
    pub fn from(file: File) -> ParserResult<ClassFile> {
        let mut bytes = file.bytes();

        let magic = try!(bytes.next_u4());
        let minor_version = try!(bytes.next_u2());
        let major_version = try!(bytes.next_u2());

        let constant_pool_count = try!(bytes.next_u2());
        let mut constant_pool = vec![];
        for _ in 1..constant_pool_count {
            constant_pool.push(try!(ConstantPoolItem::from(&mut bytes)));
        }

        let access_flags = try!(bytes.next_u2());
        let this_class = try!(bytes.next_u2());
        let super_class = try!(bytes.next_u2());

        let interfaces_count = try!(bytes.next_u2());
        let mut interfaces = vec![];
        for _ in 0..interfaces_count {
            interfaces.push(try!(bytes.next_u2()));
        }

        Ok(ClassFile {
            magic: magic,
            minor_version: minor_version,
            major_version: major_version,
            constant_pool_count: constant_pool_count,
            constant_pool: constant_pool,
            access_flags: access_flags,
            this_class: this_class,
            super_class: super_class,
            interfaces_count: interfaces_count,
            interfaces: interfaces,
        })
    }
}

#[cfg(test)]
mod tests {

    extern crate spectral;

    use self::spectral::prelude::*;

    use super::ClassFile;
    use super::components::{AccessFlags, ConstantPoolItem};

    use std::fs::File;
    use std::path::PathBuf;

    const MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR");

    #[test]
    fn can_successfully_parse_magic() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.magic).is_equal_to(&0xCAFEBABE);
    }

    #[test]
    fn can_successfully_parse_version() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.minor_version).is_equal_to(&0);
        assert_that(&classfile.major_version).is_equal_to(&52);
    }

    #[test]
    fn can_successfully_parse_constant_pool() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.constant_pool_count).is_equal_to(&26);
        assert_that(&classfile.constant_pool).has_length(25);
    }

    #[test]
    fn can_successfully_parse_access_flags() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        let access_flags = classfile.access_flags;
        asserting("class is public")
            .that(&access_flags)
            .matches(|val| AccessFlags::is_public(*val));
        asserting("class is super").that(&access_flags).matches(|val| AccessFlags::is_super(*val));
    }

    #[test]
    fn can_successfully_parse_class_references() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        let this_class = classfile.this_class;
        let super_class = classfile.super_class;
        let constant_pool = classfile.constant_pool;

        assert_that(&ConstantPoolItem::retrieve_class_info(this_class as usize, &constant_pool))
            .is_ok();
        assert_that(&ConstantPoolItem::retrieve_class_info(super_class as usize, &constant_pool))
            .is_ok();
    }

    #[test]
    fn can_successfully_parse_interfaces() {
        let test_file = open_test_resource("classfile/HelloWorld.class");
        let classfile = ClassFile::from(test_file).unwrap();

        assert_that(&classfile.interfaces_count).is_equal_to(&0);
        assert_that(&classfile.interfaces).has_length(0);
    }

    fn open_test_resource(resource_path: &str) -> File {
        let mut file_path = PathBuf::from(MANIFEST_DIR);
        file_path.push("test-resources/");
        file_path.push(resource_path);

        File::open(file_path).unwrap()
    }

}
<|endoftext|>


<commit_before>// Copyright 2014 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.

//! A collection of numeric types and traits for Rust.
//!
//! This includes new types for big integers, rationals, and complex numbers,
//! new traits for generic programming on numeric properties like `Integer`,
//! and generic range iterators.
//!
//! ## Example
//!
//! This example uses the BigRational type and [Newton's method][newt] to
//! approximate a square root to arbitrary precision:
//!
//! ```
//! extern crate num;
//! # #[cfg(all(feature = "bigint", feature="rational"))]
//! # pub mod test {
//!
//! use num::FromPrimitive;
//! use num::bigint::BigInt;
//! use num::rational::{Ratio, BigRational};
//!
//! fn approx_sqrt(number: u64, iterations: usize) -> BigRational {
//!     let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
//!     let mut approx = start.clone();
//!
//!     for _ in 0..iterations {
//!         approx = (&approx + (&start / &approx)) /
//!             Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
//!     }
//!
//!     approx
//! }
//! # }
//! # #[cfg(not(all(feature = "bigint", feature="rational")))]
//! # fn approx_sqrt(n: u64, _: usize) -> u64 { n }
//!
//! fn main() {
//!     println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
//! }
//!
//! ```
//!
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
#![doc(html_logo_url = "http://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://rust-num.github.io/num/favicon.ico",
       html_root_url = "http://rust-num.github.io/num/",
       html_playground_url = "http://play.rust-lang.org/")]

#[cfg(feature = "rustc-serialize")]
extern crate rustc_serialize;
#[cfg(feature = "rand")]
extern crate rand;

#[cfg(feature = "bigint")]
pub use bigint::{BigInt, BigUint};
#[cfg(feature = "rational")]
pub use rational::Rational;
#[cfg(all(feature = "rational", feature="bigint"))]
pub use rational::BigRational;
#[cfg(feature = "complex")]
pub use complex::Complex;
pub use integer::Integer;
pub use iter::{range, range_inclusive, range_step, range_step_inclusive};
pub use traits::{Num, Zero, One, Signed, Unsigned, Bounded,
                 Saturating, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
                 PrimInt, Float, ToPrimitive, FromPrimitive, NumCast, cast};

#[cfg(test)] use std::hash;

use std::ops::{Mul};

#[cfg(feature = "bigint")]
pub mod bigint;
pub mod complex;
pub mod integer;
pub mod iter;
pub mod traits;
#[cfg(feature = "rational")]
pub mod rational;

/// Returns the additive identity, `0`.
#[inline(always)] pub fn zero<T: Zero>() -> T { Zero::zero() }

/// Returns the multiplicative identity, `1`.
#[inline(always)] pub fn one<T: One>() -> T { One::one() }

/// Computes the absolute value.
///
/// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`
///
/// For signed integers, `::MIN` will be returned if the number is `::MIN`.
#[inline(always)]
pub fn abs<T: Signed>(value: T) -> T {
    value.abs()
}

/// The positive difference of two numbers.
///
/// Returns zero if `x` is less than or equal to `y`, otherwise the difference
/// between `x` and `y` is returned.
#[inline(always)]
pub fn abs_sub<T: Signed>(x: T, y: T) -> T {
    x.abs_sub(&y)
}

/// Returns the sign of the number.
///
/// For `f32` and `f64`:
///
/// * `1.0` if the number is positive, `+0.0` or `INFINITY`
/// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// * `NaN` if the number is `NaN`
///
/// For signed integers:
///
/// * `0` if the number is zero
/// * `1` if the number is positive
/// * `-1` if the number is negative
#[inline(always)] pub fn signum<T: Signed>(value: T) -> T { value.signum() }

/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// # Example
///
/// ```rust
/// use num;
///
/// assert_eq!(num::pow(2i8, 4), 16);
/// assert_eq!(num::pow(6u8, 3), 216);
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
    if exp == 1 { base }
    else {
        let mut acc = one::<T>();
        while exp > 0 {
            if (exp & 1) == 1 {
                acc = acc * base.clone();
            }

            // avoid overflow if we won't need it
            if exp > 1 {
                base = base.clone() * base;
            }
            exp = exp >> 1;
        }
        acc
    }
}

#[cfg(test)]
fn hash<T: hash::Hash>(x: &T) -> u64 {
    use std::hash::Hasher;
    let mut hasher = hash::SipHasher::new();
    x.hash(&mut hasher);
    hasher.finish()
}
<commit_msg>Fix the feature masks in the approx_sqrt doc example<commit_after>// Copyright 2014 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.

//! A collection of numeric types and traits for Rust.
//!
//! This includes new types for big integers, rationals, and complex numbers,
//! new traits for generic programming on numeric properties like `Integer`,
//! and generic range iterators.
//!
//! ## Example
//!
//! This example uses the BigRational type and [Newton's method][newt] to
//! approximate a square root to arbitrary precision:
//!
//! ```
//! extern crate num;
//! # #[cfg(all(feature = "bigint", feature="rational"))]
//! # mod test {
//!
//! use num::FromPrimitive;
//! use num::bigint::BigInt;
//! use num::rational::{Ratio, BigRational};
//!
//! # pub
//! fn approx_sqrt(number: u64, iterations: usize) -> BigRational {
//!     let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
//!     let mut approx = start.clone();
//!
//!     for _ in 0..iterations {
//!         approx = (&approx + (&start / &approx)) /
//!             Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
//!     }
//!
//!     approx
//! }
//! # }
//! # #[cfg(not(all(feature = "bigint", feature="rational")))]
//! # mod test { pub fn approx_sqrt(n: u64, _: usize) -> u64 { n } }
//! # use test::approx_sqrt;
//!
//! fn main() {
//!     println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
//! }
//!
//! ```
//!
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
#![doc(html_logo_url = "http://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://rust-num.github.io/num/favicon.ico",
       html_root_url = "http://rust-num.github.io/num/",
       html_playground_url = "http://play.rust-lang.org/")]

#[cfg(feature = "rustc-serialize")]
extern crate rustc_serialize;
#[cfg(feature = "rand")]
extern crate rand;

#[cfg(feature = "bigint")]
pub use bigint::{BigInt, BigUint};
#[cfg(feature = "rational")]
pub use rational::Rational;
#[cfg(all(feature = "rational", feature="bigint"))]
pub use rational::BigRational;
#[cfg(feature = "complex")]
pub use complex::Complex;
pub use integer::Integer;
pub use iter::{range, range_inclusive, range_step, range_step_inclusive};
pub use traits::{Num, Zero, One, Signed, Unsigned, Bounded,
                 Saturating, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
                 PrimInt, Float, ToPrimitive, FromPrimitive, NumCast, cast};

#[cfg(test)] use std::hash;

use std::ops::{Mul};

#[cfg(feature = "bigint")]
pub mod bigint;
pub mod complex;
pub mod integer;
pub mod iter;
pub mod traits;
#[cfg(feature = "rational")]
pub mod rational;

/// Returns the additive identity, `0`.
#[inline(always)] pub fn zero<T: Zero>() -> T { Zero::zero() }

/// Returns the multiplicative identity, `1`.
#[inline(always)] pub fn one<T: One>() -> T { One::one() }

/// Computes the absolute value.
///
/// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`
///
/// For signed integers, `::MIN` will be returned if the number is `::MIN`.
#[inline(always)]
pub fn abs<T: Signed>(value: T) -> T {
    value.abs()
}

/// The positive difference of two numbers.
///
/// Returns zero if `x` is less than or equal to `y`, otherwise the difference
/// between `x` and `y` is returned.
#[inline(always)]
pub fn abs_sub<T: Signed>(x: T, y: T) -> T {
    x.abs_sub(&y)
}

/// Returns the sign of the number.
///
/// For `f32` and `f64`:
///
/// * `1.0` if the number is positive, `+0.0` or `INFINITY`
/// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// * `NaN` if the number is `NaN`
///
/// For signed integers:
///
/// * `0` if the number is zero
/// * `1` if the number is positive
/// * `-1` if the number is negative
#[inline(always)] pub fn signum<T: Signed>(value: T) -> T { value.signum() }

/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// # Example
///
/// ```rust
/// use num;
///
/// assert_eq!(num::pow(2i8, 4), 16);
/// assert_eq!(num::pow(6u8, 3), 216);
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
    if exp == 1 { base }
    else {
        let mut acc = one::<T>();
        while exp > 0 {
            if (exp & 1) == 1 {
                acc = acc * base.clone();
            }

            // avoid overflow if we won't need it
            if exp > 1 {
                base = base.clone() * base;
            }
            exp = exp >> 1;
        }
        acc
    }
}

#[cfg(test)]
fn hash<T: hash::Hash>(x: &T) -> u64 {
    use std::hash::Hasher;
    let mut hasher = hash::SipHasher::new();
    x.hash(&mut hasher);
    hasher.finish()
}
<|endoftext|>