<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

#![allow(dead_code)]

#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate libc;
extern crate rand;
extern crate simple_stream;
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate epoll;

pub mod client;
#[cfg(target_os = "linux")]
pub mod server;

/// Initializes all the global things
pub fn init() {
    let _ = fern::init_global_logger(fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            format!("[{}][{}] {}", time::now().strftime("%Y-%m-%d][%H:%M:%S").unwrap(), level, msg)
        }),
        output: vec![fern::OutputConfig::stdout(), fern::OutputConfig::file("/var/log/hydrogen.log")],
        level: log::LogLevelFilter::Trace,
    }, log::LogLevelFilter::Warn);
    trace!("Logger initialized :)");
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

#![allow(dead_code)]

#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate libc;
extern crate rand;
extern crate simple_stream;
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate epoll;

pub mod client;
#[cfg(target_os = "linux")]
pub mod server;

/// Initializes all the global things
pub fn init() {
    let _ = fern::init_global_logger(fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            format!("[{}][{}] {}", time::now().strftime("%Y-%m-%d][%H:%M:%S").unwrap(), level, msg)
        }),
        output: vec![fern::OutputConfig::stdout(), fern::OutputConfig::file("/var/log/hydrogen.log")],
        level: log::LogLevelFilter::Trace,
    }, log::LogLevelFilter::Trace);
    trace!("Logger initialized :)");
}
<|endoftext|>


<commit_before>// XXX: Use a ref-counted gene Pool.
// A population is then only a collection of pool item ids.
// This prevents from duplicating potential large genes.
//
// Use typing to make sure that individuals are evaluated when required.
// fn evaluate(self, evaluator) -> EvaluatedPopulation
// fn add(self, ind) -> Population

#![feature(num_bits_bytes)]
extern crate bit_vec;
extern crate rand;
use rand::{Rng, sample, Rand};

pub mod bit_string;

/// A probability is a value in the range [0.0, 1.0].
/// A probability of 1.0 means `always`, that of 0.0
/// means `never`.
#[derive(Copy, Clone, Debug)]
pub struct Probability(f32);

impl Probability {
    #[inline]
    pub fn new(pb: f32) -> Probability {
        assert!(pb >= 0.0 && pb <= 1.0);
        Probability(pb)
    }
}

impl std::ops::Add<Probability> for Probability {
    type Output = Probability;
    #[inline]
    fn add(self, rhs: Probability) -> Probability {
        Probability::new(self.0 + rhs.0)
    }
}

/// A probability value is a random value in the
/// range [0.0, 1.0).
#[derive(Copy, Clone, Debug)]
pub struct ProbabilityValue(f32);

impl Rand for ProbabilityValue {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> ProbabilityValue {
        let val = rng.gen::<f32>();
        assert!(val >= 0.0 && val < 1.0);
        ProbabilityValue(val)
    }
}

impl ProbabilityValue {
    pub fn is_probable_with(self, pb: Probability) -> bool {
        self.0 < pb.0
    }
}

/// Represents a fitness value.
pub trait Fitness: Clone {
    fn fitter_than(&self, other: &Self) -> bool;
}

impl Fitness for usize {
    fn fitter_than(&self, other: &Self) -> bool {
        self > other
    }
}

/// Represents an individual in a Population.
pub trait Individual: Clone {
}

/// Evaluates the fitness of an Individual.
pub trait Evaluator<I:Individual, F:Fitness> {
    fn fitness(&self, individual: &I) -> F;
}

/// Caches the `fitness` value for an individual.
#[derive(Clone, Debug)]
pub struct EvaluatedIndividual<I:Individual, F:Fitness> {
    individual: I,
    fitness: Option<F>,
}

impl<I:Individual, F:Fitness>  EvaluatedIndividual<I,F> {
    pub fn new(ind: I) -> EvaluatedIndividual<I,F> {
        EvaluatedIndividual{individual: ind, fitness: None}
    }
    pub fn fitness(&self) -> Option<F> {
        self.fitness.clone()
    }
}

/// Manages a population of individuals.
#[derive(Clone, Debug)]
pub struct Population<I:Individual, F:Fitness> {
    population: Vec<EvaluatedIndividual<I,F>>
}

impl<I:Individual, F:Fitness> Population<I,F> {
    pub fn new() -> Population<I,F> {
        Population{population: Vec::new()}
    }

    pub fn with_capacity(capa: usize) -> Population<I,F> {
        Population{population: Vec::with_capacity(capa)}
    }

    pub fn len(&self) -> usize {
        self.population.len()
    }

    pub fn get_ref<'a>(&'a self, idx: usize) -> &'a EvaluatedIndividual<I, F> {
        &self.population[idx]
    }

    pub fn add_individual(&mut self, ind: I) {
        self.population.push(EvaluatedIndividual::new(ind));
    }

    pub fn add(&mut self, ind: EvaluatedIndividual<I,F>) {
        self.population.push(ind);
    }

    /// Evaluates the whole population, i.e. determines the fitness of
    /// each `individual` (unless already calculated).
    pub fn evaluate<E>(&mut self, evaluator: &E) where E:Evaluator<I,F> {
        for i in self.population.iter_mut() {
            if i.fitness.is_some() { continue; }
            i.fitness = Some(evaluator.fitness(&i.individual));
        }
    }

    fn add_population(&mut self, p: &Population<I,F>) {
        for ind in p.population.iter() {
            self.add(ind.clone());
        }
    }
}

/// Select the best individual out of `k` randomly choosen.
/// `n` is the total number of individuals.
pub fn tournament_selection<R:Rng, F, E>(rng: &mut R, evaluate: E, n: usize, k: usize) -> Option<(usize, F)>
  where F:Fitness,
        E:Fn(usize) -> F {
    assert!(n >= k);

    let mut best: Option<(usize, F)> = None;

    let sample = sample(rng, 0..n, k);
    for i in sample {
        let fitness = evaluate(i);
        let better = match best {
            Some((_, ref current_best_fitness)) => {
                fitness.fitter_than(current_best_fitness)
            }
            None => { true }
       };
       if better {
           best = Some((i, fitness));
       }
    }
    return best;
}

pub enum VariationMethod {
    Crossover,
    Mutation,
    Reproduction,
}

/// Mates two individual, producing two children.
pub trait OpCrossover<I: Individual> {
    fn crossover(&mut self, parent1: &I, parent2: &I) -> (I, I);
}

/// Mutates an individual.
pub trait OpMutate<I: Individual> {
    fn mutate(&mut self, ind: &I) -> I;
}

/// Selects a variation method to use.
pub trait OpVariation {
    fn variation(&mut self) -> VariationMethod;
}

/// Selects a random individual from the population.
pub trait OpSelectRandomIndividual<I: Individual, F: Fitness> {
    fn select_random_individual<'a>(&mut self, population: &'a Population<I, F>) -> &'a EvaluatedIndividual<I, F>;
}

/// Produce new generation through selection of \mu individuals from population.
pub trait OpSelect<I: Individual, F: Fitness> {
    fn select(&mut self, population: &Population<I,F>, mu: usize) -> Population<I,F>;
}

pub fn variation_or<I, F, T>(toolbox: &mut T, population: &Population<I,F>, lambda: usize) -> Population<I,F>
where I: Individual,
      F: Fitness,
      T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I,F>
{
    let mut offspring = Population::with_capacity(lambda);
    // Each step produces exactly one child.
    for _ in 0..lambda {
        let method = toolbox.variation();
        let child = match method {
            VariationMethod::Crossover => {
                // select two individuals and mate them.
                // only the first offspring is used, the second is thrown away.
                let parent1 = toolbox.select_random_individual(population);
                let parent2 = toolbox.select_random_individual(population);
                let (child1, _child2) = toolbox.crossover(&parent1.individual, &parent2.individual);
                EvaluatedIndividual::new(child1)
            }
            VariationMethod::Mutation => {
                // select a single individual and mutate it.
                let ind = toolbox.select_random_individual(population);
                let child = toolbox.mutate(&ind.individual);
                EvaluatedIndividual::new(child)
            }
            VariationMethod::Reproduction => {
                let ind = toolbox.select_random_individual(population);
                ind.clone()
            }
        };
        offspring.add(child);
    }
    return offspring;
}

// The (\mu + \lambda) algorithm.
// From `population`, \lambda offspring is produced, through either
// mutation, crossover or random reproduction.
// For the next generation, \mu individuals are selected from the \mu + \lambda (parents and offspring).
pub fn ea_mu_plus_lambda<I,F,T,E>(toolbox: &mut T, evaluator: &E, population: &Population<I,F>, mu: usize, lambda: usize, num_generations: usize) -> Population<I,F>
where I: Individual,
      F: Fitness,
      T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I,F> + OpSelect<I,F>,
      E: Evaluator<I,F>
{
    let mut p = population.clone();
    p.evaluate(evaluator);

    for _gen in 0..num_generations {
        // evaluate population. make sure that every individual has been rated.
        let mut offspring = variation_or(toolbox, &p, lambda);
        offspring.add_population(&p);
        offspring.evaluate(evaluator);
        // select from offspring the `best` individuals
        p = toolbox.select(&offspring, mu);
    }

    return p;
}
<commit_msg>Add MaxFitness and MinFitness wrappers<commit_after>// XXX: Use a ref-counted gene Pool.
// A population is then only a collection of pool item ids.
// This prevents from duplicating potential large genes.
//
// Use typing to make sure that individuals are evaluated when required.
// fn evaluate(self, evaluator) -> EvaluatedPopulation
// fn add(self, ind) -> Population

