<commit_before>//! stal-rs
//! ====
//!
//! Set algebra solver for Redis in Rust, based on
//! [Stal](https://github.com/soveran/stal).
//!
//! Description
//! -----------
//!
//! `stal-rs` provide set operations and resolves them in [Redis][redis].
//!
//! Usage
//! -----
//!
//! `stal-rs` has no dependencies. It produces a vector of Redis operations that
//! have to be run by the user.
//!
//! ```rust
//! extern crate stal;
//!
//! let foobar = stal::Set::Inter(vec![stal::Set::Key(b"foo".to_vec()), stal::Set::Key(b"bar".to_vec())]);
//! let foobar_nobaz = stal::Set::Diff(vec![foobar, stal::Set::Key(b"baz".to_vec())]);
//! let foobar_nobaz_andqux = stal::Set::Union(vec![stal::Set::Key(b"qux".to_vec()), foobar_nobaz]);
//!
//! assert_eq!(
//!    stal::Stal::new("SMEMBERS".to_string(), foobar_nobaz_andqux).solve(),
//!    (
//!     vec![
//!         vec![b"MULTI".to_vec()],
//!         vec![b"SINTERSTORE".to_vec(), b"stal:2".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!         vec![b"SDIFFSTORE".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec(), b"baz".to_vec()],
//!         vec![b"SUNIONSTORE".to_vec(), b"stal:0".to_vec(), b"qux".to_vec(), b"stal:1".to_vec()],
//!         vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//!         vec![b"DEL".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec()],
//!         vec![b"EXEC".to_vec()],
//!     ],
//!     4
//!    ));
//! ```
//!
//! `stal-rs` translates the internal calls to  `SUNION`, `SDIFF` and
//! `SINTER` into `SDIFFSTORE`, `SINTERSTORE` and `SUNIONSTORE` to
//! perform the underlying operations, and it takes care of generating
//! and deleting any temporary keys.
//!
//! The outmost command can be any set operation, for example:
//!
//! ```rust
//! extern crate stal;
//! let myset = stal::Set::Key(b"my set".to_vec());
//! stal::Stal::new("SCARD".to_string(), myset).solve();
//! ```
//!
//! If you want to preview the commands `Stal` will send to generate
//! the results, you can use `Stal.explain`:
//!
//! ```rust
//! extern crate stal;
//!
//! assert_eq!(
//! stal::Stal::new("SMEMBERS".to_string(),
//!         stal::Set::Inter(vec![
//!             stal::Set::Union(vec![
//!                 stal::Set::Key(b"foo".to_vec()),
//!                 stal::Set::Key(b"bar".to_vec()),
//!                 ]),
//!             stal::Set::Key(b"baz".to_vec()),
//!             ])
//!         ).explain(),
//! vec![
//!     vec![b"SUNIONSTORE".to_vec(), b"stal:1".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!     vec![b"SINTERSTORE".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"baz".to_vec()],
//!     vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//! ]
//! )
//! ```
//!
//! All commands are wrapped in a `MULTI/EXEC` transaction.
//!
//! [redis]: http://redis.io

#![crate_name = "stal"]
#![crate_type = "lib"]

/// A set of values. It can be generated from a Redis key or from a set
/// operation based on other sets.
#[derive(Clone)]
pub enum Set {
    /// A key
    Key(Vec<u8>),
    /// All the elements in any of the provided sets
    Union(Vec<Set>),
    /// All the elements in all the sets
    Inter(Vec<Set>),
    /// All the elements in the first set that are not in the other sets
    Diff(Vec<Set>),
}
use Set::*;

impl Set {
    /// Gets the commands to get a list of ids for this set
    pub fn ids(&self) -> Stal {
        let (op, sets) = match *self {
            Key(_) => return Stal::from_template(vec![b"SMEMBERS".to_vec(), vec![]], vec![(self.clone(), 1)]),
            Union(ref sets) => ("SUNION", sets),
            Inter(ref sets) => ("SINTER", sets),
            Diff(ref sets) => ("SDIFF", sets),
        };
        let mut command = vec![op.as_bytes().to_vec()];
        command.extend(sets.iter().map(|_| vec![]).collect::<Vec<_>>());
        let mut setv = vec![];
        for i in 0..sets.len() {
            setv.push((sets[i].clone(), i + 1));
        }
        Stal::from_template(command, setv)
    }

    /// Maps the operation to its Redis command name.
    fn command(&self) -> &'static str {
        match *self {
            Key(_) => unreachable!(),
            Union(_) => "SUNIONSTORE",
            Inter(_) => "SINTERSTORE",
            Diff(_) => "SDIFFSTORE",
        }
    }

    /// Appends the operation to `ops` and any temporary id created to `ids`.
    /// Returns the key representing the set.
    pub fn convert(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) -> Vec<u8> {
        let sets = match *self {
            Key(ref k) => return k.clone(),
            Union(ref sets) => sets,
            Inter(ref sets) => sets,
            Diff(ref sets) => sets,
        };

        let mut op = Vec::with_capacity(2 + sets.len());
        let id = format!("stal:{}", ids.len());
        let r = id.as_bytes().to_vec();
        ids.push(id);
        op.push(self.command().as_bytes().to_vec());
        op.push(r.clone());
        op.extend(sets.into_iter().map(|s| s.convert(ids, ops)));
        ops.push(op);
        r
    }
}

/// An operation to be executed on a set
pub struct Stal {
    /// A Redis command
    command: Vec<Vec<u8>>,
    /// Set in which execute the operation
    sets: Vec<(Set, usize)>,
}

impl Stal {
    pub fn new(operation: String, set: Set) -> Self {
        Stal {
            command: vec![operation.as_bytes().to_vec(), vec![]],
            sets: vec![(set, 1)],
        }
    }

    pub fn from_template(command: Vec<Vec<u8>>, sets: Vec<(Set, usize)>) -> Self {
        Stal {
            command: command,
            sets: sets,
        }
    }

    fn add_ops(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) {
        let mut command = self.command.clone();
        for args in self.sets.iter() {
            command.push(args.0.convert(ids, ops));
            command.swap_remove(args.1);
        }
        ops.push(command);
    }

    /// Returns a list of operations to run. For debug only.
    pub fn explain(&self) -> Vec<Vec<Vec<u8>>> {
        let mut ids = vec![];
        let mut ops = vec![];
        self.add_ops(&mut ids, &mut ops);
        ops
    }

    /// Returns a lit of operations, wrapped in a multi/exec.
    /// The last operation is always exec, and the returned `usize` indicates
    /// the return value of the `operation`.
    pub fn solve(&self) -> (Vec<Vec<Vec<u8>>>, usize) {
        let mut ids = vec![];
        let mut ops = vec![vec![b"MULTI".to_vec()]];
        self.add_ops(&mut ids, &mut ops);
        let pos = ops.len() - 1;
        if ids.len() > 0 {
            let mut del = vec![b"DEL".to_vec()];
            del.extend(ids.into_iter().map(|x| x.as_bytes().to_vec()));
            ops.push(del);
        }
        ops.push(vec![b"EXEC".to_vec()]);
        (ops, pos)
    }
}
<commit_msg>Add from_template header doc<commit_after>//! stal-rs
//! ====
//!
//! Set algebra solver for Redis in Rust, based on
//! [Stal](https://github.com/soveran/stal).
//!
//! Description
//! -----------
//!
//! `stal-rs` provide set operations and resolves them in [Redis][redis].
//!
//! Usage
//! -----
//!
//! `stal-rs` has no dependencies. It produces a vector of Redis operations that
//! have to be run by the user.
//!
//! ```rust
//! extern crate stal;
//!
//! let foobar = stal::Set::Inter(vec![stal::Set::Key(b"foo".to_vec()), stal::Set::Key(b"bar".to_vec())]);
//! let foobar_nobaz = stal::Set::Diff(vec![foobar, stal::Set::Key(b"baz".to_vec())]);
//! let foobar_nobaz_andqux = stal::Set::Union(vec![stal::Set::Key(b"qux".to_vec()), foobar_nobaz]);
//!
//! assert_eq!(
//!    stal::Stal::new("SMEMBERS".to_string(), foobar_nobaz_andqux).solve(),
//!    (
//!     vec![
//!         vec![b"MULTI".to_vec()],
//!         vec![b"SINTERSTORE".to_vec(), b"stal:2".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!         vec![b"SDIFFSTORE".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec(), b"baz".to_vec()],
//!         vec![b"SUNIONSTORE".to_vec(), b"stal:0".to_vec(), b"qux".to_vec(), b"stal:1".to_vec()],
//!         vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//!         vec![b"DEL".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec()],
//!         vec![b"EXEC".to_vec()],
//!     ],
//!     4
//!    ));
//! ```
//!
//! `stal-rs` translates the internal calls to  `SUNION`, `SDIFF` and
//! `SINTER` into `SDIFFSTORE`, `SINTERSTORE` and `SUNIONSTORE` to
//! perform the underlying operations, and it takes care of generating
//! and deleting any temporary keys.
//!
//! The outmost command can be any set operation, for example:
//!
//! ```rust
//! extern crate stal;
//! let myset = stal::Set::Key(b"my set".to_vec());
//! stal::Stal::new("SCARD".to_string(), myset).solve();
//! ```
//!
//! If you want to preview the commands `Stal` will send to generate
//! the results, you can use `Stal.explain`:
//!
//! ```rust
//! extern crate stal;
//!
//! assert_eq!(
//! stal::Stal::new("SMEMBERS".to_string(),
//!         stal::Set::Inter(vec![
//!             stal::Set::Union(vec![
//!                 stal::Set::Key(b"foo".to_vec()),
//!                 stal::Set::Key(b"bar".to_vec()),
//!                 ]),
//!             stal::Set::Key(b"baz".to_vec()),
//!             ])
//!         ).explain(),
//! vec![
//!     vec![b"SUNIONSTORE".to_vec(), b"stal:1".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!     vec![b"SINTERSTORE".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"baz".to_vec()],
//!     vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//! ]
//! )
//! ```
//!
//! All commands are wrapped in a `MULTI/EXEC` transaction.
//!
//! [redis]: http://redis.io