#![feature(num_bits_bytes)]
extern crate bit_vec;
extern crate rand;
use rand::{Rng, sample, Rand};
use std::cmp::PartialOrd;

pub mod bit_string;

/// A probability is a value in the range [0.0, 1.0].
/// A probability of 1.0 means `always`, that of 0.0
/// means `never`.
#[derive(Copy, Clone, Debug)]
pub struct Probability(f32);

impl Probability {
    #[inline]
    pub fn new(pb: f32) -> Probability {
        assert!(pb >= 0.0 && pb <= 1.0);
        Probability(pb)
    }
}

impl std::ops::Add<Probability> for Probability {
    type Output = Probability;
    #[inline]
    fn add(self, rhs: Probability) -> Probability {
        Probability::new(self.0 + rhs.0)
    }
}

/// A probability value is a random value in the
/// range [0.0, 1.0).
#[derive(Copy, Clone, Debug)]
pub struct ProbabilityValue(f32);

impl Rand for ProbabilityValue {
    #[inline]
    fn rand<R: Rng>(rng: &mut R) -> ProbabilityValue {
        let val = rng.gen::<f32>();
        assert!(val >= 0.0 && val < 1.0);
        ProbabilityValue(val)
    }
}

impl ProbabilityValue {
    pub fn is_probable_with(self, pb: Probability) -> bool {
        self.0 < pb.0
    }
}

/// Represents a fitness value.
pub trait Fitness: Clone {
    fn fitter_than(&self, other: &Self) -> bool;
}

/// Maximizes the fitness value as objective.
#[derive(Copy, Clone, Debug)]
pub struct MaxFitness<T:PartialOrd+Clone>(pub T);
impl<T:PartialOrd+Clone> Fitness for MaxFitness<T> {
    fn fitter_than(&self, other: &MaxFitness<T>) -> bool {
        self.0 > other.0
    }
}

/// Minimizes the fitness value as objective.
#[derive(Copy, Clone, Debug)]
pub struct MinFitness<T:PartialOrd+Clone>(pub T);
impl<T:PartialOrd+Clone> Fitness for MinFitness<T> {
    fn fitter_than(&self, other: &MinFitness<T>) -> bool {
        self.0 < other.0
    }
}

impl Fitness for usize {
    fn fitter_than(&self, other: &Self) -> bool {
        self > other
    }
}

/// Represents an individual in a Population.
pub trait Individual: Clone {
}

/// Evaluates the fitness of an Individual.
pub trait Evaluator<I:Individual, F:Fitness> {
    fn fitness(&self, individual: &I) -> F;
}

/// Caches the `fitness` value for an individual.
#[derive(Clone, Debug)]
pub struct EvaluatedIndividual<I:Individual, F:Fitness> {
    individual: I,
    fitness: Option<F>,
}

impl<I:Individual, F:Fitness>  EvaluatedIndividual<I,F> {
    pub fn new(ind: I) -> EvaluatedIndividual<I,F> {
        EvaluatedIndividual{individual: ind, fitness: None}
    }
    pub fn fitness(&self) -> Option<F> {
        self.fitness.clone()
    }
}

/// Manages a population of individuals.
#[derive(Clone, Debug)]
pub struct Population<I:Individual, F:Fitness> {
    population: Vec<EvaluatedIndividual<I,F>>
}

impl<I:Individual, F:Fitness> Population<I,F> {
    pub fn new() -> Population<I,F> {
        Population{population: Vec::new()}
    }

    pub fn with_capacity(capa: usize) -> Population<I,F> {
        Population{population: Vec::with_capacity(capa)}
    }

    pub fn len(&self) -> usize {
        self.population.len()
    }

    pub fn get_ref<'a>(&'a self, idx: usize) -> &'a EvaluatedIndividual<I, F> {
        &self.population[idx]
    }

    pub fn add_individual(&mut self, ind: I) {
        self.population.push(EvaluatedIndividual::new(ind));
    }

    pub fn add(&mut self, ind: EvaluatedIndividual<I,F>) {
        self.population.push(ind);
    }

    /// Evaluates the whole population, i.e. determines the fitness of
    /// each `individual` (unless already calculated).
    pub fn evaluate<E>(&mut self, evaluator: &E) where E:Evaluator<I,F> {
        for i in self.population.iter_mut() {
            if i.fitness.is_some() { continue; }
            i.fitness = Some(evaluator.fitness(&i.individual));
        }
    }

    fn add_population(&mut self, p: &Population<I,F>) {
        for ind in p.population.iter() {
            self.add(ind.clone());
        }
    }
}

/// Select the best individual out of `k` randomly choosen.
/// `n` is the total number of individuals.
pub fn tournament_selection<R:Rng, F, E>(rng: &mut R, evaluate: E, n: usize, k: usize) -> Option<(usize, F)>
  where F:Fitness,
        E:Fn(usize) -> F {
    assert!(n >= k);

    let mut best: Option<(usize, F)> = None;

    let sample = sample(rng, 0..n, k);
    for i in sample {
        let fitness = evaluate(i);
        let better = match best {
            Some((_, ref current_best_fitness)) => {
                fitness.fitter_than(current_best_fitness)
            }
            None => { true }
       };
       if better {
           best = Some((i, fitness));
       }
    }
    return best;
}

pub enum VariationMethod {
    Crossover,
    Mutation,
    Reproduction,
}

/// Mates two individual, producing two children.
pub trait OpCrossover<I: Individual> {
    fn crossover(&mut self, parent1: &I, parent2: &I) -> (I, I);
}

/// Mutates an individual.
pub trait OpMutate<I: Individual> {
    fn mutate(&mut self, ind: &I) -> I;
}

/// Selects a variation method to use.
pub trait OpVariation {
    fn variation(&mut self) -> VariationMethod;
}

/// Selects a random individual from the population.
pub trait OpSelectRandomIndividual<I: Individual, F: Fitness> {
    fn select_random_individual<'a>(&mut self, population: &'a Population<I, F>) -> &'a EvaluatedIndividual<I, F>;
}

/// Produce new generation through selection of \mu individuals from population.
pub trait OpSelect<I: Individual, F: Fitness> {
    fn select(&mut self, population: &Population<I,F>, mu: usize) -> Population<I,F>;
}

pub fn variation_or<I, F, T>(toolbox: &mut T, population: &Population<I,F>, lambda: usize) -> Population<I,F>
where I: Individual,
      F: Fitness,
      T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I,F>
{
    let mut offspring = Population::with_capacity(lambda);
    // Each step produces exactly one child.
    for _ in 0..lambda {
        let method = toolbox.variation();
        let child = match method {
            VariationMethod::Crossover => {
                // select two individuals and mate them.
                // only the first offspring is used, the second is thrown away.
                let parent1 = toolbox.select_random_individual(population);
                let parent2 = toolbox.select_random_individual(population);
                let (child1, _child2) = toolbox.crossover(&parent1.individual, &parent2.individual);
                EvaluatedIndividual::new(child1)
            }
            VariationMethod::Mutation => {
                // select a single individual and mutate it.
                let ind = toolbox.select_random_individual(population);
                let child = toolbox.mutate(&ind.individual);
                EvaluatedIndividual::new(child)
            }
            VariationMethod::Reproduction => {
                let ind = toolbox.select_random_individual(population);
                ind.clone()
            }
        };
        offspring.add(child);
    }
    return offspring;
}

// The (\mu + \lambda) algorithm.
// From `population`, \lambda offspring is produced, through either
// mutation, crossover or random reproduction.
// For the next generation, \mu individuals are selected from the \mu + \lambda (parents and offspring).
pub fn ea_mu_plus_lambda<I,F,T,E>(toolbox: &mut T, evaluator: &E, population: &Population<I,F>, mu: usize, lambda: usize, num_generations: usize) -> Population<I,F>
where I: Individual,
      F: Fitness,
      T: OpCrossover<I> + OpMutate<I> + OpVariation + OpSelectRandomIndividual<I,F> + OpSelect<I,F>,
      E: Evaluator<I,F>
{
    let mut p = population.clone();
    p.evaluate(evaluator);

    for _gen in 0..num_generations {
        // evaluate population. make sure that every individual has been rated.
        let mut offspring = variation_or(toolbox, &p, lambda);
        offspring.add_population(&p);
        offspring.evaluate(evaluator);
        // select from offspring the `best` individuals
        p = toolbox.select(&offspring, mu);
    }

    return p;
}
<|endoftext|>


<commit_before>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use bindings::{
    loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64,
};
use libc::{c_int, ioctl};
use std::fs::{File, OpenOptions};
use std::{
    default::Default,
    io,
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[allow(non_snake_case)]
mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

const LOOP_CONTROL: &str = "/dev/loop-control";
#[cfg(not(target_os = "android"))]
const LOOP_PREFIX: &str = "/dev/loop";
#[cfg(target_os = "android")]
const LOOP_PREFIX: &str = "/dev/block/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(self.dev_file.as_raw_fd() as c_int, LOOP_CTL_GET_FREE.into())
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file with given options.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let mut ld = LoopDevice::open("/dev/loop3").unwrap();
    /// ld.with().part_scan(true).attach("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn with(&mut self) -> AttachOptions<'_> {
        AttachOptions {
            device: self,
            info: Default::default(),
        }
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `loop.with().offset(offset).attach(file)` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        let info = loop_info64 {
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.2",
        note = "use `loop.with().offset(offset).attach(file)` instead"
    )]
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    #[deprecated(
        since = "0.2.2",
        note = "use `with().size_limit(size).attach(file)` instead"
    )]
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        size_limit: u64,
    ) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            lo_sizelimit: size_limit,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info64.
    fn attach_with_loop_info(
        &self, // TODO should be mut? - but changing it is a breaking change
        backing_file: impl AsRef<Path>,
        info: loop_info64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD.into(),
                bf.as_raw_fd() as c_int,
            )
        })?;

        if let Err(err) = ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64.into(),
                &info,
            )
        }) {
            // Ignore the error to preserve the original error
            let _ = self.detach();
            return Err(err);
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe { ioctl(self.device.as_raw_fd() as c_int, LOOP_CLR_FD.into(), 0) })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY.into(),
                0,
            )
        })?;
        Ok(())
    }
}

/// Used to set options when attaching a device. Created with [LoopDevice::with()].
///
/// # Examples
///
/// Enable partition scanning on attach:
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop6").unwrap();
/// ld.with()
///     .part_scan(true)
///     .attach("test.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// A 1MiB slice of the file located at 1KiB into the file.
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop7").unwrap();
/// ld.with()
///     .offset(1024*1024)
///     .size_limit(1024*1024*1024)
///     .attach("test.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
pub struct AttachOptions<'d> {
    device: &'d mut LoopDevice,
    info: loop_info64,
}

impl AttachOptions<'_> {
    /// Offset in bytes from the start of the backing file the data will start at.
    pub fn offset(mut self, offset: u64) -> Self {
        self.info.lo_offset = offset;
        self
    }

    /// Maximum size of the data in bytes.
    pub fn size_limit(mut self, size_limit: u64) -> Self {
        self.info.lo_sizelimit = size_limit;
        self
    }

    /// Force the kernel to scan the partition table on a newly created loop device. Note that the
    /// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
    pub fn part_scan(mut self, enable: bool) -> Self {
        if enable {
            self.info.lo_flags |= 1 << 4;
        } else {
            self.info.lo_flags &= u32::max_value() - (1 << 4);
        }
        self
    }

    /// Attach the loop device to a file with the set options.
    pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
        self.device.attach_with_loop_info(backing_file, self.info)
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<commit_msg>Bionics ioctl signature differs from glibc<commit_after>//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! ```rust
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("test.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```

extern crate libc;

use bindings::{
    loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64,
};
use libc::{c_int, ioctl};
use std::fs::{File, OpenOptions};
use std::{
    default::Default,
    io,
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[allow(non_snake_case)]
mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

#[cfg(not(target_os = "android"))]
type IoctlRequest = libc::c_ulong;
#[cfg(target_os = "android")]
type IoctlRequest = libc::c_int;

const LOOP_CONTROL: &str = "/dev/loop-control";
#[cfg(not(target_os = "android"))]
const LOOP_PREFIX: &str = "/dev/loop";
#[cfg(target_os = "android")]
const LOOP_PREFIX: &str = "/dev/block/loop";

/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
    dev_file: File,
}

impl LoopControl {
    /// Opens the loop control device.
    pub fn open() -> io::Result<Self> {
        Ok(Self {
            dev_file: OpenOptions::new()
                .read(true)
                .write(true)
                .open(LOOP_CONTROL)?,
        })
    }

    /// Finds and opens the next available loop device.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopControl;
    /// let lc = LoopControl::open().unwrap();
    /// let ld = lc.next_free().unwrap();
    /// println!("{}", ld.path().unwrap().display());
    /// ```
    pub fn next_free(&self) -> io::Result<LoopDevice> {
        let dev_num = ioctl_to_error(unsafe {
            ioctl(
                self.dev_file.as_raw_fd() as c_int,
                LOOP_CTL_GET_FREE as IoctlRequest,
            )
        })?;
        LoopDevice::open(&format!("{}{}", LOOP_PREFIX, dev_num))
    }
}

/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
    device: File,
}

impl AsRawFd for LoopDevice {
    fn as_raw_fd(&self) -> RawFd {
        self.device.as_raw_fd()
    }
}

impl LoopDevice {
    /// Opens a loop device.
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
        Ok(Self {
            device: OpenOptions::new().read(true).write(true).open(dev)?,
        })
    }

    /// Attach the loop device to a file with given options.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let mut ld = LoopDevice::open("/dev/loop3").unwrap();
    /// ld.with().part_scan(true).attach("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn with(&mut self) -> AttachOptions<'_> {
        AttachOptions {
            device: self,
            info: Default::default(),
        }
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.0",
        note = "use `loop.with().offset(offset).attach(file)` instead"
    )]
    pub fn attach<P: AsRef<Path>>(&self, backing_file: P, offset: u64) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file that maps to the whole file.
    ///
    /// # Examples
    ///
    /// Attach the device to a file.
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop4").unwrap();
    /// ld.attach_file("test.img").unwrap();
    /// # ld.detach().unwrap();
    /// ```
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
        let info = loop_info64 {
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file starting at offset into the file.
    #[deprecated(
        since = "0.2.2",
        note = "use `loop.with().offset(offset).attach(file)` instead"
    )]
    pub fn attach_with_offset<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
    ) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file starting at offset into the file and a the given sizelimit.
    #[deprecated(
        since = "0.2.2",
        note = "use `with().size_limit(size).attach(file)` instead"
    )]
    pub fn attach_with_sizelimit<P: AsRef<Path>>(
        &self,
        backing_file: P,
        offset: u64,
        size_limit: u64,
    ) -> io::Result<()> {
        let info = loop_info64 {
            lo_offset: offset,
            lo_sizelimit: size_limit,
            ..Default::default()
        };

        Self::attach_with_loop_info(self, backing_file, info)
    }

    /// Attach the loop device to a file with loop_info64.
    fn attach_with_loop_info(
        &self, // TODO should be mut? - but changing it is a breaking change
        backing_file: impl AsRef<Path>,
        info: loop_info64,
    ) -> io::Result<()> {
        let bf = OpenOptions::new()
            .read(true)
            .write(true)
            .open(backing_file)?;

        // Attach the file
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_FD as IoctlRequest,
                bf.as_raw_fd() as c_int,
            )
        })?;

        if let Err(err) = ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_STATUS64 as IoctlRequest,
                &info,
            )
        }) {
            // Ignore the error to preserve the original error
            let _ = self.detach();
            return Err(err);
        }
        Ok(())
    }

    /// Get the path of the loop device.
    #[deprecated(since = "0.2.0", note = "use `path` instead")]
    pub fn get_path(&self) -> Option<PathBuf> {
        self.path()
    }

    /// Get the path of the loop device.
    pub fn path(&self) -> Option<PathBuf> {
        let mut p = PathBuf::from("/proc/self/fd");
        p.push(self.device.as_raw_fd().to_string());
        std::fs::read_link(&p).ok()
    }

    /// Detach a loop device from its backing file.
    ///
    /// Note that the device won't fully detach until a short delay after the underling device file
    /// gets closed. This happens when LoopDev goes out of scope so you should ensure the LoopDev
    /// lives for a short a time as possible.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use loopdev::LoopDevice;
    /// let ld = LoopDevice::open("/dev/loop5").unwrap();
    /// # ld.attach_file("test.img").unwrap();
    /// ld.detach().unwrap();
    /// ```
    pub fn detach(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_CLR_FD as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }

    /// Resize a live loop device. If the size of the backing file changes this can be called to
    /// inform the loop driver about the new size.
    pub fn set_capacity(&self) -> io::Result<()> {
        ioctl_to_error(unsafe {
            ioctl(
                self.device.as_raw_fd() as c_int,
                LOOP_SET_CAPACITY as IoctlRequest,
                0,
            )
        })?;
        Ok(())
    }
}

/// Used to set options when attaching a device. Created with [LoopDevice::with()].
///
/// # Examples
///
/// Enable partition scanning on attach:
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop6").unwrap();
/// ld.with()
///     .part_scan(true)
///     .attach("test.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// A 1MiB slice of the file located at 1KiB into the file.
///
/// ```rust
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop7").unwrap();
/// ld.with()
///     .offset(1024*1024)
///     .size_limit(1024*1024*1024)
///     .attach("test.img")
///     .unwrap();
/// # ld.detach().unwrap();
/// ```
pub struct AttachOptions<'d> {
    device: &'d mut LoopDevice,
    info: loop_info64,
}

impl AttachOptions<'_> {
    /// Offset in bytes from the start of the backing file the data will start at.
    pub fn offset(mut self, offset: u64) -> Self {
        self.info.lo_offset = offset;
        self
    }

    /// Maximum size of the data in bytes.
    pub fn size_limit(mut self, size_limit: u64) -> Self {
        self.info.lo_sizelimit = size_limit;
        self
    }

    /// Force the kernel to scan the partition table on a newly created loop device. Note that the
    /// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
    pub fn part_scan(mut self, enable: bool) -> Self {
        if enable {
            self.info.lo_flags |= 1 << 4;
        } else {
            self.info.lo_flags &= u32::max_value() - (1 << 4);
        }
        self
    }

    /// Attach the loop device to a file with the set options.
    pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
        self.device.attach_with_loop_info(backing_file, self.info)
    }
}

fn ioctl_to_error(ret: i32) -> io::Result<i32> {
    if ret < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(ret)
    }
}
<|endoftext|>