#![crate_name = "stal"]
#![crate_type = "lib"]

/// A set of values. It can be generated from a Redis key or from a set
/// operation based on other sets.
#[derive(Clone)]
pub enum Set {
    /// A key
    Key(Vec<u8>),
    /// All the elements in any of the provided sets
    Union(Vec<Set>),
    /// All the elements in all the sets
    Inter(Vec<Set>),
    /// All the elements in the first set that are not in the other sets
    Diff(Vec<Set>),
}
use Set::*;

impl Set {
    /// Gets the commands to get a list of ids for this set
    pub fn ids(&self) -> Stal {
        let (op, sets) = match *self {
            Key(_) => return Stal::from_template(vec![b"SMEMBERS".to_vec(), vec![]], vec![(self.clone(), 1)]),
            Union(ref sets) => ("SUNION", sets),
            Inter(ref sets) => ("SINTER", sets),
            Diff(ref sets) => ("SDIFF", sets),
        };
        let mut command = vec![op.as_bytes().to_vec()];
        command.extend(sets.iter().map(|_| vec![]).collect::<Vec<_>>());
        let mut setv = vec![];
        for i in 0..sets.len() {
            setv.push((sets[i].clone(), i + 1));
        }
        Stal::from_template(command, setv)
    }

    /// Maps the operation to its Redis command name.
    fn command(&self) -> &'static str {
        match *self {
            Key(_) => unreachable!(),
            Union(_) => "SUNIONSTORE",
            Inter(_) => "SINTERSTORE",
            Diff(_) => "SDIFFSTORE",
        }
    }

    /// Appends the operation to `ops` and any temporary id created to `ids`.
    /// Returns the key representing the set.
    pub fn convert(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) -> Vec<u8> {
        let sets = match *self {
            Key(ref k) => return k.clone(),
            Union(ref sets) => sets,
            Inter(ref sets) => sets,
            Diff(ref sets) => sets,
        };

        let mut op = Vec::with_capacity(2 + sets.len());
        let id = format!("stal:{}", ids.len());
        let r = id.as_bytes().to_vec();
        ids.push(id);
        op.push(self.command().as_bytes().to_vec());
        op.push(r.clone());
        op.extend(sets.into_iter().map(|s| s.convert(ids, ops)));
        ops.push(op);
        r
    }
}

/// An operation to be executed on a set
pub struct Stal {
    /// A Redis command
    command: Vec<Vec<u8>>,
    /// Set in which execute the operation
    sets: Vec<(Set, usize)>,
}

impl Stal {
    pub fn new(operation: String, set: Set) -> Self {
        Stal {
            command: vec![operation.as_bytes().to_vec(), vec![]],
            sets: vec![(set, 1)],
        }
    }

    /// Takes an arbitrary command that uses one or more sets. The `command`
    /// must have placeholders where the set keys should go. Each element
    /// in `sets` specifies the position in the `command`.
    pub fn from_template(command: Vec<Vec<u8>>, sets: Vec<(Set, usize)>) -> Self {
        Stal {
            command: command,
            sets: sets,
        }
    }

    fn add_ops(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) {
        let mut command = self.command.clone();
        for args in self.sets.iter() {
            command.push(args.0.convert(ids, ops));
            command.swap_remove(args.1);
        }
        ops.push(command);
    }

    /// Returns a list of operations to run. For debug only.
    pub fn explain(&self) -> Vec<Vec<Vec<u8>>> {
        let mut ids = vec![];
        let mut ops = vec![];
        self.add_ops(&mut ids, &mut ops);
        ops
    }

    /// Returns a lit of operations, wrapped in a multi/exec.
    /// The last operation is always exec, and the returned `usize` indicates
    /// the return value of the `operation`.
    pub fn solve(&self) -> (Vec<Vec<Vec<u8>>>, usize) {
        let mut ids = vec![];
        let mut ops = vec![vec![b"MULTI".to_vec()]];
        self.add_ops(&mut ids, &mut ops);
        let pos = ops.len() - 1;
        if ids.len() > 0 {
            let mut del = vec![b"DEL".to_vec()];
            del.extend(ids.into_iter().map(|x| x.as_bytes().to_vec()));
            ops.push(del);
        }
        ops.push(vec![b"EXEC".to_vec()]);
        (ops, pos)
    }
}
<|endoftext|>


<commit_before>extern crate asnom;

extern crate bytes;
extern crate byteorder;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate log;
extern crate native_tls;
#[macro_use]
extern crate nom;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_proto;
extern crate tokio_service;
extern crate tokio_tls;
extern crate url;

mod add;
mod bind;
mod conn;
mod delete;
mod ldap;
mod modify;
mod modifydn;
mod protocol;
mod search;
mod filter;

pub use conn::{EntryStream, LdapConn, LdapConnAsync};
pub use ldap::Ldap;
pub use modify::Mod;
pub use protocol::LdapResult;
pub use search::{DerefAliases, Scope, SearchEntry, SearchOptions};

pub mod asn1 {
    pub use asnom::structure::StructureTag;
    pub use asnom::structures::{ASNTag, Boolean, Enumerated, ExplicitTag, Integer, Null, OctetString, Sequence, Set, Tag};
    pub use asnom::common::TagClass;
}
<commit_msg>Reexport search::SearchStream<commit_after>extern crate asnom;

extern crate bytes;
extern crate byteorder;
#[macro_use]
extern crate futures;
#[macro_use]
extern crate log;
extern crate native_tls;
#[macro_use]
extern crate nom;
extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_proto;
extern crate tokio_service;
extern crate tokio_tls;
extern crate url;

mod add;
mod bind;
mod conn;
mod delete;
mod ldap;
mod modify;
mod modifydn;
mod protocol;
mod search;
mod filter;

pub use conn::{EntryStream, LdapConn, LdapConnAsync};
pub use ldap::Ldap;
pub use modify::Mod;
pub use protocol::LdapResult;
pub use search::{DerefAliases, Scope, SearchEntry, SearchOptions, SearchStream};

pub mod asn1 {
    pub use asnom::structure::StructureTag;
    pub use asnom::structures::{ASNTag, Boolean, Enumerated, ExplicitTag, Integer, Null, OctetString, Sequence, Set, Tag};
    pub use asnom::common::TagClass;
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn print_empty() {
        let v = PVec::<u32>::new();
        println!("{:?}", v);
    }
}

use std::sync::Arc;

#[cfg(feature = "narrow_branching")]
//const BRANCH_EXPONENT: usize = 2;
const BRANCH_FACTOR: usize = 4; // 2^BRANCH_EXPONENT
#[cfg(not(feature = "narrow_branching"))]
//const BRANCH_EXPONENT: usize = 5;
const BRANCH_FACTOR: usize = 32; // 2^BRANCH_EXPONENT

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Default)]
pub struct PVec<T> {
    root: Node<T>,
    len: usize,
}

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
enum Node<T> {
    Branch { children: [Option<Arc<PVec<T>>>; BRANCH_FACTOR], },
    Leaf { elements: [Option<T>; BRANCH_FACTOR], },
}

#[cfg(feature = "narrow_branching")]
macro_rules! empty_arr {
    () => {
        [None, None,
         None, None]
    }
}

#[cfg(not(feature = "narrow_branching"))]
macro_rules! empty_arr {
    () => {
        [None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None]
    }
}

impl<T> Default for Node<T> {
    fn default() -> Self {
        Node::Leaf { elements: empty_arr!() }
    }
}

impl<T> PVec<T> {
    pub fn new() -> Self {
        PVec {
            root: Node::default(),
            len: 0,
        }
    }
}
<commit_msg>Implemented pass-by-value `push` for RB-tree<commit_after>#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn print_pushes() {
        let mut v = PVec::new();
        println!("{:?}", v);
        for i in 0..(BRANCH_FACTOR * 4 + 1) {
            v = v.push(i);
            println!("{:#?}", v)
        }
    }
}

use std::sync::Arc;
use std::mem;

#[cfg(feature = "narrow_branching")]
//const BRANCH_EXPONENT: usize = 2;
const BRANCH_FACTOR: usize = 4; // 2^BRANCH_EXPONENT
#[cfg(not(feature = "narrow_branching"))]
//const BRANCH_EXPONENT: usize = 5;
const BRANCH_FACTOR: usize = 32; // 2^BRANCH_EXPONENT

#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Default)]
pub struct PVec<T> {
    root: Node<T>,
    len: usize,
}

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
enum Node<T> {
    Branch { children: [Option<Arc<PVec<T>>>; BRANCH_FACTOR], },
    Leaf { elements: [Option<T>; BRANCH_FACTOR], },
}

#[cfg(feature = "narrow_branching")]
macro_rules! empty_arr {
    () => {
        [None, None,
         None, None]
    }
}

#[cfg(not(feature = "narrow_branching"))]
macro_rules! empty_arr {
    () => {
        [None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None,
         None, None, None, None]
    }
}

impl<T> Default for Node<T> {
    fn default() -> Self {
        Node::Leaf { elements: empty_arr!() }
    }
}

impl<T: Clone> Clone for Node<T> {
    fn clone(&self) -> Self {
        match *self {
            Node::Branch { ref children } => {
                let mut kids = empty_arr!();
                for i in 0..children.len() {
                    kids[i] = children[i].clone()
                }
                Node::Branch { children: kids }
            }
            Node::Leaf { ref elements } => {
                let mut elems = empty_arr!();
                for i in 0..elements.len() {
                    elems[i] = elements[i].clone()
                }
                Node::Leaf { elements: elems }
            }
        }
    }
}

use std::fmt::Debug;

impl<T: Clone + Debug> PVec<T> {
    pub fn new() -> Self {
        PVec {
            root: Node::default(),
            len: 0,
        }
    }

    pub fn push(self, element: T) -> Self {
        if self.len < BRANCH_FACTOR {
            let mut new = self.clone();
            match new.root {
                Node::Leaf { ref mut elements } => {
                    elements[new.len] = Some(element);
                    new.len += 1;
                }
                Node::Branch { ref mut children } => {
                    let i = self.len;
                    let old = mem::replace(&mut children[i], None);
                    match old {
                        Some(child_ref) => {
                            let child = (*child_ref).clone().push(element);
                            if child.len == BRANCH_FACTOR {
                                new.len += 1;
                            }
                            mem::replace(&mut children[i], Some(Arc::new(child)));
                        }
                        None => {
                            let child = PVec::new().push(element);
                            mem::replace(&mut children[i], Some(Arc::new(child)));
                        }
                    }
                }
            }
            new
        } else {
            let mut children = empty_arr!();
            children[0] = Some(Arc::new(self));
            children[1] = Some(Arc::new(PVec::new().push(element)));
            PVec {
                root: Node::Branch { children: children },
                len: 1,
            }
        }
    }
}
<|endoftext|>


<commit_before>//! par is a terminal interface for rendering progress bars
//!
//! #example
//! ```rust
//! use par::Bar;
//! fn main() {
//!     let bar = Bar::new(100);
//!     for i in 1..101 {
//!         bar.add(i);
//!         std::thread::sleep_ms(10);
//!     }
//!     bar.finish_print("done")
//! }
//! ```
extern crate capsize;
extern crate termsize;

use capsize::Capacity;
use std::io::{self, Read, Write};
use std::iter;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::default::Default;

static FORMAT: &'static str = "[=>_]";

/// Unit of display for progress counter
pub enum Units {
    /// No units
    None,
    /// format progress in humanized byte sizes
    Bytes
}

impl Default for Units {
    fn default() -> Units {
        Units::None
    }
}

/// Preference for reporting progress
pub enum Reporter {
    /// Report progress to stdout
    StdOut,
    /// Report progress to stderr
    StdErr,
    /// Report progress to a callback
    Callback(fn(String) -> ())
}

impl Default for Reporter {
    fn default() -> Reporter {
        Reporter::StdErr
    }
}

/// IO progress. Implementations for Read and Write are provided
pub struct IO<T> {
    inner: T,
    bar: Bar
}

impl <R: Read> IO<R> {
    pub fn new(read: R, bar: Bar) -> IO<R> {
        IO {
            inner: read,
            bar: bar
        }
    }
}

impl <R: Read> Read for IO<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = try!(self.inner.read(buf));
        self.bar.add(n);
        Ok(n)
    }
}

impl <W: Write> IO<W> {
    pub fn new(write: W, bar: Bar) -> IO<W> {
        IO {
            inner: write,
            bar: bar
        }
    }
}

impl <W: Write> Write for IO<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.bar.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/// A bar of progress information
#[derive(Default)]
pub struct Bar {
    total: usize,
    /// show progress percent, defaults to true
    pub show_percent: bool,
    /// show progress counter. Defaults to true
    pub show_counter: bool,
    /// show main progress bar UI. Defaults to true
    pub show_bar: bool,
    /// an arbitrary string label to prefix display with
    pub prefix: String,
    /// preference for reporting progress
    pub reporter: Reporter,
    current: AtomicUsize,
    units: Units,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_empty: String,
    bar_end: String
}

impl Bar {
    /// creates a new bar with a target total size of `total`
    pub fn new(total: usize) -> Bar {
        let mut bar = Bar {
            total: total,
            show_percent: true,
            show_counter: true,
            show_bar: true,
            ..Default::default()
        };
        bar.format(FORMAT);
        bar
    }

    /// sets a custom format for display
    /// Example: bar.format("[=>_]")
    pub fn format(&mut self, spec: &str) {
        if spec.len() == 5 {
            let parts = spec.split("").collect::<Vec<&str>>();
            self.bar_start = parts[1].to_owned();
            self.bar_current = parts[2].to_owned();
            self.bar_current_n = parts[3].to_owned();
            self.bar_empty = parts[4].to_owned();
            self.bar_end = parts[5].to_owned();
        }
    }

    /// increment bar count by one
    pub fn incr(&self) -> usize {
        self.add(1)
    }

    /// add 1 to the bar count
    pub fn add(&self, delta: usize) -> usize {
        let prev = self.current.fetch_add(delta, Ordering::Relaxed);
        self.update();
        prev
    }

    /// sets the bar count to specified value
    pub fn set(&self, value: usize) {
        self.current.store(value, Ordering::Relaxed);
        self.update()
    }

    pub fn update(&self) {
        let current = self.current.load(Ordering::Relaxed);
        if current <= self.total {
            self.write(current)
        }
    }

    pub fn finish_print(&self, msg: &str) {
        println!("");
        println!("{}", msg)
    }

    fn width(&self) -> usize {
        termsize::get().map(|s|s.cols as usize).unwrap_or(80)
    }

    fn write(&self, current: usize) {
        fn repeat(what: &str, n: usize) -> String {
            iter::repeat(what).take(n).collect::<String>()
        }

        fn unit(value: usize, units: &Units) -> String {
            match *units {
                Units::None => value.to_string(),
                Units::Bytes => (value as i64).capacity()
            }
        }

        let width = self.width();

        let mut prefix = self.prefix.clone();
        let mut mid = String::new();
        let mut suffix = String::new();

        // counter
        if self.show_counter {
            prefix = prefix + &format!(
                "{} / {} ", unit(current, &self.units), unit(self.total, &self.units)
            );
        }

        // percent complete
        if self.show_percent {
            let percent = current as f64 / (self.total as f64 / 100_f64);
            suffix = suffix + &format!(
                " {:.*} %", 2, percent
            );
        }

        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let cur_count = (
                    (current as f64 / self.total as f64) * size as f64
                ).ceil() as usize;
                let empt_count = size - cur_count;
                mid = self.bar_start.clone();
                if empt_count > 0 {
                    mid = mid + &repeat(self.bar_current.as_ref(), (cur_count - 1) as usize) +  &self.bar_current_n;
                } else {
                    mid = mid + &repeat(self.bar_current.as_ref(), cur_count as usize);
                }
                mid = mid + &repeat(self.bar_empty.as_ref(), (empt_count) as usize) + &self.bar_end
            }
        }
        let mut display = prefix + &mid + &suffix;
        if display.len() < width {
            let remaining = width - display.len();
            display = display + &repeat(" ", remaining);
        }

        match self.reporter {
            Reporter::StdErr => {
                let _ = write!(&mut std::io::stderr(), "\r{}", display);
            },
            Reporter::StdOut => {
                let _ = write!(&mut std::io::stderr(), "\r{}", display);
            },
            Reporter::Callback(cb) => {
                cb(display);
            }
        }
    }
}

#[test]
fn it_works() {
}
<commit_msg>disambiguate reader and writer<commit_after>//! par is a terminal interface for rendering progress bars
//!
//! #example
//! ```rust
//! use par::Bar;
//! fn main() {
//!     let bar = Bar::new(100);
//!     for i in 1..101 {
//!         bar.add(i);
//!         std::thread::sleep_ms(10);
//!     }
//!     bar.finish_print("done")
//! }
//! ```
extern crate capsize;
extern crate termsize;

use capsize::Capacity;
use std::io::{self, Read, Write};
use std::iter;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::default::Default;

static FORMAT: &'static str = "[=>_]";

/// Unit of display for progress counter
pub enum Units {
    /// No units
    None,
    /// format progress in humanized byte sizes
    Bytes
}

impl Default for Units {
    fn default() -> Units {
        Units::None
    }
}

/// Preference for reporting progress
pub enum Reporter {
    /// Report progress to stdout
    StdOut,
    /// Report progress to stderr
    StdErr,
    /// Report progress to a callback
    Callback(fn(String) -> ())
}

impl Default for Reporter {
    fn default() -> Reporter {
        Reporter::StdErr
    }
}

/// write progress. Implementations for Read and Write are provided
pub struct Writer<T> {
    inner: T,
    bar: Bar
}

/// read progress. Implementations for Read and Write are provided
pub struct Reader<T> {
    inner: T,
    bar: Bar
}

impl <R: Read> Reader<R> {
    pub fn new(read: R, bar: Bar) -> Reader<R> {
        Reader {
            inner: read,
            bar: bar
        }
    }
}

impl <R: Read> Read for Reader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = try!(self.inner.read(buf));
        self.bar.add(n);
        Ok(n)
    }
}

impl <W: Write> Writer<W> {
    pub fn new(write: W, bar: Bar) -> Writer<W> {
        Writer {
            inner: write,
            bar: bar
        }
    }
}

impl <W: Write> Write for Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.bar.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/// A bar of progress information
#[derive(Default)]
pub struct Bar {
    total: usize,
    /// show progress percent, defaults to true
    pub show_percent: bool,
    /// show progress counter. Defaults to true
    pub show_counter: bool,
    /// show main progress bar UI. Defaults to true
    pub show_bar: bool,
    /// an arbitrary string label to prefix display with
    pub prefix: String,
    /// preference for reporting progress
    pub reporter: Reporter,
    current: AtomicUsize,
    pub units: Units,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_empty: String,
    bar_end: String
}

impl Bar {
    /// creates a new bar with a target total size of `total`
    pub fn new(total: usize) -> Bar {
        let mut bar = Bar {
            total: total,
            show_percent: true,
            show_counter: true,
            show_bar: true,
            ..Default::default()
        };
        bar.format(FORMAT);
        bar
    }

    /// sets a custom format for display
    /// Example: bar.format("[=>_]")
    pub fn format(&mut self, spec: &str) {
        if spec.len() == 5 {
            let parts = spec.split("").collect::<Vec<&str>>();
            self.bar_start = parts[1].to_owned();
            self.bar_current = parts[2].to_owned();
            self.bar_current_n = parts[3].to_owned();
            self.bar_empty = parts[4].to_owned();
            self.bar_end = parts[5].to_owned();
        }
    }

    /// increment bar count by one
    pub fn incr(&self) -> usize {
        self.add(1)
    }