<commit_before>//! Please contact me on github and file any issues if you find some, I'm also open to PRs or other suggestions.
//! CHANGELOG (0.2.0):
//! If you're upgrading from iron-tera 0.1.4, the API has changed slightly for serialized values.
//! Serde 0.9.0 to_value returns a `Result`, this means you need to handle the possiblity of a serialization failure.
//! Since we're still pre-1.0 and the API isn't completely stable, I've only incremented the minor version number.
//!
//! ## Examples
//! The following is a complete working example that I've tested with serde 0.9.0, tera 0.7.0 and iron-tera 0.2.0
//! ```rust
//! #[macro_use]
//! extern crate tera;
//!
//! extern crate iron;
//! extern crate router;
//! #[macro_use]
//! extern crate serde_json;
//! extern crate iron_tera;
//!
//! use iron::prelude::*;
//! use iron::status;
//! use router::Router;
//! use tera::Context;
//!
//! use iron_tera::{Template, TeraEngine, TemplateMode};
//!
//! fn main() {
//!     let mut router = Router::new();
//!     router.get("/context", context_handler, "context");
//!     router.get("/json", json_handler, "json");
//!
//!     let mut chain = Chain::new(router);
//!     let teng = TeraEngine::new("src/examples/templates/**/*");
//!     chain.link_after(teng);
//!
//!     Iron::new(chain).http("localhost:5000").unwrap();
//! }
//!
//!
//! fn context_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     let mut context = Context::new();
//!     context.add("username", &"Bob");
//!     context.add("my_var", &"Thing"); // comment out to see alternate thing
//!     context.add("numbers", &vec![1, 2, 3]);
//!     context.add("bio", &"<script>alert('pwnd');</script>");
//!
//!     resp.set_mut(Template::new("users/profile.html", TemplateMode::from_context(context)))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! fn json_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     let blob = json!({
//!         "username": "John Doe",
//!         "my_var": "Thing",
//!         "numbers": [
//!             "1",
//!             "+44 2345678",
//!             "3"
//!         ],
//!         "bio": "<script>alert('pwnd');</script>"
//!      });
//!
//!     resp.set_mut(Template::new("users/profile.html",
//!                                TemplateMode::from_serial(&blob).unwrap()))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
//! Creating a template from a struct
//! ```rust
//! // The following uses serde's Serialize
//! #[derive(Serialize)]
//! struct Product {
//!     name: String,
//!     value: i32,
//! }
//! // Rendering from a struct that implements Serialize
//! fn produce_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     // Using serialized values
//!     let product = Product {
//!         name: "Foo".into(),
//!         value: 42,
//!     };
//!     resp.set_mut(Template::new("product.html", TemplateMode::from_serial(&product).unwrap())) // I made a choice here to return a result and let you handle the serialization error how you see fit.
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
#![allow(dead_code)]
#[macro_use] extern crate serde_json;
#[macro_use] extern crate tera;
extern crate iron;
extern crate serde;

use iron::prelude::*;
use iron::{AfterMiddleware, typemap, status};
use iron::modifier::Modifier;
use iron::headers::ContentType;

use tera::{Tera, Context};

use serde::ser::Serialize;
use serde_json::{Value, to_value};

/// There are 2 main ways to pass data to generate a template.
#[derive(Clone)]
pub enum TemplateMode {
    /// TeraContext constructor takes a `Context`
    TeraContext(Context),
    /// Serialized constructor takes a `Value` from `serde_json`
    Serialized(Value),
}

/// TemplateMode should only ever be created from these smart constructors,
/// not with the enums type constructors.
impl TemplateMode {
    pub fn from_context(context: Context) -> TemplateMode {
        TemplateMode::TeraContext(context)
    }
    pub fn from_serial<S: Serialize>(serialized: S) -> Result<TemplateMode, serde_json::Error> {
        Ok(TemplateMode::Serialized(to_value(serialized)?))
    }
}

/// Our template holds a name (path to template) and a mode (constructed with `from_context` or `from_serial`)
#[derive(Clone)]
pub struct Template {
    mode: TemplateMode,
    name: String,
}

impl Template {
    pub fn new(name: &str, mode: TemplateMode) -> Template {
        Template {
            name: name.into(),
            mode: mode,
        }
    }
}

/// TeraEngine holds the Tera struct so that it can be used by many handlers without explicitly passing
pub struct TeraEngine {
    pub tera: Tera,
}

/// `compile_templates!` is used to parse the contents of a dir for all templates.
impl TeraEngine {
    /// Take a `String` and convert to a slice or preferably a `&str`
    pub fn new(dir: &str) -> TeraEngine {
        TeraEngine { tera: compile_templates!(dir) }
    }
}

impl typemap::Key for TeraEngine {
    type Value = Template;
}

impl Modifier<Response> for Template {
    fn modify(self, resp: &mut Response) {
        resp.extensions.insert::<TeraEngine>(self);
    }
}

/// The middleware implementation for TeraEngine
impl AfterMiddleware for TeraEngine {
    /// This is where all the magic happens. We extract `TeraEngine` from Iron's `Response`,
    /// determine what `TemplateMode` we should render in, and pass the appropriate values to
    /// tera's render methods.
    fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
        let wrapper = resp.extensions.remove::<TeraEngine>().and_then(|t| {
            match t.mode {
                TemplateMode::TeraContext(context) => Some(self.tera.render(&t.name, &context)),
                TemplateMode::Serialized(value) => Some(self.tera.value_render(&t.name, &value)),
            }
        });
        match wrapper {
            Some(result) => {
                result.map_err(|e| IronError::new(e, status::InternalServerError))
                    .and_then(|page| {
                        if !resp.headers.has::<ContentType>() {
                            resp.headers.set(ContentType::html());
                        }
                        resp.set_mut(page);
                        Ok(resp)
                    })
            }
            None => Ok(resp),
        }
    }

    fn catch(&self, req: &mut Request, mut err: IronError) -> IronResult<Response> {
        err.response = self.after(req, err.response)?;
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    use super::{TeraEngine, Template, TemplateMode};
    use iron::prelude::*;
    use tera::{Tera, Context};
    //
    // #[derive(Serialize)]
    // struct Product {
    //     name: String,
    //     value: i32,
    // }

    fn test_resp() -> IronResult<Response> {
        let resp = Response::new();

        let mut context = Context::new();
        context.add("username", &"Foo");
        context.add("bio", &"Bar");
        context.add("numbers", &vec![1, 2, 3]);

        Ok(resp.set(Template::new("users/foo.html", TemplateMode::from_context(context))))
    }

    #[test]
    fn example() {
        let mut resp = test_resp().ok().expect("response expected");

        match resp.get::<TeraEngine>() {
            Ok(h) => {
                assert_eq!(h.name, "users/profile.html".to_string());
                assert_eq!(h.name
                               .as_object()
                               .unwrap()
                               .get(&"Foo".to_string())
                               .unwrap()
                               .as_string()
                               .unwrap(),
                           "Bar");
            }
            _ => panic!("template expected"),
        }
        // let teng = TeraEngine::new("src/test_template/**/*");
        // // Context option 1
        // let mut context = Context::new();
        // context.add("firstname", &"Foo");
        // context.add("lastname", &"Bar");
        //
        // let t = Template::new("index", TemplateMode::TeraContext(context));
        //
        // // Context option 2
        // let mut c2 = Context::new();
        // c2.add("firstname", &"Foo");
        // c2.add("lastname", &"Bar");
        //
        // let t4 = Template::new("index", TemplateMode::from_context(c2));
        //
        // // Using serialized values
        // let product = Product {
        //     name: "Foo".into(),
        //     value: 42,
        // };
        // // option 2
        // let t2 = Template::new("index", TemplateMode::from_serial(&product));
    }
}
<commit_msg>removed the abolished method<commit_after>//! Please contact me on github and file any issues if you find some, I'm also open to PRs or other suggestions.
//! CHANGELOG (0.2.0):
//! If you're upgrading from iron-tera 0.1.4, the API has changed slightly for serialized values.
//! Serde 0.9.0 to_value returns a `Result`, this means you need to handle the possiblity of a serialization failure.
//! Since we're still pre-1.0 and the API isn't completely stable, I've only incremented the minor version number.
//!
//! ## Examples
//! The following is a complete working example that I've tested with serde 0.9.0, tera 0.7.0 and iron-tera 0.2.0
//! ```rust
//! #[macro_use]
//! extern crate tera;
//!
//! extern crate iron;
//! extern crate router;
//! #[macro_use]
//! extern crate serde_json;
//! extern crate iron_tera;
//!
//! use iron::prelude::*;
//! use iron::status;
//! use router::Router;
//! use tera::Context;
//!
//! use iron_tera::{Template, TeraEngine, TemplateMode};
//!
//! fn main() {
//!     let mut router = Router::new();
//!     router.get("/context", context_handler, "context");
//!     router.get("/json", json_handler, "json");
//!
//!     let mut chain = Chain::new(router);
//!     let teng = TeraEngine::new("src/examples/templates/**/*");
//!     chain.link_after(teng);
//!
//!     Iron::new(chain).http("localhost:5000").unwrap();
//! }
//!
//!
//! fn context_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     let mut context = Context::new();
//!     context.add("username", &"Bob");
//!     context.add("my_var", &"Thing"); // comment out to see alternate thing
//!     context.add("numbers", &vec![1, 2, 3]);
//!     context.add("bio", &"<script>alert('pwnd');</script>");
//!
//!     resp.set_mut(Template::new("users/profile.html", TemplateMode::from_context(context)))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! fn json_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     let blob = json!({
//!         "username": "John Doe",
//!         "my_var": "Thing",
//!         "numbers": [
//!             "1",
//!             "+44 2345678",
//!             "3"
//!         ],
//!         "bio": "<script>alert('pwnd');</script>"
//!      });
//!
//!     resp.set_mut(Template::new("users/profile.html",
//!                                TemplateMode::from_serial(&blob).unwrap()))
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
//! Creating a template from a struct
//! ```rust
//! // The following uses serde's Serialize
//! #[derive(Serialize)]
//! struct Product {
//!     name: String,
//!     value: i32,
//! }
//! // Rendering from a struct that implements Serialize
//! fn produce_handler(_: &mut Request) -> IronResult<Response> {
//!     let mut resp = Response::new();
//!
//!     // Using serialized values
//!     let product = Product {
//!         name: "Foo".into(),
//!         value: 42,
//!     };
//!     resp.set_mut(Template::new("product.html", TemplateMode::from_serial(&product).unwrap())) // I made a choice here to return a result and let you handle the serialization error how you see fit.
//!         .set_mut(status::Ok);
//!     Ok(resp)
//! }
//! ```
#![allow(dead_code)]
#[macro_use] extern crate serde_json;
#[macro_use] extern crate tera;
extern crate iron;
extern crate serde;

use iron::prelude::*;
use iron::{AfterMiddleware, typemap, status};
use iron::modifier::Modifier;
use iron::headers::ContentType;

use tera::{Tera, Context};

use serde::ser::Serialize;
use serde_json::{Value, to_value};

/// There are 2 main ways to pass data to generate a template.
#[derive(Clone)]
pub enum TemplateMode {
    /// TeraContext constructor takes a `Context`
    TeraContext(Context),
    /// Serialized constructor takes a `Value` from `serde_json`
    Serialized(Value),
}

/// TemplateMode should only ever be created from these smart constructors,
/// not with the enums type constructors.
impl TemplateMode {
    pub fn from_context(context: Context) -> TemplateMode {
        TemplateMode::TeraContext(context)
    }
    pub fn from_serial<S: Serialize>(serialized: S) -> Result<TemplateMode, serde_json::Error> {
        Ok(TemplateMode::Serialized(to_value(serialized)?))
    }
}

/// Our template holds a name (path to template) and a mode (constructed with `from_context` or `from_serial`)
#[derive(Clone)]
pub struct Template {
    mode: TemplateMode,
    name: String,
}

impl Template {
    pub fn new(name: &str, mode: TemplateMode) -> Template {
        Template {
            name: name.into(),
            mode: mode,
        }
    }
}

/// TeraEngine holds the Tera struct so that it can be used by many handlers without explicitly passing
pub struct TeraEngine {
    pub tera: Tera,
}

/// `compile_templates!` is used to parse the contents of a dir for all templates.
impl TeraEngine {
    /// Take a `String` and convert to a slice or preferably a `&str`
    pub fn new(dir: &str) -> TeraEngine {
        TeraEngine { tera: compile_templates!(dir) }
    }
}

impl typemap::Key for TeraEngine {
    type Value = Template;
}

impl Modifier<Response> for Template {
    fn modify(self, resp: &mut Response) {
        resp.extensions.insert::<TeraEngine>(self);
    }
}

/// The middleware implementation for TeraEngine
impl AfterMiddleware for TeraEngine {
    /// This is where all the magic happens. We extract `TeraEngine` from Iron's `Response`,
    /// determine what `TemplateMode` we should render in, and pass the appropriate values to
    /// tera's render methods.
    fn after(&self, _: &mut Request, mut resp: Response) -> IronResult<Response> {
        let wrapper = resp.extensions.remove::<TeraEngine>().and_then(|t| {
            match t.mode {
                TemplateMode::TeraContext(context) => Some(self.tera.render(&t.name, &context)),
                TemplateMode::Serialized(value) => Some(self.tera.render(&t.name, &value)),
            }
        });
        match wrapper {
            Some(result) => {
                result.map_err(|e| IronError::new(e, status::InternalServerError))
                    .and_then(|page| {
                        if !resp.headers.has::<ContentType>() {
                            resp.headers.set(ContentType::html());
                        }
                        resp.set_mut(page);
                        Ok(resp)
                    })
            }
            None => Ok(resp),
        }
    }

    fn catch(&self, req: &mut Request, mut err: IronError) -> IronResult<Response> {
        err.response = self.after(req, err.response)?;
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    use super::{TeraEngine, Template, TemplateMode};
    use iron::prelude::*;
    use tera::{Tera, Context};
    //
    // #[derive(Serialize)]
    // struct Product {
    //     name: String,
    //     value: i32,
    // }

    fn test_resp() -> IronResult<Response> {
        let resp = Response::new();

        let mut context = Context::new();
        context.add("username", &"Foo");
        context.add("bio", &"Bar");
        context.add("numbers", &vec![1, 2, 3]);

        Ok(resp.set(Template::new("users/foo.html", TemplateMode::from_context(context))))
    }

    #[test]
    fn example() {
        let mut resp = test_resp().ok().expect("response expected");

        match resp.get::<TeraEngine>() {
            Ok(h) => {
                assert_eq!(h.name, "users/profile.html".to_string());
                assert_eq!(h.name
                               .as_object()
                               .unwrap()
                               .get(&"Foo".to_string())
                               .unwrap()
                               .as_string()
                               .unwrap(),
                           "Bar");
            }
            _ => panic!("template expected"),
        }
        // let teng = TeraEngine::new("src/test_template/**/*");
        // // Context option 1
        // let mut context = Context::new();
        // context.add("firstname", &"Foo");
        // context.add("lastname", &"Bar");
        //
        // let t = Template::new("index", TemplateMode::TeraContext(context));
        //
        // // Context option 2
        // let mut c2 = Context::new();
        // c2.add("firstname", &"Foo");
        // c2.add("lastname", &"Bar");
        //
        // let t4 = Template::new("index", TemplateMode::from_context(c2));
        //
        // // Using serialized values
        // let product = Product {
        //     name: "Foo".into(),
        //     value: 42,
        // };
        // // option 2
        // let t2 = Template::new("index", TemplateMode::from_serial(&product));
    }
}
<|endoftext|>


<commit_before>//! # Mime
//!
//! Mime is now Media Type, technically, but `Mime` is more immediately
//! understandable, so the main type here is `Mime`.
//!
//! ## What is Mime?
//!
//! Example mime string: `text/plain;charset=utf-8`
//!
//! ```rust
//! # use std::str::FromStr;
//! # use mime::Mime;
//! # use mime::TopLevel::Text;
//! # use mime::SubLevel::Plain;
//! # use mime::Attr::Charset;
//! # use mime::Value::Utf8;
//! let mime: Mime = FromStr::from_str("text/plain;charset=utf-8").unwrap();
//! assert_eq!(mime, Mime(Text, Plain, vec![(Charset, Utf8)]));
//! ```

#![doc(html_root_url = "http://hyperium.github.io/mime.rs")]
#![experimental]
#![feature(macro_rules, phase)]

#[phase(plugin, link)]
extern crate log;

#[cfg(test)]
extern crate test;

use std::ascii::AsciiExt;
use std::fmt;
use std::str::FromStr;
use std::iter::Enumerate;
use std::str::Chars;

macro_rules! inspect(
    ($s:expr, $t:expr) => ({
        let t = $t;
        debug!("inspect {}: {}", $s, t);
        t
    })
);

/// Mime, or Media Type. Encapsulates common registers types.
///
/// Consider that a traditional mime type contains a "top level type",
/// a "sub level type", and 0-N "parameters". And they're all strings.
/// Strings everywhere. Strings mean typos. Rust has type safety. We should
/// use types!
///
/// So, Mime bundles together this data into types so the compiler can catch
/// your typos.
///
/// This improves things so you use match without Strings:
///
/// ```rust
/// use std::str::FromStr;
/// use mime::{Mime, TopLevel, SubLevel};
///
/// let mime: mime::Mime = FromStr::from_str("application/json").unwrap();
///
/// match mime {
///     Mime(TopLevel::Application, SubLevel::Json, _) => println!("matched json!"),
///     _ => ()
/// }
/// ```
#[deriving(Clone, PartialEq)]
pub struct Mime(pub TopLevel, pub SubLevel, pub Vec<Param>);

macro_rules! enoom {
    (pub enum $en:ident; $ext:ident; $($ty:ident, $text:expr;)*) => (

        #[deriving(Clone)]
        pub enum $en {
            $($ty),*,
            $ext(String)
        }

        impl PartialEq for $en {
            fn eq(&self, other: &$en) -> bool {
                match (self, other) {
                    $( (&$en::$ty, &$en::$ty) => true ),*,
                    (&$en::$ext(ref a), &$en::$ext(ref b)) => a == b,
                    _ => self.to_string() == other.to_string()
                }
            }
        }

        impl fmt::Show for $en {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                match *self {
                    $($en::$ty => $text),*,
                    $en::$ext(ref s) => return s.fmt(fmt)
                }.fmt(fmt)
            }
        }

        impl FromStr for $en {
            fn from_str(s: &str) -> Option<$en> {
                Some(match s {
                    $(_s if _s == $text => $en::$ty),*,
                    s => $en::$ext(inspect!(stringify!($ext), s).to_string())
                })
            }
        }
    )
}

enoom! {
    pub enum TopLevel;
    Ext;
    Star, "*";
    Text, "text";
    Image, "image";
    Audio, "audio";
    Video, "video";
    Application, "application";
    Multipart, "multipart";
    Message, "message";
    Model, "model";
}

enoom! {
    pub enum SubLevel;
    Ext;
    Star, "*";

    // common text/*
    Plain, "plain";
    Html, "html";
    Xml, "xml";
    Javascript, "javascript";
    Css, "css";

    // common application/*
    Json, "json";
    WwwFormUrlEncoded, "x-www-form-urlencoded";

    // multipart/*
    FormData, "form-data";

    // common image/*
    Png, "png";
    Gif, "gif";
    Bmp, "bmp";
    Jpeg, "jpeg";
}