    /// add 1 to the bar count
    pub fn add(&self, delta: usize) -> usize {
        let prev = self.current.fetch_add(delta, Ordering::Relaxed);
        self.update();
        prev
    }

    /// sets the bar count to specified value
    pub fn set(&self, value: usize) {
        self.current.store(value, Ordering::Relaxed);
        self.update()
    }

    pub fn update(&self) {
        let current = self.current.load(Ordering::Relaxed);
        if current <= self.total {
            self.write(current)
        }
    }

    pub fn finish_print(&self, msg: &str) {
        println!("");
        println!("{}", msg)
    }

    fn width(&self) -> usize {
        termsize::get().map(|s|s.cols as usize).unwrap_or(80)
    }

    fn write(&self, current: usize) {
        fn repeat(what: &str, n: usize) -> String {
            iter::repeat(what).take(n).collect::<String>()
        }

        fn unit(value: usize, units: &Units) -> String {
            match *units {
                Units::None => value.to_string(),
                Units::Bytes => (value as i64).capacity()
            }
        }

        let width = self.width();

        let mut prefix = self.prefix.clone();
        let mut mid = String::new();
        let mut suffix = String::new();

        // counter
        if self.show_counter {
            prefix = prefix + &format!(
                "{} / {} ", unit(current, &self.units), unit(self.total, &self.units)
            );
        }

        // percent complete
        if self.show_percent {
            let percent = current as f64 / (self.total as f64 / 100_f64);
            suffix = suffix + &format!(
                " {:.*} %", 2, percent
            );
        }

        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let cur_count = (
                    (current as f64 / self.total as f64) * size as f64
                ).ceil() as usize;
                let empt_count = size - cur_count;
                mid = self.bar_start.clone();
                if empt_count > 0 {
                    mid = mid + &repeat(self.bar_current.as_ref(), (cur_count - 1) as usize) +  &self.bar_current_n;
                } else {
                    mid = mid + &repeat(self.bar_current.as_ref(), cur_count as usize);
                }
                mid = mid + &repeat(self.bar_empty.as_ref(), (empt_count) as usize) + &self.bar_end
            }
        }
        let mut display = prefix + &mid + &suffix;
        if display.len() < width {
            let remaining = width - display.len();
            display = display + &repeat(" ", remaining);
        }

        match self.reporter {
            Reporter::StdErr => {
                let _ = write!(&mut std::io::stderr(), "\r{}", display);
            },
            Reporter::StdOut => {
                let _ = write!(&mut std::io::stderr(), "\r{}", display);
            },
            Reporter::Callback(cb) => {
                cb(display);
            }
        }
    }
}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>//! This is all based on https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html.

extern crate encoding;
extern crate regex;

use encoding::Encoding;
use encoding::DecoderTrap;
use encoding::all::ISO_8859_1;
use regex::Regex;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::io;
use std::io::BufRead;
use std::io::Bytes;
use std::io::ErrorKind;
use std::io::Read;
use std::iter::Peekable;

/////////////////////

#[derive(Debug)]
pub struct PropertiesError {
  description: String,
  cause: Option<Box<Error+Send+Sync>>,
}

impl PropertiesError {
  fn new(description: &str, cause: Option<Box<Error+Send+Sync>>) -> Self {
    PropertiesError {
      description: description.to_string(),
      cause: cause,
    }
  }
}

impl Error for PropertiesError {
  fn description(&self) -> &str {
    &self.description
  }
}

impl Display for PropertiesError {
  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
    Display::fmt(&self.description, f)
  }
}

/////////////////////

#[derive(PartialEq,Eq,Debug)]
struct NaturalLine(String);

// We can't use BufRead.lines() because it doesn't use the proper line endings
struct NaturalLines<R: Read> {
  bytes: Peekable<Bytes<R>>,
  eof: bool,
}

impl<R: Read> NaturalLines<R> {
  fn new(reader: R) -> Self {
    NaturalLines {
      bytes: reader.bytes().peekable(),
      eof: false,
    }
  }
}

const LF: u8 = 10;
const CR: u8 = 13;

impl< R: Read> Iterator for NaturalLines<R> {
  type Item = io::Result<NaturalLine>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.eof {
      return None;
    }
    let mut buf = String::new();
    loop {
      match self.bytes.next() {
        Some(r) => match r {
          Ok(b) => {
            match b {
              CR => {
                match self.bytes.peek() {
                  Some(&Ok(LF)) => { self.bytes.next(); },
                  _ => (),
                }
                return Some(Ok(NaturalLine(buf)));
              },
              LF => return Some(Ok(NaturalLine(buf))),
              _ => match ISO_8859_1.decode(&[b], DecoderTrap::Strict) {
                Ok(s) => buf.push_str(&s),
                Err(_) => return Some(Err(io::Error::new(ErrorKind::InvalidData, "Error reading ISO-8859-1 encoding"))),
              },
            };
          },
          Err(e) => return Some(Err(e)),
        },
        None => {
          self.eof = true;
          return Some(Ok(NaturalLine(buf)));
        },
      }
    }
  }
}

/////////////////////

#[derive(PartialEq,Eq,Debug)]
struct LogicalLine(String);

struct LogicalLines<I: Iterator<Item=io::Result<NaturalLine>>> {
  physical_lines: I,
  eof: bool,
}

impl<I: Iterator<Item=io::Result<NaturalLine>>> LogicalLines<I> {
  fn new(physical_lines: I) -> Self {
    LogicalLines {
      physical_lines: physical_lines,
      eof: false,
    }
  }
}

fn count_ending_backslashes(s: &str) -> usize {
  let chars: Vec<char> = s.chars().collect();
  let n = chars.len();
  let mut i = n;
  while i > 0 {
    i -= 1;
    if chars[i] != '\\' {
      return n - 1 - i;
    }
  }
  n
}

impl<I: Iterator<Item=io::Result<NaturalLine>>> Iterator for LogicalLines<I> {
  type Item = io::Result<LogicalLine>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.eof {
      return None;
    }
    let mut buf = String::new();
    let mut first = true;
    loop {
      match self.physical_lines.next() {
        Some(Err(e)) => return Some(Err(e)),
        Some(Ok(NaturalLine(line))) => {
          buf.push_str(
            if first {
              &line
            } else {
              line.trim_left()
            }
          );
          if count_ending_backslashes(&line) % 2 == 1 {
            buf.pop();
          } else {
            return Some(Ok(LogicalLine(buf)));
          }
        },
        None => {
          self.eof = true;
          return None;
        },
      }
      first = false;
    }
  }
}

/////////////////////

#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
enum ParsedLine<'a> {
  Comment(&'a str),
  KVPair(&'a str, &'a str),
}

/////////////////////

// Note that this is not the same as char.is_whitespace.  This is correct.
fn is_whitespace(c: char) -> bool {
  match c {
    ' ' | '\t' | '\x0C' | '\n' | '\r' => true,
    _ => false,
  }
}

struct LineParser {
  re: Regex,
}

impl LineParser {
  fn new() -> Self {
    // Note that we have to use \x20 to match a space since we're ignoring whitespace
    let re_str = r"(?x) # allow whitespace and comments
      (?:^|[^\\])(?:\\\\)* # ignore an even number of preceding backslashes
      (
        [\x20\t\r\n\x0c]*[:=][\x20\t\r\n\x0c]* # try matching an actual separator (: or =)
        |
        [\x20\t\r\n\x0c]+ # try matching whitespace only
      )
    ";
    LineParser {
      re: Regex::new(re_str).unwrap(),
    }
  }

  fn parse_line<'a>(&self, line: &'a str) -> Option<ParsedLine<'a>> {
    let line = trim_unescaped_whitespace(line);
    if line.is_empty() {
      return None;
    }
    if line.starts_with("#") || line.starts_with("!") {
      return Some(ParsedLine::Comment(trim_unescaped_whitespace(&line[1..])));
    }
    match self.re.captures(line) {
      Some(c) => {
        let (start, end) = c.pos(1).unwrap();
        Some(ParsedLine::KVPair(&line[..start], &line[end..]))
      },
      None => Some(ParsedLine::KVPair(line, "")),
    }
  }
}

fn trim_unescaped_whitespace(s: &str) -> &str {
  fn ltrim(s: &str) -> &str {
    for (i, c) in s.char_indices() {
      if !is_whitespace(c) {
        return &s[i..];
      }
    }
    "" // all whitespace
  }

  fn rtrim(s: &str) -> &str {
    let mut ix = 0; // index of first whitespace character after last non-whitespace character
    let mut escaped = false;
    let mut last_was_non_whitespace = true;
    for (i, c) in s.char_indices() {
      if escaped {
        escaped = false;
        last_was_non_whitespace = true;
      } else if c == '\\' {
        escaped = true;
      } else if is_whitespace(c) {
        if last_was_non_whitespace {
          ix = i;
        }
        last_was_non_whitespace = false;
      } else {
        last_was_non_whitespace = true;
      }
    }
    if last_was_non_whitespace {
      s
    } else {
      &s[..ix]
    }
  }

  ltrim(rtrim(s))
}

pub fn load(reader: &mut BufRead) -> Result<HashMap<String, String>, PropertiesError> {
  let map = HashMap::new();
  let mut lines = LogicalLines::new(NaturalLines::new(reader));
  loop {
    match lines.next() {
      Some(line) => (), // TODO
      None => break,
    }
  }
  Ok(map)
}

/////////////////////

#[cfg(test)]
mod tests {
  use super::CR;
  use super::LF;
  use super::LogicalLine;
  use super::LogicalLines;
  use super::NaturalLine;
  use super::NaturalLines;
  use super::ParsedLine;

  const SP: u8 = 32; // space

  #[test]
  fn natural_lines() {
    let data = [
      (vec![], vec![""]),
      (vec![SP], vec![" "]),
      (vec![SP, CR], vec![" ", ""]),
      (vec![SP, LF], vec![" ", ""]),
      (vec![SP, CR, LF], vec![" ", ""]),
      (vec![SP, CR, SP], vec![" ", " "]),
      (vec![SP, LF, SP], vec![" ", " "]),
      (vec![SP, CR, LF, SP], vec![" ", " "]),
      (vec![CR], vec!["", ""]),
      (vec![LF], vec!["", ""]),
      (vec![CR, LF], vec!["", ""]),
      (vec![CR, SP], vec!["", " "]),
      (vec![LF, SP], vec!["", " "]),
      (vec![CR, LF, SP], vec!["", " "]),
    ];
    for &(ref bytes, ref lines) in data.iter() {
      let reader = &bytes as &[u8];
      let mut iter = NaturalLines::new(reader);
      for line in lines {
        match (line.to_string(), iter.next()) {
          (e, Some(Ok(NaturalLine(a)))) => if e != a {
            panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", bytes, e, a);
          },
          (e, a) => panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", bytes, e, a),
        }
      }
      match iter.next() {
        None => (),
        a => panic!("Failure while processing {:?}.  Expected None, but was {:?}", bytes, a),
      }
    }
  }

  #[test]
  fn logical_lines() {
    let data = [
      (vec![], vec![]),
      (vec!["foo"], vec!["foo"]),
      (vec!["foo", "bar"], vec!["foo", "bar"]),
      (vec!["foo\\", "bar"], vec!["foobar"]),
      (vec!["foo\\\\", "bar"], vec!["foo\\\\", "bar"]),
      (vec!["foo\\\\\\", "bar"], vec!["foo\\\\bar"]),
      (vec!["foo\\", " bar"], vec!["foobar"]),
      (vec!["\u{1F41E}\\", "\u{1F41E}"], vec!["\u{1F41E}\u{1F41E}"]),
      (vec!["\u{1F41E}\\", " \u{1F41E}"], vec!["\u{1F41E}\u{1F41E}"]),
    ];
    for &(ref input_lines, ref lines) in data.iter() {
      let mut iter = LogicalLines::new(input_lines.iter().map(|x| Ok(NaturalLine(x.to_string()))));
      for line in lines {
        match (line.to_string(), iter.next()) {
          (e, Some(Ok(LogicalLine(a)))) => if e != a {
            panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", input_lines, e, a);
          },
          (e, a) => panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", input_lines, e, a),
        }
      }
      match iter.next() {
        None => (),
        a => panic!("Failure while processing {:?}.  Expected None, but was {:?}", input_lines, a),
      }
    }
  }

  #[test]
  fn count_ending_backslashes() {
    assert_eq!(0, super::count_ending_backslashes(""));

    assert_eq!(0, super::count_ending_backslashes("x"));
    assert_eq!(1, super::count_ending_backslashes("\\"));

    assert_eq!(0, super::count_ending_backslashes("xx"));
    assert_eq!(0, super::count_ending_backslashes("\\x"));
    assert_eq!(1, super::count_ending_backslashes("x\\"));
    assert_eq!(2, super::count_ending_backslashes("\\\\"));

    assert_eq!(0, super::count_ending_backslashes("xxx"));
    assert_eq!(0, super::count_ending_backslashes("\\xx"));
    assert_eq!(0, super::count_ending_backslashes("x\\x"));
    assert_eq!(0, super::count_ending_backslashes("\\\\x"));
    assert_eq!(1, super::count_ending_backslashes("xx\\"));
    assert_eq!(1, super::count_ending_backslashes("\\x\\"));
    assert_eq!(2, super::count_ending_backslashes("x\\\\"));
    assert_eq!(3, super::count_ending_backslashes("\\\\\\"));

    assert_eq!(0, super::count_ending_backslashes("x\u{1F41E}"));
    assert_eq!(0, super::count_ending_backslashes("\\\u{1F41E}"));
    assert_eq!(0, super::count_ending_backslashes("\u{1F41E}x"));
    assert_eq!(1, super::count_ending_backslashes("\u{1F41E}\\"));
  }

  #[test]
  fn parse_line() {
    let data = [
      ("", None),
      (" ", None),
      ("\\ ", Some(ParsedLine::KVPair("\\ ", ""))),
      ("# foo", Some(ParsedLine::Comment("foo"))),
      (" # foo", Some(ParsedLine::Comment("foo"))),
      ("a # foo", Some(ParsedLine::KVPair("a", "# foo"))),
      ("a", Some(ParsedLine::KVPair("a", ""))),
      ("a = b", Some(ParsedLine::KVPair("a", "b"))),
      ("a : b", Some(ParsedLine::KVPair("a", "b"))),
      ("a b", Some(ParsedLine::KVPair("a", "b"))),
      (" a = b", Some(ParsedLine::KVPair("a", "b"))),
      (" a : b", Some(ParsedLine::KVPair("a", "b"))),
      (" a b", Some(ParsedLine::KVPair("a", "b"))),
      ("a:=b", Some(ParsedLine::KVPair("a", "=b"))),
      ("a=:b", Some(ParsedLine::KVPair("a", ":b"))),
      ("a b:c", Some(ParsedLine::KVPair("a", "b:c"))),
      ("a\\ \\:\\=b c", Some(ParsedLine::KVPair("a\\ \\:\\=b", "c"))),
      ("a\\ \\:\\=b=c", Some(ParsedLine::KVPair("a\\ \\:\\=b", "c"))),
      ("a\\\\ \\\\:\\\\=b c", Some(ParsedLine::KVPair("a\\\\", "\\\\:\\\\=b c"))),
      ("\\  b", Some(ParsedLine::KVPair("\\ ", "b"))),
      ("=", Some(ParsedLine::KVPair("", ""))),
      ("\u{1F41E}=\u{1F41E}", Some(ParsedLine::KVPair("\u{1F41E}", "\u{1F41E}"))),
    ];
    let splitter = super::LineParser::new();
    for &(line, ref expected) in data.iter() {
      let actual = splitter.parse_line(line);
      if expected != &actual {
        panic!("Failed when splitting {:?}.  Expected {:?} but got {:?}", line, expected, actual);
      }
    }
  }

  #[test]
  fn trim_unescaped_whitespace() {
    let data = [
      ("", ""),
      (" ", ""),
      ("x", "x"),
      (" x x ", "x x"),
      ("  xx  xx  ", "xx  xx"),
      (" \\  ", "\\ "),
      (" \u{1F41E} \u{1F41E} ", "\u{1F41E} \u{1F41E}"),
    ];
    for &(raw, trimmed) in data.iter() {
      let t = super::trim_unescaped_whitespace(raw);
      if trimmed != t {
        panic!("Failed when trimming {:?}.  Expected {:?} but got {:?}", raw, trimmed, t);
      }
    }
  }
}
<commit_msg>Use regex for parsing and get rid of trim_unescaped_whitespace<commit_after>//! This is all based on https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html.

extern crate encoding;
extern crate regex;

use encoding::Encoding;
use encoding::DecoderTrap;
use encoding::all::ISO_8859_1;
use regex::Regex;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::io;
use std::io::BufRead;
use std::io::Bytes;
use std::io::ErrorKind;
use std::io::Read;
use std::iter::Peekable;

/////////////////////

#[derive(Debug)]
pub struct PropertiesError {
  description: String,
  cause: Option<Box<Error+Send+Sync>>,
}

impl PropertiesError {
  fn new(description: &str, cause: Option<Box<Error+Send+Sync>>) -> Self {
    PropertiesError {
      description: description.to_string(),
      cause: cause,
    }
  }
}

impl Error for PropertiesError {
  fn description(&self) -> &str {
    &self.description
  }
}

impl Display for PropertiesError {
  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
    Display::fmt(&self.description, f)
  }
}

/////////////////////

#[derive(PartialEq,Eq,Debug)]
struct NaturalLine(String);

// We can't use BufRead.lines() because it doesn't use the proper line endings
struct NaturalLines<R: Read> {
  bytes: Peekable<Bytes<R>>,
  eof: bool,
}

impl<R: Read> NaturalLines<R> {
  fn new(reader: R) -> Self {
    NaturalLines {
      bytes: reader.bytes().peekable(),
      eof: false,
    }
  }
}

const LF: u8 = 10;
const CR: u8 = 13;

impl< R: Read> Iterator for NaturalLines<R> {
  type Item = io::Result<NaturalLine>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.eof {
      return None;
    }
    let mut buf = String::new();
    loop {
      match self.bytes.next() {
        Some(r) => match r {
          Ok(b) => {
            match b {
              CR => {
                match self.bytes.peek() {
                  Some(&Ok(LF)) => { self.bytes.next(); },
                  _ => (),
                }
                return Some(Ok(NaturalLine(buf)));
              },
              LF => return Some(Ok(NaturalLine(buf))),
              _ => match ISO_8859_1.decode(&[b], DecoderTrap::Strict) {
                Ok(s) => buf.push_str(&s),
                Err(_) => return Some(Err(io::Error::new(ErrorKind::InvalidData, "Error reading ISO-8859-1 encoding"))),
              },
            };
          },
          Err(e) => return Some(Err(e)),
        },
        None => {
          self.eof = true;
          return Some(Ok(NaturalLine(buf)));
        },
      }
    }
  }
}

/////////////////////

#[derive(PartialEq,Eq,Debug)]
struct LogicalLine(String);

struct LogicalLines<I: Iterator<Item=io::Result<NaturalLine>>> {
  physical_lines: I,
  eof: bool,
}

impl<I: Iterator<Item=io::Result<NaturalLine>>> LogicalLines<I> {
  fn new(physical_lines: I) -> Self {
    LogicalLines {
      physical_lines: physical_lines,
      eof: false,
    }
  }
}

fn count_ending_backslashes(s: &str) -> usize {
  let chars: Vec<char> = s.chars().collect();
  let n = chars.len();
  let mut i = n;
  while i > 0 {
    i -= 1;
    if chars[i] != '\\' {
      return n - 1 - i;
    }
  }
  n
}

impl<I: Iterator<Item=io::Result<NaturalLine>>> Iterator for LogicalLines<I> {
  type Item = io::Result<LogicalLine>;