enoom! {
    pub enum Attr;
    Ext;
    Charset, "charset";
    Q, "q";
}

enoom! {
    pub enum Value;
    Ext;
    Utf8, "utf-8";
}

pub type Param = (Attr, Value);

impl fmt::Show for Mime {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let Mime(ref top, ref sub, ref params) = *self;
        try!(write!(fmt, "{}/{}", top, sub));
        fmt_params(params.as_slice(), fmt)
    }
}

impl FromStr for Mime {
    fn from_str(raw: &str) -> Option<Mime> {
        let ascii = raw.to_ascii_lower(); // lifetimes :(
        let raw = ascii.as_slice();
        let len = raw.len();
        let mut iter = raw.chars().enumerate();
        let mut params = vec![];
        // toplevel
        let mut start;
        let mut top;
        loop {
            match inspect!("top iter", iter.next()) {
                Some((0, c)) if is_restricted_name_first_char(c) => (),
                Some((i, c)) if i > 0 && is_restricted_name_char(c) => (),
                Some((i, '/')) if i > 0 => match FromStr::from_str(raw.slice_to(i)) {
                    Some(t) => {
                        top = t;
                        start = i + 1;
                        break;
                    }
                    None => return None
                },
                _ => return None // EOF and no toplevel is no Mime
            };

        }

        // sublevel
        let mut sub;
        loop {
            match inspect!("sub iter", iter.next()) {
                Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
                Some((i, c)) if i > start && is_restricted_name_char(c) => (),
                Some((i, ';')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                    Some(s) => {
                        sub = s;
                        start = i + 1;
                        break;
                    }
                    None => return None
                },
                None => match FromStr::from_str(raw.slice_from(start)) {
                    Some(s) => return Some(Mime(top, s, params)),
                    None => return None
                },
                _ => return None
            };
        }

        // params
        debug!("starting params, len={}", len);
        loop {
            match inspect!("param", param_from_str(raw, &mut iter, start)) {
                Some((p, end)) => {
                    params.push(p);
                    start = end;
                    if start >= len {
                        break;
                    }
                }
                None => break
            }
        }

        Some(Mime(top, sub, params))
    }
}

fn param_from_str(raw: &str, iter: &mut Enumerate<Chars>, mut start: uint) -> Option<(Param, uint)> {
    let mut attr;
    debug!("param_from_str, start={}", start);
    loop {
        match inspect!("attr iter", iter.next()) {
            Some((i, ' ')) if i == start => start = i + 1,
            Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
            Some((i, c)) if i > start && is_restricted_name_char(c) => (),
            Some((i, '=')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                Some(a) => {
                    attr = inspect!("attr", a);
                    start = i + 1;
                    break;
                },
                None => return None
            },
            _ => return None
        }
    }
    let mut value;
    // values must be restrict-name-char or "anything goes"
    let mut is_quoted = false;
    loop {
        match inspect!("value iter", iter.next()) {
            Some((i, '"')) if i == start => {
                debug!("quoted");
                is_quoted = true;
                start = i + 1;
            },
            Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
            Some((i, '"')) if i > start && is_quoted => match FromStr::from_str(raw.slice(start, i)) {
                Some(v) => {
                    value = v;
                    start = i + 1;
                    break;
                },
                None => return None
            },
            Some((i, c)) if i > start && is_quoted || is_restricted_name_char(c) => (),
            Some((i, ';')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                Some(v) => {
                    value = v;
                    start = i + 1;
                    break;
                },
                None => return None
            },
            None => match FromStr::from_str(raw.slice_from(start)) {
                Some(v) => {
                    value = v;
                    start = raw.len();
                    break;
                },
                None => return None
            },

            _ => return None
        }
    }

    Some(((attr, value), start))
}

// From [RFC6838](http://tools.ietf.org/html/rfc6838#section-4.2):
//
// > All registered media types MUST be assigned top-level type and
// > subtype names.  The combination of these names serves to uniquely
// > identify the media type, and the subtype name facet (or the absence
// > of one) identifies the registration tree.  Both top-level type and
// > subtype names are case-insensitive.
// >
// > Type and subtype names MUST conform to the following ABNF:
// >
// >     type-name = restricted-name
// >     subtype-name = restricted-name
// >
// >     restricted-name = restricted-name-first *126restricted-name-chars
// >     restricted-name-first  = ALPHA / DIGIT
// >     restricted-name-chars  = ALPHA / DIGIT / "!" / "#" /
// >                              "$" / "&" / "-" / "^" / "_"
// >     restricted-name-chars =/ "." ; Characters before first dot always
// >                                  ; specify a facet name
// >     restricted-name-chars =/ "+" ; Characters after last plus always
// >                                  ; specify a structured syntax suffix
//
fn is_restricted_name_first_char(c: char) -> bool {
    match c {
        'a'...'z' |
        '0'...'9' => true,
        _ => false
    }
}

fn is_restricted_name_char(c: char) -> bool {
    if is_restricted_name_first_char(c) {
        true
    } else {
        match c {
            '!' |
            '#' |
            '$' |
            '&' |
            '-' |
            '^' |
            '.' |
            '+' |
            '_' => true,
            _ => false
        }
    }
}

#[inline]
fn fmt_params<T: AsSlice<Param>>(params: T, fmt: &mut fmt::Formatter) -> fmt::Result {
    for param in params.as_slice().iter() {
        try!(fmt_param(param, fmt));
    }
    Ok(())
}

#[inline]
fn fmt_param(param: &Param, fmt: &mut fmt::Formatter) -> fmt::Result {
    let (ref attr, ref value) = *param;
    write!(fmt, "; {}={}", attr, value)
}

#[cfg(test)]
mod tests {
    use std::str::{FromStr, from_str};
    use test::Bencher;
    use super::{TopLevel, SubLevel, Attr, Value, Mime};

    #[test]
    fn test_mime_show() {
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![]);
        assert_eq!(mime.to_string(), "text/plain".to_string());
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]);
        assert_eq!(mime.to_string(), "text/plain; charset=utf-8".to_string());
    }

    #[test]
    fn test_mime_from_str() {
        assert_eq!(FromStr::from_str("text/plain"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        assert_eq!(FromStr::from_str("TEXT/PLAIN"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        assert_eq!(FromStr::from_str("text/plain; charset=utf-8"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])));
        assert_eq!(FromStr::from_str("text/plain;charset=\"utf-8\""), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])));
        assert_eq!(FromStr::from_str("text/plain; charset=utf-8; foo=bar"),
            Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8),
                                        (Attr::Ext("foo".to_string()), Value::Ext("bar".to_string())) ])));
    }

    #[bench]
    fn bench_show(b: &mut Bencher) {
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8), (Attr::Ext("foo".to_string()), Value::Ext("bar".to_string()))]);
        b.bytes = mime.to_string().as_bytes().len() as u64;
        b.iter(|| mime.to_string())
    }

    #[bench]
    fn bench_from_str(b: &mut Bencher) {
        let s = "text/plain; charset=utf-8; foo=bar";
        b.bytes = s.as_bytes().len() as u64;
        b.iter(|| from_str::<Mime>(s))
    }
}
<commit_msg>deny warnings<commit_after>//! # Mime
//!
//! Mime is now Media Type, technically, but `Mime` is more immediately
//! understandable, so the main type here is `Mime`.
//!
//! ## What is Mime?
//!
//! Example mime string: `text/plain;charset=utf-8`
//!
//! ```rust
//! # use std::str::FromStr;
//! # use mime::Mime;
//! # use mime::TopLevel::Text;
//! # use mime::SubLevel::Plain;
//! # use mime::Attr::Charset;
//! # use mime::Value::Utf8;
//! let mime: Mime = FromStr::from_str("text/plain;charset=utf-8").unwrap();
//! assert_eq!(mime, Mime(Text, Plain, vec![(Charset, Utf8)]));
//! ```

#![doc(html_root_url = "http://hyperium.github.io/mime.rs")]
#![experimental]
#![feature(macro_rules, phase)]
#![deny(warnings)]

#[phase(plugin, link)]
extern crate log;

#[cfg(test)]
extern crate test;

use std::ascii::AsciiExt;
use std::fmt;
use std::str::FromStr;
use std::iter::Enumerate;
use std::str::Chars;

macro_rules! inspect(
    ($s:expr, $t:expr) => ({
        let t = $t;
        debug!("inspect {}: {}", $s, t);
        t
    })
);

/// Mime, or Media Type. Encapsulates common registers types.
///
/// Consider that a traditional mime type contains a "top level type",
/// a "sub level type", and 0-N "parameters". And they're all strings.
/// Strings everywhere. Strings mean typos. Rust has type safety. We should
/// use types!
///
/// So, Mime bundles together this data into types so the compiler can catch
/// your typos.
///
/// This improves things so you use match without Strings:
///
/// ```rust
/// use std::str::FromStr;
/// use mime::{Mime, TopLevel, SubLevel};
///
/// let mime: mime::Mime = FromStr::from_str("application/json").unwrap();
///
/// match mime {
///     Mime(TopLevel::Application, SubLevel::Json, _) => println!("matched json!"),
///     _ => ()
/// }
/// ```
#[deriving(Clone, PartialEq)]
pub struct Mime(pub TopLevel, pub SubLevel, pub Vec<Param>);