  fn next(&mut self) -> Option<Self::Item> {
    if self.eof {
      return None;
    }
    let mut buf = String::new();
    let mut first = true;
    loop {
      match self.physical_lines.next() {
        Some(Err(e)) => return Some(Err(e)),
        Some(Ok(NaturalLine(line))) => {
          buf.push_str(
            if first {
              &line
            } else {
              line.trim_left()
            }
          );
          if count_ending_backslashes(&line) % 2 == 1 {
            buf.pop();
          } else {
            return Some(Ok(LogicalLine(buf)));
          }
        },
        None => {
          self.eof = true;
          return None;
        },
      }
      first = false;
    }
  }
}

/////////////////////

#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
enum ParsedLine<'a> {
  Comment(&'a str),
  KVPair(&'a str, &'a str),
}

/////////////////////

// Note that this is not the same as char.is_whitespace.  This is correct.
fn is_whitespace(c: char) -> bool {
  match c {
    ' ' | '\t' | '\x0C' | '\n' | '\r' => true,
    _ => false,
  }
}

struct LineParser {
  re: Regex,
}

impl LineParser {
  fn new() -> Self {
    // Note that we have to use \x20 to match a space and \x23 to match a pound character since we're ignoring whitespace and comments
    let re_str = r"(?x) # allow whitespace and comments
      ^
      [\x20\t\r\n\x0c]* # ignorable whitespace
      (?:
        [\x23!] # start of comment (# or !)
        [\x20\t\r\n\x0c]* # ignorable whitespace
        (.*?) # comment text
      |
        ((?:[^\\:=\x20\t\r\n\x0c]|\\.)*) # key
        (?:
          [\x20\t\r\n\x0c]*[:=][\x20\t\r\n\x0c]* # try matching an actual separator (: or =)
        |
          [\x20\t\r\n\x0c]+ # try matching whitespace only
        )
        (
          (?:[^\\]|\\.)*? # value
          (?:\\$)? # end of line backslash, can't show up in real input because it's caught by LogicalLines
        )
      |
        (
          (?:[^\\:=\x20\t\r\n\x0c]|\\.)* # key with no value
          (?:\\$)? # end of line backslash, can't show up in real input because it's caught by LogicalLines
        )
      )
      [\x20\t\r\n\x0c]* # ignorable whitespace
      $
    ";
    LineParser {
      re: Regex::new(re_str).unwrap(),
    }
  }

  fn parse_line<'a>(&self, line: &'a str) -> Option<ParsedLine<'a>> {
    match self.re.captures(line) {
      Some(c) => {
        if let Some((start, end)) = c.pos(1) {
          Some(ParsedLine::Comment(&line[start..end]))
        } else if let Some((kstart, kend)) = c.pos(2) {
          let (vstart, vend) = c.pos(3).unwrap(); // key and value either both match or both fail, and the key matched
          Some(ParsedLine::KVPair(&line[kstart..kend], &line[vstart..vend]))
        } else if let Some((start, end)) = c.pos(4) {
          let key = &line[start..end];
          if key == "" {
            None
          } else {
            Some(ParsedLine::KVPair(key, ""))
          }
        } else {
          panic!("Failed to get any groups out of the regular expression.")
        }
      },
      None => panic!("Failed to match on {:?}", line), // This should never happen.  The pattern should match all strings.
    }
  }
}

pub fn load(reader: &mut BufRead) -> Result<HashMap<String, String>, PropertiesError> {
  let map = HashMap::new();
  let mut lines = LogicalLines::new(NaturalLines::new(reader));
  loop {
    match lines.next() {
      Some(line) => (), // TODO
      None => break,
    }
  }
  Ok(map)
}

/////////////////////

#[cfg(test)]
mod tests {
  use super::CR;
  use super::LF;
  use super::LogicalLine;
  use super::LogicalLines;
  use super::NaturalLine;
  use super::NaturalLines;
  use super::ParsedLine;

  const SP: u8 = 32; // space

  #[test]
  fn natural_lines() {
    let data = [
      (vec![], vec![""]),
      (vec![SP], vec![" "]),
      (vec![SP, CR], vec![" ", ""]),
      (vec![SP, LF], vec![" ", ""]),
      (vec![SP, CR, LF], vec![" ", ""]),
      (vec![SP, CR, SP], vec![" ", " "]),
      (vec![SP, LF, SP], vec![" ", " "]),
      (vec![SP, CR, LF, SP], vec![" ", " "]),
      (vec![CR], vec!["", ""]),
      (vec![LF], vec!["", ""]),
      (vec![CR, LF], vec!["", ""]),
      (vec![CR, SP], vec!["", " "]),
      (vec![LF, SP], vec!["", " "]),
      (vec![CR, LF, SP], vec!["", " "]),
    ];
    for &(ref bytes, ref lines) in data.iter() {
      let reader = &bytes as &[u8];
      let mut iter = NaturalLines::new(reader);
      for line in lines {
        match (line.to_string(), iter.next()) {
          (e, Some(Ok(NaturalLine(a)))) => if e != a {
            panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", bytes, e, a);
          },
          (e, a) => panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", bytes, e, a),
        }
      }
      match iter.next() {
        None => (),
        a => panic!("Failure while processing {:?}.  Expected None, but was {:?}", bytes, a),
      }
    }
  }

  #[test]
  fn logical_lines() {
    let data = [
      (vec![], vec![]),
      (vec!["foo"], vec!["foo"]),
      (vec!["foo", "bar"], vec!["foo", "bar"]),
      (vec!["foo\\", "bar"], vec!["foobar"]),
      (vec!["foo\\\\", "bar"], vec!["foo\\\\", "bar"]),
      (vec!["foo\\\\\\", "bar"], vec!["foo\\\\bar"]),
      (vec!["foo\\", " bar"], vec!["foobar"]),
      (vec!["\u{1F41E}\\", "\u{1F41E}"], vec!["\u{1F41E}\u{1F41E}"]),
      (vec!["\u{1F41E}\\", " \u{1F41E}"], vec!["\u{1F41E}\u{1F41E}"]),
    ];
    for &(ref input_lines, ref lines) in data.iter() {
      let mut iter = LogicalLines::new(input_lines.iter().map(|x| Ok(NaturalLine(x.to_string()))));
      for line in lines {
        match (line.to_string(), iter.next()) {
          (e, Some(Ok(LogicalLine(a)))) => if e != a {
            panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", input_lines, e, a);
          },
          (e, a) => panic!("Failure while processing {:?}.  Expected Some(Ok({:?})), but was {:?}", input_lines, e, a),
        }
      }
      match iter.next() {
        None => (),
        a => panic!("Failure while processing {:?}.  Expected None, but was {:?}", input_lines, a),
      }
    }
  }

  #[test]
  fn count_ending_backslashes() {
    assert_eq!(0, super::count_ending_backslashes(""));

    assert_eq!(0, super::count_ending_backslashes("x"));
    assert_eq!(1, super::count_ending_backslashes("\\"));

    assert_eq!(0, super::count_ending_backslashes("xx"));
    assert_eq!(0, super::count_ending_backslashes("\\x"));
    assert_eq!(1, super::count_ending_backslashes("x\\"));
    assert_eq!(2, super::count_ending_backslashes("\\\\"));

    assert_eq!(0, super::count_ending_backslashes("xxx"));
    assert_eq!(0, super::count_ending_backslashes("\\xx"));
    assert_eq!(0, super::count_ending_backslashes("x\\x"));
    assert_eq!(0, super::count_ending_backslashes("\\\\x"));
    assert_eq!(1, super::count_ending_backslashes("xx\\"));
    assert_eq!(1, super::count_ending_backslashes("\\x\\"));
    assert_eq!(2, super::count_ending_backslashes("x\\\\"));
    assert_eq!(3, super::count_ending_backslashes("\\\\\\"));

    assert_eq!(0, super::count_ending_backslashes("x\u{1F41E}"));
    assert_eq!(0, super::count_ending_backslashes("\\\u{1F41E}"));
    assert_eq!(0, super::count_ending_backslashes("\u{1F41E}x"));
    assert_eq!(1, super::count_ending_backslashes("\u{1F41E}\\"));
  }

  #[test]
  fn parse_line() {
    let data = [
      ("", None),
      (" ", None),
      ("\\ ", Some(ParsedLine::KVPair("\\ ", ""))),
      ("# foo", Some(ParsedLine::Comment("foo"))),
      (" # foo", Some(ParsedLine::Comment("foo"))),
      ("a # foo", Some(ParsedLine::KVPair("a", "# foo"))),
      ("a", Some(ParsedLine::KVPair("a", ""))),
      ("a = b", Some(ParsedLine::KVPair("a", "b"))),
      ("a : b", Some(ParsedLine::KVPair("a", "b"))),
      ("a b", Some(ParsedLine::KVPair("a", "b"))),
      (" a = b", Some(ParsedLine::KVPair("a", "b"))),
      (" a : b", Some(ParsedLine::KVPair("a", "b"))),
      (" a b", Some(ParsedLine::KVPair("a", "b"))),
      ("a:=b", Some(ParsedLine::KVPair("a", "=b"))),
      ("a=:b", Some(ParsedLine::KVPair("a", ":b"))),
      ("a b:c", Some(ParsedLine::KVPair("a", "b:c"))),
      ("a\\ \\:\\=b c", Some(ParsedLine::KVPair("a\\ \\:\\=b", "c"))),
      ("a\\ \\:\\=b=c", Some(ParsedLine::KVPair("a\\ \\:\\=b", "c"))),
      ("a\\\\ \\\\:\\\\=b c", Some(ParsedLine::KVPair("a\\\\", "\\\\:\\\\=b c"))),
      ("\\  b", Some(ParsedLine::KVPair("\\ ", "b"))),
      ("=", Some(ParsedLine::KVPair("", ""))),
      ("\u{1F41E}=\u{1F41E}", Some(ParsedLine::KVPair("\u{1F41E}", "\u{1F41E}"))),
    ];
    let splitter = super::LineParser::new();
    for &(line, ref expected) in data.iter() {
      let actual = splitter.parse_line(line);
      if expected != &actual {
        panic!("Failed when splitting {:?}.  Expected {:?} but got {:?}", line, expected, actual);
      }
    }
  }
}
<|endoftext|>


<commit_before>//! # The labyrinth library
//!
//! The labyrinth library provides tools for building and managing labyrinths.
//!
//! # Example
//!
//! ```
//! extern crate labyrinth;
//!
//! use labyrinth::{Wall, Direction};
//! fn main() {
//!     let mut wall = Wall::new(5, 5);
//!     wall.carve();
//!     wall.print();
//!     wall.open(0, 10, Direction::Up);
//!     assert_eq!(true, wall.is_open(0, 10, Direction::Up));
//!     wall.close(0, 10, Direction::Up);
//!     assert_eq!(false, wall.is_open(0, 10, Direction::Up));
//! }
//! ```

extern crate rand;

pub mod vector2d;
pub mod wall;
pub mod direction;
pub mod room;

pub type Int = i32;
pub type Uint = u32;

pub use wall::Wall;
pub use direction::Direction;
<commit_msg>fix doc test in lib<commit_after>//! # The labyrinth library
//!
//! The labyrinth library provides tools for building and managing labyrinths.
//!
//! # Example
//!
//! ```
//! extern crate labyrinth;
//!
//! use labyrinth::{Wall, Direction};
//! fn main() {
//!     let mut wall = Wall::new(20, 20);
//!     wall.carve();
//!     wall.print();
//!     wall.open(0, 10, Direction::Up);
//!     assert_eq!(true, wall.is_open(0, 10, Direction::Up));
//!     wall.close(0, 10, Direction::Up);
//!     assert_eq!(false, wall.is_open(0, 10, Direction::Up));
//! }
//! ```

extern crate rand;

pub mod vector2d;
pub mod wall;
pub mod direction;
pub mod room;

pub type Int = i32;
pub type Uint = u32;

pub use wall::Wall;
pub use direction::Direction;
<|endoftext|>


<commit_before>
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Cursor;
use std::io::Read;

pub struct Tty {
    pub stdout: String,
    pub stderr: String,
}

// https://docs.docker.com/engine/api/v1.26/#operation/ContainerAttach
impl Tty {
    pub fn new(mut stream: Box<Read>) -> Tty {
        let mut stdout: Vec<u8> = vec![];
        let mut stderr: Vec<u8> = vec![];
        loop {
            // 8 byte header [ STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4 ]
            let mut header = [0; 8];
            match stream.read_exact(&mut header) {
                Ok(_) => {
                    let payload_size: Vec<u8> = header[4..8].to_vec();
                    let mut buffer = vec![
                        0;
                        Cursor::new(&payload_size)
                            .read_u32::<BigEndian>()
                            .unwrap() as
                            usize
                    ];
                    match stream.read_exact(&mut buffer) {
                        Ok(_) => {
                            match header[0] {
                                // stdin, unhandled
                                0 => break,
                                // stdout
                                1 => stdout.append(&mut buffer),
                                // stderr
                                2 => stderr.append(&mut buffer),
                                //unhandled
                                _ => break,
                            }
                        }
                        Err(_) => break,
                    };
                }
                Err(_) => break,
            }
        }
        Tty {
            stdout: String::from_utf8_lossy(&stdout).to_string(),
            stderr: String::from_utf8_lossy(&stderr).to_string(),
        }
    }
}
<commit_msg>Add InterlacedTty<commit_after>
use byteorder::{BigEndian, ReadBytesExt};
use std::io::Cursor;
use std::io::Read;

#[derive(Debug)]
pub enum TtyLine {
    StdOut(String),
    StdErr(String),
}

pub struct Tty {
    pub stdout: String,
    pub stderr: String,
}

// https://docs.docker.com/engine/api/v1.26/#operation/ContainerAttach
impl Tty {
    pub fn new(stream: impl Read) -> Tty {
        let mut stdout: Vec<String> = vec![];
        let mut stderr: Vec<String> = vec![];

        let lines = demux(stream);
        for line in lines {
            match line {
                TtyLine::StdOut(s) => stdout.push(s),
                TtyLine::StdErr(s) => stderr.push(s),
            }
        }

        Tty {
            stdout: stdout.concat(),
            stderr: stderr.concat(),
        }
    }
}

/// Used to demux the output of Docker log, but still keep lines from stdout and stderr interlaced
/// in the right order.
pub struct InterlacedTty {
    pub lines: Vec<TtyLine>,
}

// https://docs.docker.com/engine/api/v1.26/#operation/ContainerAttach
impl InterlacedTty {
    pub fn new(stream: impl Read) -> InterlacedTty {
        let lines = demux(stream);

        InterlacedTty { lines }
    }
}

fn demux(mut stream: impl Read) -> Vec<TtyLine> {
    let mut lines: Vec<TtyLine> = vec![];
    loop {
        // 8 byte header [ STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4 ]
        let mut header = [0; 8];
        match stream.read_exact(&mut header) {
            Ok(_) => {
                let payload_size: Vec<u8> = header[4..8].to_vec();
                let mut buffer = vec![
                    0;
                    Cursor::new(&payload_size).read_u32::<BigEndian>().unwrap()
                        as usize
                ];
                match stream.read_exact(&mut buffer) {
                    Ok(_) => {
                        match header[0] {
                            // stdin, unhandled
                            0 => break,
                            // stdout
                            1 => lines.push(TtyLine::StdOut(
                                String::from_utf8_lossy(&buffer).trim().to_string(),
                            )),
                            // stderr
                            2 => lines.push(TtyLine::StdErr(
                                String::from_utf8_lossy(&buffer).trim().to_string(),
                            )),
                            //unhandled
                            _ => break,
                        }
                    }
                    Err(_) => break,
                };
            }
            Err(_) => break,
        }
    }
    lines
}
<|endoftext|>


<commit_before>/*!
  RFC 3986 Compliant URL parsing

  This is a port of Go's Url library.
      -http://golang.org/src/pkg/net/url/url.go

  From http://en.wikipedia.org/wiki/URI_scheme#Examples:

     foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose
     \_/   \_______________/ \_________/ \__/            \___/ \_/ \______________________/ \__/
      |           |               |       |                |    |            |                |
      |       userinfo         hostname  port              |    |          query          fragment
      |    \________________________________/\_____________|____|/ \__/        \__/
      |                    |                          |    |    |    |          |
      |                    |                          |    |    |    |          |
   scheme              authority                    path   |    |    interpretable as keys
    name   \_______________________________________________|____|/       \____/     \_____/
      |                         |                          |    |          |           |
      |                 hierarchical part                  |    |    interpretable as values
      |                                                    |    |
      |            path               interpretable as filename |
      |   ___________|____________                              |
     / \ /                        \                             |
     urn:example:animal:ferret:nose               interpretable as extension

*/

use std::fmt;

/// Error codes for Url parsing.
pub enum ErrorCode {
    /// Invalid url escape.
    EscapeError
}

/// Converts the enum error codes to human readable strings.
pub fn error_str(error: ErrorCode) -> &'static str {
    return match error {
        EscapeError => "Escape error.",
    }
}

impl fmt::Show for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        error_str(*self).fmt(f)
    }
}

/// Error reports an error and the operation and URL that caused it.
pub struct Error<'a> {
    op:  &'a str,
    url: &'a str,
    error_code: ErrorCode
}

impl<'a> Error<'a> {
    fn error(&'a self) -> String {
        return self.op.to_string()
            .append(" ")
            .append(self.url)
            .append(": ")
            .append(error_str(self.error_code));
    }
}

/// Encoding is used to determine if a reserved error can appear unescaped.
pub enum Encoding {
    /// Paths
    EncodePath,
    /// Passwords
    EncodeUserPassword,
    /// Query Components
    EncodeQueryComponent,
    /// Fragments
    EncodeFragment
}

/// Return true if specified character should be escaped when
/// appearing in a URL string, according to RFC 3986/
/// When 'all' is true the full range of reserved characters are matched.
/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn should_escape(c: u8, mode: Encoding) -> bool {
    // Unreserved characters (alphanum)
    return match c {
        d if '0' as u8 <= d && d <= '9' as u8 ||
             'a' as u8 <= d && d <= 'z' as u8 ||
             'A' as u8 <= d && d <= 'Z' as u8  => false,

        d if d == '-' as u8 ||
             d == '_' as u8 ||
             d == '.' as u8 ||
             d == '~' as u8                    => false,

        d if bytes!("$&+,/:;=?@").contains(&d) => match mode {
            EncodePath           => c == '?' as u8,

            EncodeUserPassword   => c == '@' as u8 ||
                                    c == '/' as u8 ||
                                    c == ':' as u8,

            EncodeQueryComponent => true,

            EncodeFragment       => false
        },

        _                                      => true
    }
}

/// Checks if a u8 is a hex representable.
/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn ishex(c: u8) -> bool {
    return match c {
        d if '0' as u8 <= d && d <= '9' as u8 => true,
        d if 'a' as u8 <= d && d <= 'f' as u8 => true,
        d if 'A' as u8 <= d && d <= 'F' as u8 => true,
        _                                     => false
    }
}

/// lolwut
/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn unhex(c: u8) -> u8 {
    return match c {
       d if '0' as u8 <= d && d <= '9' as u8 => c - '0' as u8,
       d if 'a' as u8 <= d && d <= 'f' as u8 => c - 'a' as u8 + 10,
       d if 'A' as u8 <= d && d <= 'F' as u8 => c - 'A' as u8 + 10,
       _                                     => 0
    }
}

#[cfg(test)]
mod tests {
    use super::{ishex, should_escape, Encoding, EncodePath};