macro_rules! enoom {
    (pub enum $en:ident; $ext:ident; $($ty:ident, $text:expr;)*) => (

        #[deriving(Clone)]
        pub enum $en {
            $($ty),*,
            $ext(String)
        }

        impl PartialEq for $en {
            fn eq(&self, other: &$en) -> bool {
                match (self, other) {
                    $( (&$en::$ty, &$en::$ty) => true ),*,
                    (&$en::$ext(ref a), &$en::$ext(ref b)) => a == b,
                    _ => self.to_string() == other.to_string()
                }
            }
        }

        impl fmt::Show for $en {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                match *self {
                    $($en::$ty => $text),*,
                    $en::$ext(ref s) => return s.fmt(fmt)
                }.fmt(fmt)
            }
        }

        impl FromStr for $en {
            fn from_str(s: &str) -> Option<$en> {
                Some(match s {
                    $(_s if _s == $text => $en::$ty),*,
                    s => $en::$ext(inspect!(stringify!($ext), s).to_string())
                })
            }
        }
    )
}

enoom! {
    pub enum TopLevel;
    Ext;
    Star, "*";
    Text, "text";
    Image, "image";
    Audio, "audio";
    Video, "video";
    Application, "application";
    Multipart, "multipart";
    Message, "message";
    Model, "model";
}

enoom! {
    pub enum SubLevel;
    Ext;
    Star, "*";

    // common text/*
    Plain, "plain";
    Html, "html";
    Xml, "xml";
    Javascript, "javascript";
    Css, "css";

    // common application/*
    Json, "json";
    WwwFormUrlEncoded, "x-www-form-urlencoded";

    // multipart/*
    FormData, "form-data";

    // common image/*
    Png, "png";
    Gif, "gif";
    Bmp, "bmp";
    Jpeg, "jpeg";
}

enoom! {
    pub enum Attr;
    Ext;
    Charset, "charset";
    Q, "q";
}

enoom! {
    pub enum Value;
    Ext;
    Utf8, "utf-8";
}

pub type Param = (Attr, Value);

impl fmt::Show for Mime {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let Mime(ref top, ref sub, ref params) = *self;
        try!(write!(fmt, "{}/{}", top, sub));
        fmt_params(params.as_slice(), fmt)
    }
}

impl FromStr for Mime {
    fn from_str(raw: &str) -> Option<Mime> {
        let ascii = raw.to_ascii_lower(); // lifetimes :(
        let raw = ascii.as_slice();
        let len = raw.len();
        let mut iter = raw.chars().enumerate();
        let mut params = vec![];
        // toplevel
        let mut start;
        let mut top;
        loop {
            match inspect!("top iter", iter.next()) {
                Some((0, c)) if is_restricted_name_first_char(c) => (),
                Some((i, c)) if i > 0 && is_restricted_name_char(c) => (),
                Some((i, '/')) if i > 0 => match FromStr::from_str(raw.slice_to(i)) {
                    Some(t) => {
                        top = t;
                        start = i + 1;
                        break;
                    }
                    None => return None
                },
                _ => return None // EOF and no toplevel is no Mime
            };

        }

        // sublevel
        let mut sub;
        loop {
            match inspect!("sub iter", iter.next()) {
                Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
                Some((i, c)) if i > start && is_restricted_name_char(c) => (),
                Some((i, ';')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                    Some(s) => {
                        sub = s;
                        start = i + 1;
                        break;
                    }
                    None => return None
                },
                None => match FromStr::from_str(raw.slice_from(start)) {
                    Some(s) => return Some(Mime(top, s, params)),
                    None => return None
                },
                _ => return None
            };
        }

        // params
        debug!("starting params, len={}", len);
        loop {
            match inspect!("param", param_from_str(raw, &mut iter, start)) {
                Some((p, end)) => {
                    params.push(p);
                    start = end;
                    if start >= len {
                        break;
                    }
                }
                None => break
            }
        }

        Some(Mime(top, sub, params))
    }
}

fn param_from_str(raw: &str, iter: &mut Enumerate<Chars>, mut start: uint) -> Option<(Param, uint)> {
    let mut attr;
    debug!("param_from_str, start={}", start);
    loop {
        match inspect!("attr iter", iter.next()) {
            Some((i, ' ')) if i == start => start = i + 1,
            Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
            Some((i, c)) if i > start && is_restricted_name_char(c) => (),
            Some((i, '=')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                Some(a) => {
                    attr = inspect!("attr", a);
                    start = i + 1;
                    break;
                },
                None => return None
            },
            _ => return None
        }
    }
    let mut value;
    // values must be restrict-name-char or "anything goes"
    let mut is_quoted = false;
    loop {
        match inspect!("value iter", iter.next()) {
            Some((i, '"')) if i == start => {
                debug!("quoted");
                is_quoted = true;
                start = i + 1;
            },
            Some((i, c)) if i == start && is_restricted_name_first_char(c) => (),
            Some((i, '"')) if i > start && is_quoted => match FromStr::from_str(raw.slice(start, i)) {
                Some(v) => {
                    value = v;
                    start = i + 1;
                    break;
                },
                None => return None
            },
            Some((i, c)) if i > start && is_quoted || is_restricted_name_char(c) => (),
            Some((i, ';')) if i > start => match FromStr::from_str(raw.slice(start, i)) {
                Some(v) => {
                    value = v;
                    start = i + 1;
                    break;
                },
                None => return None
            },
            None => match FromStr::from_str(raw.slice_from(start)) {
                Some(v) => {
                    value = v;
                    start = raw.len();
                    break;
                },
                None => return None
            },

            _ => return None
        }
    }

    Some(((attr, value), start))
}

// From [RFC6838](http://tools.ietf.org/html/rfc6838#section-4.2):
//
// > All registered media types MUST be assigned top-level type and
// > subtype names.  The combination of these names serves to uniquely
// > identify the media type, and the subtype name facet (or the absence
// > of one) identifies the registration tree.  Both top-level type and
// > subtype names are case-insensitive.
// >
// > Type and subtype names MUST conform to the following ABNF:
// >
// >     type-name = restricted-name
// >     subtype-name = restricted-name
// >
// >     restricted-name = restricted-name-first *126restricted-name-chars
// >     restricted-name-first  = ALPHA / DIGIT
// >     restricted-name-chars  = ALPHA / DIGIT / "!" / "#" /
// >                              "$" / "&" / "-" / "^" / "_"
// >     restricted-name-chars =/ "." ; Characters before first dot always
// >                                  ; specify a facet name
// >     restricted-name-chars =/ "+" ; Characters after last plus always
// >                                  ; specify a structured syntax suffix
//
fn is_restricted_name_first_char(c: char) -> bool {
    match c {
        'a'...'z' |
        '0'...'9' => true,
        _ => false
    }
}

fn is_restricted_name_char(c: char) -> bool {
    if is_restricted_name_first_char(c) {
        true
    } else {
        match c {
            '!' |
            '#' |
            '$' |
            '&' |
            '-' |
            '^' |
            '.' |
            '+' |
            '_' => true,
            _ => false
        }
    }
}

#[inline]
fn fmt_params<T: AsSlice<Param>>(params: T, fmt: &mut fmt::Formatter) -> fmt::Result {
    for param in params.as_slice().iter() {
        try!(fmt_param(param, fmt));
    }
    Ok(())
}

#[inline]
fn fmt_param(param: &Param, fmt: &mut fmt::Formatter) -> fmt::Result {
    let (ref attr, ref value) = *param;
    write!(fmt, "; {}={}", attr, value)
}

#[cfg(test)]
mod tests {
    use std::str::{FromStr, from_str};
    use test::Bencher;
    use super::{TopLevel, SubLevel, Attr, Value, Mime};

    #[test]
    fn test_mime_show() {
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![]);
        assert_eq!(mime.to_string(), "text/plain".to_string());
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]);
        assert_eq!(mime.to_string(), "text/plain; charset=utf-8".to_string());
    }

    #[test]
    fn test_mime_from_str() {
        assert_eq!(FromStr::from_str("text/plain"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        assert_eq!(FromStr::from_str("TEXT/PLAIN"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![])));
        assert_eq!(FromStr::from_str("text/plain; charset=utf-8"), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])));
        assert_eq!(FromStr::from_str("text/plain;charset=\"utf-8\""), Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])));
        assert_eq!(FromStr::from_str("text/plain; charset=utf-8; foo=bar"),
            Some(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8),
                                        (Attr::Ext("foo".to_string()), Value::Ext("bar".to_string())) ])));
    }

    #[bench]
    fn bench_show(b: &mut Bencher) {
        let mime = Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8), (Attr::Ext("foo".to_string()), Value::Ext("bar".to_string()))]);
        b.bytes = mime.to_string().as_bytes().len() as u64;
        b.iter(|| mime.to_string())
    }

    #[bench]
    fn bench_from_str(b: &mut Bencher) {
        let s = "text/plain; charset=utf-8; foo=bar";
        b.bytes = s.as_bytes().len() as u64;
        b.iter(|| s.parse::<Mime>())
    }
}
<|endoftext|>


<commit_before>//! Helpers for writing Alfred XML output

use std::io;
use std::io::BufferedWriter;
use std::str;

/// Representation of an <item>
#[deriving(PartialEq,Eq,Clone)]
pub struct Item {
    /// Identifier for the results. If given, must be unique among items, and is used for
    /// prioritizing feedback results based on usage. If blank, Alfred uses a UUID and does
    /// not learn from the results.
    uid: Option<str::MaybeOwned<'static>>,
    /// The value that is passed to the next portion of the workflow when this item
    /// is selected.
    arg: Option<str::MaybeOwned<'static>>,
    /// What type of result this is, if any.
    type_: Option<ItemType>,
    /// Whether or not the result item is 'valid'. If `false`, `autocomplete` may be used.
    valid: bool,
    /// Autocomplete data for valid=false items. When this item is selected, the autocomplete
    /// value is inserted into the Alfred window.
    autocomplete: Option<str::MaybeOwned<'static>>,

    /// Title for the item
    title: str::MaybeOwned<'static>,
    /// Subtitle for the item
    subtitle: Option<str::MaybeOwned<'static>>,
    /// Icon for the item
    icon: Option<Icon>
}

impl Item {
    /// Returns a new Item with the given title
    pub fn new<S: str::IntoMaybeOwned<'static>>(title: S) -> Item {
        Item {
            uid: None,
            arg: None,
            type_: None,
            valid: true,
            autocomplete: None,
            title: title.into_maybe_owned(),
            subtitle: None,
            icon: None
        }
    }
}

/// Item icons
#[deriving(PartialEq,Eq,Clone)]
pub enum Icon {
    /// Path to an image file on disk relative to the workflow directory
    PathIcon(str::MaybeOwned<'static>),
    /// Path to a file whose icon will be used
    FileIcon(str::MaybeOwned<'static>),
    /// UTI for a file type to use (e.g. public.folder)
    FileType(str::MaybeOwned<'static>)
}

/// Item types
#[deriving(PartialEq,Eq,Clone)]
pub enum ItemType {
    FileItemType
}

impl Item {
    /// Writes the XML fragment representing the Item to the Writer
    pub fn write_xml(&self, w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
        fn write_indent(w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
            for _ in range(0, indent) {
                try!(w.write_str("    "));
            }
            Ok(())
        }

        let mut w = BufferedWriter::with_capacity(512, w);

        try!(write_indent(&mut w, indent));
        try!(w.write_str("<item"));
        match self.uid {
            None => (),
            Some(ref uid) => {
                try!(write!(&mut w, r#" uid="{}""#, encode_entities(uid.as_slice())));
            }
        }
        match self.arg {
            None => (),
            Some(ref arg) => {
                try!(write!(&mut w, r#" arg="{}""#, encode_entities(arg.as_slice())));
            }
        }
        match self.type_ {
            None => (),
            Some(FileItemType) => {
                try!(w.write_str(r#" type="file""#));
            }
        }
        try!(write!(&mut w, r#" valid="{}""#, if self.valid { "yes" } else { "no" }));
        match self.autocomplete {
            None => (),
            Some(ref auto) => {
                try!(write!(&mut w, r#" autocomplete="{}""#, encode_entities(auto.as_slice())));
            }
        }
        try!(w.write_str(">\n"));

        try!(write_indent(&mut w, indent+1));
        try!(write!(&mut w, "<title>{}</title>\n", encode_entities(self.title.as_slice())));

        match self.subtitle {
            None => (),
            Some(ref s) => {
                try!(write_indent(&mut w, indent+1));
                try!(write!(&mut w, "<subtitle>{}</subtitle>\n", encode_entities(s.as_slice())));
            }
        }

        match self.icon {
            None => (),
            Some(ref icon) => {
                try!(write_indent(&mut w, indent+1));
                match *icon {
                    PathIcon(ref s) => {
                        try!(write!(&mut w, "<icon>{}</icon>\n", encode_entities(s.as_slice())));
                    }
                    FileIcon(ref s) => {
                        try!(write!(&mut w, "<icon type=\"fileicon\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                    FileType(ref s) => {
                        try!(write!(&mut w, "<icon type=\"filetype\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                }
            }
        }

        try!(write_indent(&mut w, indent));
        try!(w.write_str("</item>\n"));

        try!(w.flush());
        Ok(())
    }
}

fn encode_entities<'a>(s: &'a str) -> str::MaybeOwned<'a> {
    fn encode_entity(c: char) -> Option<&'static str> {
        Some(match c {
            '<' => "<",
            '>' => ">",
            '"' => """,
            '&' => "&",
            '\0'...'\x08' |
            '\x0B'...'\x0C' |
            '\x0E'...'\x1F' |
            '\uFFFE' | '\uFFFF' => {
                // these are all invalid characters in XML
                "\uFFFD"
            }
            _ => return None
        })
    }

    if s.chars().any(|c| encode_entity(c).is_some()) {
        let mut res = String::with_capacity(s.len());
        for c in s.chars() {
            match encode_entity(c) {
                Some(ent) => res.push_str(ent),
                None => res.push(c)
            }
        }
        str::Owned(res)
    } else {
        str::Slice(s)
    }
}
<commit_msg>#![warn(missing_doc)]<commit_after>//! Helpers for writing Alfred XML output

#![warn(missing_doc)]

use std::io;
use std::io::BufferedWriter;
use std::str;

/// Representation of an <item>
#[deriving(PartialEq,Eq,Clone)]
pub struct Item {
    /// Identifier for the results. If given, must be unique among items, and is used for
    /// prioritizing feedback results based on usage. If blank, Alfred uses a UUID and does
    /// not learn from the results.
    uid: Option<str::MaybeOwned<'static>>,
    /// The value that is passed to the next portion of the workflow when this item
    /// is selected.
    arg: Option<str::MaybeOwned<'static>>,
    /// What type of result this is, if any.
    type_: Option<ItemType>,
    /// Whether or not the result item is 'valid'. If `false`, `autocomplete` may be used.
    valid: bool,
    /// Autocomplete data for valid=false items. When this item is selected, the autocomplete
    /// value is inserted into the Alfred window.
    autocomplete: Option<str::MaybeOwned<'static>>,

    /// Title for the item
    title: str::MaybeOwned<'static>,
    /// Subtitle for the item
    subtitle: Option<str::MaybeOwned<'static>>,
    /// Icon for the item
    icon: Option<Icon>
}

impl Item {
    /// Returns a new Item with the given title
    pub fn new<S: str::IntoMaybeOwned<'static>>(title: S) -> Item {
        Item {
            uid: None,
            arg: None,
            type_: None,
            valid: true,
            autocomplete: None,
            title: title.into_maybe_owned(),
            subtitle: None,
            icon: None
        }
    }
}

/// Item icons
#[deriving(PartialEq,Eq,Clone)]
pub enum Icon {
    /// Path to an image file on disk relative to the workflow directory
    PathIcon(str::MaybeOwned<'static>),
    /// Path to a file whose icon will be used
    FileIcon(str::MaybeOwned<'static>),
    /// UTI for a file type to use (e.g. public.folder)
    FileType(str::MaybeOwned<'static>)
}

/// Item types
#[deriving(PartialEq,Eq,Clone)]
pub enum ItemType {
    /// Type representing a file
    FileItemType
}

impl Item {
    /// Writes the XML fragment representing the Item to the Writer
    pub fn write_xml(&self, w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
        fn write_indent(w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
            for _ in range(0, indent) {
                try!(w.write_str("    "));
            }
            Ok(())
        }

        let mut w = BufferedWriter::with_capacity(512, w);

        try!(write_indent(&mut w, indent));
        try!(w.write_str("<item"));
        match self.uid {
            None => (),
            Some(ref uid) => {
                try!(write!(&mut w, r#" uid="{}""#, encode_entities(uid.as_slice())));
            }
        }
        match self.arg {
            None => (),
            Some(ref arg) => {
                try!(write!(&mut w, r#" arg="{}""#, encode_entities(arg.as_slice())));
            }
        }
        match self.type_ {
            None => (),
            Some(FileItemType) => {
                try!(w.write_str(r#" type="file""#));
            }
        }
        try!(write!(&mut w, r#" valid="{}""#, if self.valid { "yes" } else { "no" }));
        match self.autocomplete {
            None => (),
            Some(ref auto) => {
                try!(write!(&mut w, r#" autocomplete="{}""#, encode_entities(auto.as_slice())));
            }
        }
        try!(w.write_str(">\n"));

        try!(write_indent(&mut w, indent+1));
        try!(write!(&mut w, "<title>{}</title>\n", encode_entities(self.title.as_slice())));

        match self.subtitle {
            None => (),
            Some(ref s) => {
                try!(write_indent(&mut w, indent+1));
                try!(write!(&mut w, "<subtitle>{}</subtitle>\n", encode_entities(s.as_slice())));
            }
        }

        match self.icon {
            None => (),
            Some(ref icon) => {
                try!(write_indent(&mut w, indent+1));
                match *icon {
                    PathIcon(ref s) => {
                        try!(write!(&mut w, "<icon>{}</icon>\n", encode_entities(s.as_slice())));
                    }
                    FileIcon(ref s) => {
                        try!(write!(&mut w, "<icon type=\"fileicon\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                    FileType(ref s) => {
                        try!(write!(&mut w, "<icon type=\"filetype\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                }
            }
        }

        try!(write_indent(&mut w, indent));
        try!(w.write_str("</item>\n"));

        try!(w.flush());
        Ok(())
    }
}

fn encode_entities<'a>(s: &'a str) -> str::MaybeOwned<'a> {
    fn encode_entity(c: char) -> Option<&'static str> {
        Some(match c {
            '<' => "<",
            '>' => ">",
            '"' => """,
            '&' => "&",
            '\0'...'\x08' |
            '\x0B'...'\x0C' |
            '\x0E'...'\x1F' |
            '\uFFFE' | '\uFFFF' => {
                // these are all invalid characters in XML
                "\uFFFD"
            }
            _ => return None
        })
    }

    if s.chars().any(|c| encode_entity(c).is_some()) {
        let mut res = String::with_capacity(s.len());
        for c in s.chars() {
            match encode_entity(c) {
                Some(ent) => res.push_str(ent),
                None => res.push(c)
            }
        }
        str::Owned(res)
    } else {
        str::Slice(s)
    }
}
<|endoftext|>