    #[test]
    fn test_ishhex() {
        let should_pass = bytes!("059adfADF");
        for i in should_pass.iter() {
            assert_eq!(ishex(*i), true);
        }

        let should_fail = bytes!("zq';ä");
        for i in should_fail.iter() {
            assert_eq!(ishex(*i), false);
        }
    }
}
<commit_msg>unescape is partially complete<commit_after>/*!
  RFC 3986 Compliant URL parsing

  This is a port of Go's Url library.
    -http://golang.org/src/pkg/net/url/url.go

  From http://en.wikipedia.org/wiki/URI_scheme#Examples:

  foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose
  \_/   \_______________/ \_________/ \__/            \___/ \_/ \______________________/ \__/
  |           |               |       |                |    |            |                |
  |       userinfo         hostname  port              |    |          query          fragment
  |    \________________________________/\_____________|____|/ \__/        \__/
  |                    |                          |    |    |    |          |
  |                    |                          |    |    |    |          |
  scheme              authority                    path   |    |    interpretable as keys
  name   \_______________________________________________|____|/       \____/     \_____/
  |                         |                          |    |          |           |
  |                 hierarchical part                  |    |    interpretable as values
  |                                                    |    |
  |            path               interpretable as filename |
  |   ___________|____________                              |
  / \ /                        \                             |
  urn:example:animal:ferret:nose               interpretable as extension

*/

use std::fmt;
use std::str;

/// Error codes for Url parsing.
pub enum ErrorCode {
    /// Invalid url escape.
    /// TODO: impl
    /// func (e EscapeError) Error() string {
    ///     return "invalid URL escape " + strconv.Quote(string(e))
    /// }
    EscapeError
}

/// Converts the enum error codes to human readable strings.
pub fn error_str(error: ErrorCode) -> &'static str {
    return match error {
        EscapeError => "Escape error.",
    }
}

impl fmt::Show for ErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        error_str(*self).fmt(f)
    }
}

/// Error reports an error and the operation and URL that caused it.
pub struct Error<'a> {
    op:  &'a str,
    url: &'a str,
    error_code: ErrorCode
}

impl<'a> Error<'a> {
    fn error(&'a self) -> String {
        return self.op.to_string()
            .append(" ")
            .append(self.url)
            .append(": ")
            .append(error_str(self.error_code));
    }
}

/// Encoding is used to determine if a reserved error can appear unescaped.
#[deriving(PartialEq)]
pub enum Encoding {
    /// Paths
    EncodePath,
    /// Passwords
    EncodeUserPassword,
    /// Query Components
    EncodeQueryComponent,
    /// Fragments
    EncodeFragment
}

/// Return true if specified character should be escaped when
/// appearing in a URL string, according to RFC 3986/
/// When 'all' is true the full range of reserved characters are matched.
/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn should_escape(c: u8, mode: Encoding) -> bool {
    // Unreserved characters (alphanum)
    return match c {
        d if '0' as u8 <= d && d <= '9' as u8 ||
             'a' as u8 <= d && d <= 'z' as u8 ||
             'A' as u8 <= d && d <= 'Z' as u8  => false,

            d if bytes!("-_.~").contains(&d)   => false,

            d if bytes!("$&+,/:;=?@").contains(&d) => match mode {
                EncodePath           => c == '?' as u8,

                EncodeUserPassword   => c == '@' as u8 ||
                                        c == '/' as u8 ||
                                        c == ':' as u8,

                EncodeQueryComponent => true,

                EncodeFragment       => false
            },

            _                                    => true
    }
}

/// Checks if a u8 is a hex representable.
/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn ishex(c: u8) -> bool {
    return match c {
        d if '0' as u8 <= d && d <= '9' as u8 => true,
        d if 'a' as u8 <= d && d <= 'f' as u8 => true,
        d if 'A' as u8 <= d && d <= 'F' as u8 => true,
        _                                     => false
    }
}

/// TODO: Rewrite this function with b'0'... when the feature arrives.
pub fn unhex(c: u8) -> u8 {
    return match c {
        d if '0' as u8 <= d && d <= '9' as u8 => c - '0' as u8,
        d if 'a' as u8 <= d && d <= 'f' as u8 => c - 'a' as u8 + 10,
        d if 'A' as u8 <= d && d <= 'F' as u8 => c - 'A' as u8 + 10,
        _                                     => 0
    }
}

/// unescape unescapes a string, the mode specifics
/// which section of the URL string is being unescaped
/// TODO: Rewrite this function with b'0'... when the feature arrives.
/// TODO: Refactor this beast.
/// TODO: Ask the IRC is from_utf(s.into_bytes()).unwrap() will never fail.
pub fn unescape(s: String, mode: Encoding) -> Result<String, String> {
    let mut n = 0;
    let mut has_plus = false;
    let mut i = 0;
    let s_slice = s.as_slice().clone();
    let s_len = s_slice.len();
    while i < s_len {
        match s_slice[i] {
            // '%' as u8
            37 => {
                n += 1;
                if i + 2 >= s_len || !ishex(s_slice[i + 1]) || !ishex(s_slice[i + 2]) {
                    let output = match s_len {
                        q if q - i > 3 => s_slice.slice(i, i + 3),
                        _              => s_slice.slice(i, s_len)
                    };
                    return Err(error_str(EscapeError)
                               .to_string()
                               .append(output));
                }
                i += 3;
            },

            // '+' as u8
               43 => {
                   if mode == EncodeQueryComponent {
                       has_plus = true;
                   }
                   i += 1;
               },

               _  => {
                   i += 1;
               }
        }
    }

    if n == 0 && !has_plus {
        return Ok(s_slice.to_string());
    }

    return Ok(s_slice.to_string());
}

#[cfg(test)]
mod tests {
    use super::{ishex, should_escape, EncodePath, EncodeUserPassword,
    EncodeQueryComponent};

    #[test]
    fn test_ishhex() {
        let should_pass = bytes!("059adfADF");
        for i in should_pass.iter() {
            assert_eq!(ishex(*i), true);
        }

        let should_fail = bytes!("zq';ä");
        for i in should_fail.iter() {
            assert_eq!(ishex(*i), false);
        }
    }

    #[test]
    fn test_should_escape() {
        assert_eq!(should_escape('z' as u8, EncodePath),           false);
        assert_eq!(should_escape('-' as u8, EncodePath),           false);
        assert_eq!(should_escape('?' as u8, EncodePath),           true);
        assert_eq!(should_escape('?' as u8, EncodeUserPassword),   false);
        assert_eq!(should_escape('?' as u8, EncodeQueryComponent), true);
    }

    #[test]
    fn test_unescaped() {
        assert!(false);
    }
}
<|endoftext|>


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

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use app_units::Au;
use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::{kCTFontDefaultOrientation};
use font::FontTableTag;
use font::FractionalPixel;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use std::ptr;
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;

pub struct FontTable {
    data: CFData,
}

// assumes 72 points per inch, and 96 px per inch
fn px_to_pt(px: f64) -> f64 {
    px / 96. * 72.
}

// assumes 72 points per inch, and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}

fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn with_buffer<F>(&self, blk: F) where F: FnOnce(*const u8, usize) {
        blk(self.data.bytes().as_ptr(), self.data.len() as usize);
    }
}

#[derive(Debug)]
pub struct FontHandle {
    pub font_data: Arc<FontTemplateData>,
    pub ctfont: CTFont,
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont(size) {
            Some(ref ctfont) => {
                Ok(FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                })
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_weight::T::Weight100,
            v if v < 2.0 => font_weight::T::Weight200,
            v if v < 3.0 => font_weight::T::Weight300,
            v if v < 4.0 => font_weight::T::Weight400,
            v if v < 5.0 => font_weight::T::Weight500,
            v if v < 6.0 => font_weight::T::Weight600,
            v if v < 7.0 => font_weight::T::Weight700,
            v if v < 8.0 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId)
                        -> FractionalPixel {
        // TODO: Implement on mac
        0.0
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(Au::from_f64_px)
                                  .unwrap_or(max_advance_width);

        let metrics =  FontMetrics {
            underline_size:   au_from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          au_from_pt(leading),
            x_height:         au_from_pt(self.ctfont.x_height() as f64),
            em_size:          em_size,
            ascent:           au_from_pt(ascent * scale),
            descent:          au_from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<Box<FontTable>> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(box FontTable::wrap(data))
        })
    }
}
<commit_msg>gfx: Change the mapping from Mac weights to CSS weights so that 0.0 maps to 400, not 500.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use app_units::Au;
use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::{kCTFontDefaultOrientation};
use font::FontTableTag;
use font::FractionalPixel;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use std::ptr;
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;

pub struct FontTable {
    data: CFData,
}

// assumes 72 points per inch, and 96 px per inch
fn px_to_pt(px: f64) -> f64 {
    px / 96. * 72.
}

// assumes 72 points per inch, and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}

fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn with_buffer<F>(&self, blk: F) where F: FnOnce(*const u8, usize) {
        blk(self.data.bytes().as_ptr(), self.data.len() as usize);
    }
}

#[derive(Debug)]
pub struct FontHandle {
    pub font_data: Arc<FontTemplateData>,
    pub ctfont: CTFont,
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont(size) {
            Some(ref ctfont) => {
                Ok(FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                })
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = if normalized <= 0.0 {
            4.0 + normalized * 3.0  // [1.0, 4.0]
        } else {
            4.0 + normalized * 5.0  // [4.0, 9.0]
        }; // [1.0, 9.0], centered on 4.0
        match normalized.round() as u32 {
            1 => font_weight::T::Weight100,
            2 => font_weight::T::Weight200,
            3 => font_weight::T::Weight300,
            4 => font_weight::T::Weight400,
            5 => font_weight::T::Weight500,
            6 => font_weight::T::Weight600,
            7 => font_weight::T::Weight700,
            8 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId)
                        -> FractionalPixel {
        // TODO: Implement on mac
        0.0
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(Au::from_f64_px)
                                  .unwrap_or(max_advance_width);

        let metrics =  FontMetrics {
            underline_size:   au_from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          au_from_pt(leading),
            x_height:         au_from_pt(self.ctfont.x_height() as f64),
            em_size:          em_size,
            ascent:           au_from_pt(ascent * scale),
            descent:          au_from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<Box<FontTable>> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(box FontTable::wrap(data))
        })
    }
}
<|endoftext|>