<commit_before>#![allow(dead_code)]

pub static NAME: &'static str = "BIRL Interpreter";
pub static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub static GREETING: &'static str = "Aqui nóis constrói fibra.";

type Identifier = String;

use std::collections::HashMap;
pub struct Structure {
    fields: HashMap<Identifier, Value>,
}
impl Structure {
    pub fn size_of(&self) -> usize {
        // Add together all the entry sizes
        self.fields.values().fold(0, |size, entry| size + entry.size_of())
    }
}

pub enum Type{
    Array, Str, Char, Struct, Void,
    Unsigned8, Unsigned16, Unsigned32, Unsigned64, UnsignedSize,
      Signed8,   Signed16,   Signed32,   Signed64,   SignedSize,
    Float32, Float64
}
impl Type{

}

/// Possible language types, and their correspondants in Rust
pub enum Value {
    /// Stores a collections of other elements
    Array(Vec<Value>),
    /// Owned string
    Str(String),
    /// Character
    Char(char),
    /// Structure
    Struct(Structure),
    /// A type containing no value
    Void,

    // Integer types
    Unsigned8(u8), Unsigned16(u16), Unsigned32(u32), Unsigned64(u64), UnsignedSize(usize),
      Signed8(i8), Signed16(i16),   Signed32(i32),   Signed64(i64),   SignedSize(isize),

    // Floats
    Float32(f32), Float64(f64),
}
impl Value {
    pub fn get_type(&self) -> Type{
        match self{
            &Value::Array(_) => Type::Array,
            &Value::Str(_) => Type::Str,
            &Value::Char(_) => Type::Char,
            &Value::Struct(_) => Type::Struct,

            &Value::Void => Type::Void,

            // Both signed and unsigned types have the same size in bytes
            &Value::Unsigned8(_)    => Type::Unsigned8,    &Value::Signed8(_)    => Type::Signed8,
            &Value::Unsigned16(_)   => Type::Unsigned16,   &Value::Signed16(_)   => Type::Signed16,
            &Value::Unsigned32(_)   => Type::Unsigned32,   &Value::Signed32(_)   => Type::Signed32,
            &Value::Unsigned64(_)   => Type::Unsigned64,   &Value::Signed64(_)   => Type::Signed64,
            &Value::UnsignedSize(_) => Type::UnsignedSize, &Value::SignedSize(_) => Type::SignedSize,

            &Value::Float32(_) => Type::Float32,
            &Value::Float64(_) => Type::Float64,
        }
    }

    pub fn size_of(&self) -> usize {
        use std::mem::size_of as len;
        match self {
            &Value::Array(ref array) => array.iter().fold(0, |size, entry| size + entry.size_of()),
            &Value::Str(ref string) => string.len(),
            &Value::Char(ref c) => c.len_utf8(),
            &Value::Struct(ref structure) => structure.size_of(),

            // Internally, voids have no size
            &Value::Void => 0,

            // Both signed and unsigned types have the same size in bytes
            &Value::Unsigned8(_) | &Value::Signed8(_) => len::<u8>(),
            &Value::Unsigned16(_) | &Value::Signed16(_) => len::<u16>(),
            &Value::Unsigned32(_) | &Value::Signed32(_) => len::<u32>(),
            &Value::Unsigned64(_) | &Value::Signed64(_) => len::<u64>(),
            &Value::UnsignedSize(_) | &Value::SignedSize(_) => len::<usize>(),

            &Value::Float32(_) => len::<f32>(),
            &Value::Float64(_) => len::<f64>(),
        }
    }
}

pub struct Function {
    name: String,
}
impl Function {
    pub fn call() -> Type {
        Type::Void
    }
}
<commit_msg>Inicial VM implementation<commit_after>#![allow(dead_code)]

pub static NAME: &'static str = "BIRL Interpreter";
pub static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub static GREETING: &'static str = "Aqui nóis constrói fibra.";

type Identifier = String;

use std::collections::HashMap;
#[derive(Clone, PartialEq, Debug)]
pub struct Structure {
    fields: HashMap<Identifier, Value>,
}
impl Structure {
    pub fn new() -> Structure{
        Structure{ fields: HashMap::new() }
    }

    pub fn size_of(&self) -> usize {
        // Add together all the entry sizes
        self.fields.values().fold(0, |size, entry| size + entry.size_of())
    }
}

#[derive(Clone, PartialEq, Debug)]
pub struct Function{ function: Box<i8> }
impl Function {
    pub fn call() -> Type {
        Type::Void
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Type{
    Array, Str, Char, Struct, Void,
    Unsigned8, Unsigned16, Unsigned32, Unsigned64, UnsignedSize,
      Signed8,   Signed16,   Signed32,   Signed64,   SignedSize,
    Float32, Float64
}
impl Type{
    pub fn default(self) -> Value{
        match self{
            Type::Array  => Value::Array(Vec::new()),
            Type::Str    => Value::Str(String::new()),
            Type::Char   => Value::Char(' '),
            Type::Struct => Value::Struct(Structure::new()),
            Type::Void   => Value::Void,

            Type::Unsigned8    => Value::Unsigned8(0),    Type::Signed8    => Value::Signed8(0),
            Type::Unsigned16   => Value::Unsigned16(0),   Type::Signed16   => Value::Signed16(0),
            Type::Unsigned32   => Value::Unsigned32(0),   Type::Signed32   => Value::Signed32(0),
            Type::Unsigned64   => Value::Unsigned64(0),   Type::Signed64   => Value::Signed64(0),
            Type::UnsignedSize => Value::UnsignedSize(0), Type::SignedSize => Value::SignedSize(0),

            Type::Float32 => Value::Float32(0.0),
            Type::Float64 => Value::Float64(0.0),
        }
    }
}

/// Possible language types, and their correspondants in Rust
#[derive(Clone, PartialEq, Debug)]
pub enum Value {
    /// Stores a collections of other elements
    Array(Vec<Value>),
    /// Owned string
    Str(String),
    /// Character
    Char(char),
    /// Structure
    Struct(Structure),
    /// A type containing no value
    Void,

    // Integer types
    Unsigned8(u8), Unsigned16(u16), Unsigned32(u32), Unsigned64(u64), UnsignedSize(usize),
      Signed8(i8), Signed16(i16),   Signed32(i32),   Signed64(i64),   SignedSize(isize),

    // Floats
    Float32(f32), Float64(f64),
}
impl Value {
    pub fn get_type(&self) -> Type{
        match self{
            &Value::Array(_) => Type::Array,
            &Value::Str(_) => Type::Str,
            &Value::Char(_) => Type::Char,
            &Value::Struct(_) => Type::Struct,

            &Value::Void => Type::Void,

            // Both signed and unsigned types have the same size in bytes
            &Value::Unsigned8(_)    => Type::Unsigned8,    &Value::Signed8(_)    => Type::Signed8,
            &Value::Unsigned16(_)   => Type::Unsigned16,   &Value::Signed16(_)   => Type::Signed16,
            &Value::Unsigned32(_)   => Type::Unsigned32,   &Value::Signed32(_)   => Type::Signed32,
            &Value::Unsigned64(_)   => Type::Unsigned64,   &Value::Signed64(_)   => Type::Signed64,
            &Value::UnsignedSize(_) => Type::UnsignedSize, &Value::SignedSize(_) => Type::SignedSize,

            &Value::Float32(_) => Type::Float32,
            &Value::Float64(_) => Type::Float64,
        }
    }

    pub fn size_of(&self) -> usize {
        use std::mem::size_of as len;
        match self {
            &Value::Array(ref array) => array.iter().fold(0, |size, entry| size + entry.size_of()),
            &Value::Str(ref string) => string.len(),
            &Value::Char(ref c) => c.len_utf8(),
            &Value::Struct(ref structure) => structure.size_of(),

            // Internally, voids have no size
            &Value::Void => 0,

            // Both signed and unsigned types have the same size in bytes
            &Value::Unsigned8(_) | &Value::Signed8(_) => len::<u8>(),
            &Value::Unsigned16(_) | &Value::Signed16(_) => len::<u16>(),
            &Value::Unsigned32(_) | &Value::Signed32(_) => len::<u32>(),
            &Value::Unsigned64(_) | &Value::Signed64(_) => len::<u64>(),
            &Value::UnsignedSize(_) | &Value::SignedSize(_) => len::<usize>(),

            &Value::Float32(_) => len::<f32>(),
            &Value::Float64(_) => len::<f64>(),
        }
    }

    pub fn try_cast(self, target: Type) -> Result<Value, Value>{
        macro_rules! cast_number{
            ($num:ident) => {
                match target{
                    // Cast to other number types
                    Type::Unsigned8 => Ok(Value::Unsigned8($num as u8)), Type::Signed8 => Ok(Value::Signed8($num as i8)),
                    Type::Unsigned16 => Ok(Value::Unsigned16($num as u16)), Type::Signed16 => Ok(Value::Signed16($num as i16)),
                    Type::Unsigned32 => Ok(Value::Unsigned32($num as u32)), Type::Signed32 => Ok(Value::Signed32($num as i32)),
                    Type::Unsigned64 => Ok(Value::Unsigned64($num as u64)), Type::Signed64 => Ok(Value::Signed64($num as i64)),
                    Type::UnsignedSize => Ok(Value::UnsignedSize($num as usize)), Type::SignedSize => Ok(Value::SignedSize($num as isize)),

                    Type::Float32 => Ok(Value::Float32($num as f32)), Type::Float64 => Ok(Value::Float64($num as f64)),

                    // Format onto a String
                    Type::Str => Ok(Value::Str(format!("{}", $num))),

                    // No other valid conversions
                    _ => { Err(self) }
                }
            }
        }

        if let Type::Void = target {
            Ok(Value::Void)
        }else{
            match self{
                Value::Char(c) => if let Type::Str = target { Ok(Value::Str(c.escape_unicode().collect())) } else { Err(self) },

                // Implement scalar casting for all number types
                Value::Unsigned8(number)  => cast_number!(number), Value::Signed8(number) => cast_number!(number),
                Value::Unsigned16(number) => cast_number!(number), Value::Signed16(number) => cast_number!(number),
                Value::Unsigned32(number) => cast_number!(number), Value::Signed32(number) => cast_number!(number),
                Value::Unsigned64(number) => cast_number!(number), Value::Signed64(number) => cast_number!(number),
                Value::UnsignedSize(number) => cast_number!(number), Value::SignedSize(number) => cast_number!(number),
                Value::Float32(number) => cast_number!(number), Value::Float64(number) => cast_number!(number),

                _ => { Err(self) }
            }
        }
    }
}

#[test]
fn value(){
    // Casting
    let value = Value::Signed64(-159357789456123);

    // Stingifying
    assert_eq!(value.clone().try_cast(Type::Str).unwrap(), Value::Str("-159357789456123".to_owned()));

    // Casting between signed and unsigned
    assert_eq!(value.clone().try_cast(Type::Unsigned64).unwrap(), Value::Unsigned64(18446584715920095493));
}

#[derive(Clone, PartialEq, Debug)]
pub struct Context{
    variables: HashMap<Identifier, Value>,
    functions: HashMap<Identifier, Function>,

    stack: Vec<Context>
}
impl Context{
    /// Saves the current state of the context
    pub fn push(&mut self){
        let copy = self.clone();
        self.stack.push(copy);
    }

    /// Revers to a previously saved context discarding all changes
    pub fn pop(&mut self){
        let previous = self.stack.pop().expect("Tried to pop Context with empty stack");
        *self = previous
    }

    /// Reverts to a previously saved context retaining the changes made to values present in it
    pub fn merge_pop(&mut self){
        let mut previous = self.stack.pop().expect("Tried to pop Context with empty stack");

        for (key, value) in previous.variables.iter_mut(){
            // Apply changes made to variables present on the previous stack
            if let Some(new) = self.variables.remove(key){ *value = new }
        }
        for (key, value) in previous.functions.iter_mut(){
            // Apply changes made to functions present on the previous stack
            if let Some(new) = self.functions.remove(key){ *value = new }
        }

        *self = previous
    }
}

use std::rc::Rc;
use std::cell::RefCell;
pub struct VirtualMachine{
    memory: Vec<Rc<RefCell<Value>>>
}
impl VirtualMachine{
    pub fn new() -> VirtualMachine{
        VirtualMachine{ memory: Vec::new() }
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Card {
    pub suit: Suit,
    pub rank: Rank
}

impl Card {
    pub fn new(suit: Suit, rank: Rank) -> Card {
        Card { suit: suit, rank: rank }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Suit {
    Club,
    Diamond,
    Heart,
    Spade
}

impl Suit {
    pub fn from_number(num: &u32) -> Option<Suit> {
        match *num {
            1 => Some(Suit::Heart),
            2 => Some(Suit::Diamond),
            3 => Some(Suit::Spade),
            4 => Some(Suit::Club),
            _ => None
        }
    }
}

impl Into<u32> for Suit {
    fn into(self) -> u32 {
        match self {
            Suit::Heart => 1,
            Suit::Diamond => 2,
            Suit::Spade => 3,
            Suit::Club => 4
        }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Rank {
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King
}

impl Rank {
    pub fn from_number(num: &u32) -> Option<Rank> {
        match *num {
            1 => Some(Rank::Ace),
            2 => Some(Rank::Two),
            3 => Some(Rank::Three),
            4 => Some(Rank::Four),
            5 => Some(Rank::Five),
            6 => Some(Rank::Six),
            7 => Some(Rank::Seven),
            8 => Some(Rank::Eight),
            9 => Some(Rank::Nine),
            10 => Some(Rank::Ten),
            11 => Some(Rank::Jack),
            12 => Some(Rank::Queen),
            13 => Some(Rank::King),
            _ => None
        }
    }
}

impl Into<u32> for Rank {
    fn into(self) -> u32 {
        match self {
            Rank::Ace => 1,
            Rank::Two => 2,
            Rank::Three => 3,
            Rank::Four => 4,
            Rank::Five => 5,
            Rank::Six => 6,
            Rank::Seven => 7,
            Rank::Eight => 8,
            Rank::Nine => 9,
            Rank::Ten => 10,
            Rank::Jack => 11,
            Rank::Queen => 12,
            Rank::King => 13
        }
    }
}

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

    #[test]
    fn reversible_suit() {
        assert_eq!(Some(Suit::Heart), Suit::from_number(&Suit::Heart.into()));
    }

    #[test]
    fn reversible_rank() {
        assert_eq!(Some(Rank::Ace), Rank::from_number(&Rank::Ace.into()));
    }
}
<commit_msg>remove whitespace<commit_after>#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Card {
    pub suit: Suit,
    pub rank: Rank
}

impl Card {
    pub fn new(suit: Suit, rank: Rank) -> Card {
        Card { suit: suit, rank: rank }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Suit {
    Club,
    Diamond,
    Heart,
    Spade
}

impl Suit {
    pub fn from_number(num: &u32) -> Option<Suit> {
        match *num {
            1 => Some(Suit::Heart),
            2 => Some(Suit::Diamond),
            3 => Some(Suit::Spade),
            4 => Some(Suit::Club),
            _ => None
        }
    }
}

impl Into<u32> for Suit {
    fn into(self) -> u32 {
        match self {
            Suit::Heart => 1,
            Suit::Diamond => 2,
            Suit::Spade => 3,
            Suit::Club => 4
        }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Rank {
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King
}

impl Rank {
    pub fn from_number(num: &u32) -> Option<Rank> {
        match *num {
            1 => Some(Rank::Ace),
            2 => Some(Rank::Two),
            3 => Some(Rank::Three),
            4 => Some(Rank::Four),
            5 => Some(Rank::Five),
            6 => Some(Rank::Six),
            7 => Some(Rank::Seven),
            8 => Some(Rank::Eight),
            9 => Some(Rank::Nine),
            10 => Some(Rank::Ten),
            11 => Some(Rank::Jack),
            12 => Some(Rank::Queen),
            13 => Some(Rank::King),
            _ => None
        }
    }
}

impl Into<u32> for Rank {
    fn into(self) -> u32 {
        match self {
            Rank::Ace => 1,
            Rank::Two => 2,
            Rank::Three => 3,
            Rank::Four => 4,
            Rank::Five => 5,
            Rank::Six => 6,
            Rank::Seven => 7,
            Rank::Eight => 8,
            Rank::Nine => 9,
            Rank::Ten => 10,
            Rank::Jack => 11,
            Rank::Queen => 12,
            Rank::King => 13
        }
    }
}

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

    #[test]
    fn reversible_suit() {
        assert_eq!(Some(Suit::Heart), Suit::from_number(&Suit::Heart.into()));
    }

    #[test]
    fn reversible_rank() {
        assert_eq!(Some(Rank::Ace), Rank::from_number(&Rank::Ace.into()));
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map::Entry as HashMapEntry;
use std::sync::{Arc, RwLock};
use std::ops::{Deref, DerefMut};
use std::vec::Vec;

use data::{Entry, Hint, SequenceNumber};
use log::{Log, LogWriter};

#[derive(Debug)]
struct IndexEntry {
    file_id: u32,
    entry_pos: u64,
    entry_size: u64,
    sequence: SequenceNumber,
}

struct Index(HashMap<Vec<u8>, IndexEntry>);

impl Index {
    fn new() -> Index {
        Index(HashMap::new())
    }

    fn update(&mut self, hint: Hint, file_id: u32) {
        match self.entry(hint.key.to_vec()) {
            HashMapEntry::Occupied(mut o) => {
                if o.get().sequence <= hint.sequence {
                    if hint.deleted {
                        o.remove();
                    } else {
                        o.insert(IndexEntry {
                            file_id: file_id,
                            entry_pos: hint.entry_pos,
                            entry_size: hint.entry_size(),
                            sequence: hint.sequence,
                        });
                    }
                }
            }
            HashMapEntry::Vacant(e) => {
                if !hint.deleted {
                    e.insert(IndexEntry {
                        file_id: file_id,
                        entry_pos: hint.entry_pos,
                        entry_size: hint.entry_size(),
                        sequence: hint.sequence,
                    });
                }
            }
        }
    }
}

impl Deref for Index {
    type Target = HashMap<Vec<u8>, IndexEntry>;
    fn deref(&self) -> &HashMap<Vec<u8>, IndexEntry> {
        &self.0
    }
}

impl DerefMut for Index {
    fn deref_mut(&mut self) -> &mut HashMap<Vec<u8>, IndexEntry> {
        &mut self.0
    }
}

struct CaskInner {
    current_sequence: SequenceNumber,
    index: Index,
    log: Log,
}

impl CaskInner {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        self.index.get(key).and_then(|index_entry| {
            let entry = self.log.read_entry(index_entry.file_id, index_entry.entry_pos);
            if entry.deleted {
                warn!("Index pointed to dead entry: Entry {{ key: {:?}, sequence: {} }} at file: \
                       {}",
                      entry.key,
                      entry.sequence,
                      index_entry.file_id);
                None
            } else {
                Some(entry.value.into_owned())
            }
        })
    }

    fn put(&mut self, key: Vec<u8>, value: &[u8]) {
        let index_entry = {
            let entry = Entry::new(self.current_sequence, &*key, value);

            let (file_id, file_pos) = self.log.append_entry(&entry);

            self.current_sequence += 1;

            IndexEntry {
                file_id: file_id,
                entry_pos: file_pos,
                entry_size: entry.size(),
                sequence: entry.sequence,
            }
        };

        self.index.insert(key, index_entry);
    }

    fn delete(&mut self, key: &[u8]) {
        if self.index.remove(key).is_some() {
            let entry = Entry::deleted(self.current_sequence, key);
            let _ = self.log.append_entry(&entry);
            self.current_sequence += 1;
        }
    }

    fn compact(&self, file_id: u32) -> Option<u32> {
        self.log.hints(file_id).map(|hints| {
            let new_file_id = self.log.new_file_id();
            info!("Compacting data file: {} into: {}", file_id, new_file_id);

            let mut log_writer = LogWriter::new(&self.log.path, new_file_id, false);
            let mut deletes = HashMap::new();

            {
                let inserts = hints.filter(|hint| {
                    let index_entry = self.index.get(&*hint.key);

                    if hint.deleted {
                        if index_entry.is_none() {
                            match deletes.entry(hint.key.to_vec()) {
                                HashMapEntry::Occupied(mut o) => {
                                    if *o.get() < hint.sequence {
                                        o.insert(hint.sequence);
                                    }
                                }
                                HashMapEntry::Vacant(e) => {
                                    e.insert(hint.sequence);
                                }
                            }
                        }

                        false

                    } else {
                        index_entry.is_some() && index_entry.unwrap().sequence == hint.sequence
                    }
                });

                for hint in inserts {
                    log_writer.write(&self.log.read_entry(file_id, hint.entry_pos));
                }
            }

            for (key, sequence) in deletes {
                log_writer.write(&Entry::deleted(sequence, key));
            }

            new_file_id
        })
    }
}

#[derive(Clone)]
pub struct Cask {
    inner: Arc<RwLock<CaskInner>>,
}

impl Cask {
    pub fn open(path: &str, sync: bool) -> Cask {
        info!("Opening database: {:?}", &path);
        let mut log = Log::open(path, sync);
        let mut index = Index::new();

        let mut sequence = 0;

        for file_id in log.files() {
            let mut f = |hint: Hint| {
                if hint.sequence > sequence {
                    sequence = hint.sequence;
                }

                index.update(hint, file_id);
            };

            match log.hints(file_id) {
                Some(hints) => {
                    for hint in hints {
                        f(hint);
                    }
                }
                None => {
                    for hint in log.recreate_hints(file_id) {
                        f(hint);
                    }
                }
            };
        }

        info!("Opened database: {:?}", &path);
        info!("Current sequence number: {:?}", sequence);

        Cask {
            inner: Arc::new(RwLock::new(CaskInner {
                current_sequence: sequence + 1,
                log: log,
                index: index,
            })),
        }
    }

    pub fn compact(&self, file_id: u32) {
        let new_file_id = {
            self.inner.read().unwrap().compact(file_id)
        };

        if let Some(new_file_id) = new_file_id {
            let hints = {
                self.inner.read().unwrap().log.hints(new_file_id)
            };

            if let Some(hints) = hints {
                for hint in hints {
                    self.inner.write().unwrap().index.update(hint, new_file_id);
                }

                self.inner.write().unwrap().log.swap_file(file_id, new_file_id);

                info!("Finished compacting data file: {} into: {}",
                      file_id,
                      new_file_id);
            };
        }
    }

    pub fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        self.inner.read().unwrap().get(key)
    }

    pub fn put(&self, key: Vec<u8>, value: &[u8]) {
        self.inner.write().unwrap().put(key, value)
    }

    pub fn delete(&self, key: &[u8]) {
        self.inner.write().unwrap().delete(key)
    }
}
<commit_msg>finer-grained locking during compaction<commit_after>use std::collections::HashMap;
use std::collections::hash_map::Entry as HashMapEntry;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::ops::{Deref, DerefMut};
use std::vec::Vec;

use data::{Entry, Hint, SequenceNumber};
use log::{Log, LogWriter};

#[derive(Clone, Copy, Debug)]
struct IndexEntry {
    file_id: u32,
    entry_pos: u64,
    entry_size: u64,
    sequence: SequenceNumber,
}

struct Index(HashMap<Vec<u8>, IndexEntry>);

impl Index {
    fn new() -> Index {
        Index(HashMap::new())
    }

    fn update(&mut self, hint: Hint, file_id: u32) {
        match self.entry(hint.key.to_vec()) {
            HashMapEntry::Occupied(mut o) => {
                if o.get().sequence <= hint.sequence {
                    if hint.deleted {
                        o.remove();
                    } else {
                        o.insert(IndexEntry {
                            file_id: file_id,
                            entry_pos: hint.entry_pos,
                            entry_size: hint.entry_size(),
                            sequence: hint.sequence,
                        });
                    }
                }
            }
            HashMapEntry::Vacant(e) => {
                if !hint.deleted {
                    e.insert(IndexEntry {
                        file_id: file_id,
                        entry_pos: hint.entry_pos,
                        entry_size: hint.entry_size(),
                        sequence: hint.sequence,
                    });
                }
            }
        }
    }
}

impl Deref for Index {
    type Target = HashMap<Vec<u8>, IndexEntry>;
    fn deref(&self) -> &HashMap<Vec<u8>, IndexEntry> {
        &self.0
    }
}

impl DerefMut for Index {
    fn deref_mut(&mut self) -> &mut HashMap<Vec<u8>, IndexEntry> {
        &mut self.0
    }
}

struct CaskInner {
    current_sequence: SequenceNumber,
    index: Index,
    log: Log,
}

impl CaskInner {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        self.index.get(key).and_then(|index_entry| {
            let entry = self.log.read_entry(index_entry.file_id, index_entry.entry_pos);
            if entry.deleted {
                warn!("Index pointed to dead entry: Entry {{ key: {:?}, sequence: {} }} at file: \
                       {}",
                      entry.key,
                      entry.sequence,
                      index_entry.file_id);
                None
            } else {
                Some(entry.value.into_owned())
            }
        })
    }

    fn put(&mut self, key: Vec<u8>, value: &[u8]) {
        let index_entry = {
            let entry = Entry::new(self.current_sequence, &*key, value);

            let (file_id, file_pos) = self.log.append_entry(&entry);

            self.current_sequence += 1;

            IndexEntry {
                file_id: file_id,
                entry_pos: file_pos,
                entry_size: entry.size(),
                sequence: entry.sequence,
            }
        };

        self.index.insert(key, index_entry);
    }

    fn delete(&mut self, key: &[u8]) {
        if self.index.remove(key).is_some() {
            let entry = Entry::deleted(self.current_sequence, key);
            let _ = self.log.append_entry(&entry);
            self.current_sequence += 1;
        }
    }
}

#[derive(Clone)]
pub struct Cask {
    path: PathBuf,
    inner: Arc<RwLock<CaskInner>>,
}

impl Cask {
    pub fn open(path: &str, sync: bool) -> Cask {
        info!("Opening database: {:?}", &path);
        let mut log = Log::open(path, sync);
        let mut index = Index::new();

        let mut sequence = 0;

        for file_id in log.files() {
            let mut f = |hint: Hint| {
                if hint.sequence > sequence {
                    sequence = hint.sequence;
                }

                index.update(hint, file_id);
            };

            match log.hints(file_id) {
                Some(hints) => {
                    for hint in hints {
                        f(hint);
                    }
                }
                None => {
                    for hint in log.recreate_hints(file_id) {
                        f(hint);
                    }
                }
            };
        }

        info!("Opened database: {:?}", &path);
        info!("Current sequence number: {:?}", sequence);

        Cask {
            path: log.path.clone(),
            inner: Arc::new(RwLock::new(CaskInner {
                current_sequence: sequence + 1,
                log: log,
                index: index,
            })),
        }
    }

    fn compact_file(&self, file_id: u32) -> Option<u32> {
        let hints = {
            self.inner.read().unwrap().log.hints(file_id)
        };

        hints.map(|hints| {
            let new_file_id = {
                self.inner.read().unwrap().log.new_file_id()
            };

            info!("Compacting data file: {} into: {}", file_id, new_file_id);

            let mut log_writer = LogWriter::new(&self.path, new_file_id, false);
            let mut deletes = HashMap::new();

            {
                let inserts = hints.filter(|hint| {
                    let inner = self.inner.read().unwrap();
                    let index_entry = inner.index.get(&*hint.key);

                    if hint.deleted {
                        if index_entry.is_none() {
                            match deletes.entry(hint.key.to_vec()) {
                                HashMapEntry::Occupied(mut o) => {
                                    if *o.get() < hint.sequence {
                                        o.insert(hint.sequence);
                                    }
                                }
                                HashMapEntry::Vacant(e) => {
                                    e.insert(hint.sequence);
                                }
                            }
                        }

                        false

                    } else {
                        index_entry.is_some() && index_entry.unwrap().sequence == hint.sequence
                    }
                });

                for hint in inserts {
                    let log = &self.inner.read().unwrap().log;
                    log_writer.write(&log.read_entry(file_id, hint.entry_pos));
                }
            }

            for (key, sequence) in deletes {
                log_writer.write(&Entry::deleted(sequence, key));
            }

            new_file_id
        })
    }

    pub fn compact(&self, file_id: u32) {
        let new_file_id = self.compact_file(file_id);

        if let Some(new_file_id) = new_file_id {
            let hints = {
                self.inner.read().unwrap().log.hints(new_file_id)
            };

            if let Some(hints) = hints {
                for hint in hints {
                    self.inner.write().unwrap().index.update(hint, new_file_id);
                }

                self.inner.write().unwrap().log.swap_file(file_id, new_file_id);

                info!("Finished compacting data file: {} into: {}",
                      file_id,
                      new_file_id);
            };
        }
    }

    pub fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        self.inner.read().unwrap().get(key)
    }

    pub fn put(&self, key: Vec<u8>, value: &[u8]) {
        self.inner.write().unwrap().put(key, value)
    }

    pub fn delete(&self, key: &[u8]) {
        self.inner.write().unwrap().delete(key)
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::BufRead;
use std::error::Error;

fn main() {
    let stdin = io::stdin();
    let mut ns: Vec<f64> = match read_floats(&mut stdin.lock()) {
        Ok(ns) => ns,
        Err(e) => panic!("failed to read numbers: {}", e),
    };

    ns.sort_by(|a, b| match a.partial_cmp(b) {
        Some(o) => o,
        None => panic!("cannot compare {} and {}, thus cannort sort them", a, b),
    });

    let count = ns.len();

    let sum = ns.iter().fold(0.0, |a, &b| a + b);

    println!("count: {}", count);
    println!("sum:   {}", sum);

    if ns.is_empty() {
        println!("(empty set of numbers, remaining stats not available)");
    } else {
        println!("avg:   {}", sum / count as f64);
        println!("max:   {}", ns[count - 1]);
        println!("p999:  {}", ns[ns.len() * 999 / 1000]);
        println!("p99:   {}", ns[ns.len() * 99 / 100]);
        println!("p90:   {}", ns[ns.len() * 90 / 100]);
        println!("p50:   {}", ns[ns.len() * 50 / 100]);
        println!("min:   {}", ns[0]);
    }
}

fn read_floats(input: &mut io::BufRead) -> Result<Vec<f64>, Box<Error>> {
    let ns: Result<Vec<f64>, Box<Error>> = input
        .lines()
        .map(|line| -> Result<f64, Box<Error>> {
            Ok(line?.parse::<f64>()?)
        })
        .collect();

    ns
}
<commit_msg>Minimize main(), improve error handling.<commit_after>use std::io;
use std::io::BufRead;
use std::error::Error;

fn main() {
    if let Err(e) = numstats() {
        eprintln!("error: {}", e);
        std::process::exit(1);
    }
}

fn numstats() -> Result<(), Box<Error>> {
    let stdin = io::stdin();
    let mut ns: Vec<f64> = read_floats(&mut stdin.lock())?;

    ns.sort_by(|a, b| match a.partial_cmp(b) {
        Some(o) => o,
        None => panic!("cannot compare {} and {}, thus cannort sort them", a, b),
    });

    let count = ns.len();

    let sum = ns.iter().fold(0.0, |a, &b| a + b);

    println!("count: {}", count);
    println!("sum:   {}", sum);

    if ns.is_empty() {
        println!("(empty set of numbers, remaining stats not available)");
    } else {
        println!("avg:   {}", sum / count as f64);
        println!("max:   {}", ns[count - 1]);
        println!("p999:  {}", ns[ns.len() * 999 / 1000]);
        println!("p99:   {}", ns[ns.len() * 99 / 100]);
        println!("p90:   {}", ns[ns.len() * 90 / 100]);
        println!("p50:   {}", ns[ns.len() * 50 / 100]);
        println!("min:   {}", ns[0]);
    }

    Ok(())
}

fn read_floats(input: &mut io::BufRead) -> Result<Vec<f64>, Box<Error>> {
    let ns: Result<Vec<f64>, Box<Error>> = input
        .lines()
        .map(|line| -> Result<f64, Box<Error>> {
            Ok(line?.parse::<f64>()?)
        })
        .collect();

    ns
}
<|endoftext|>


<commit_before>/// Case-insensitive tag! variant for nom
///
/// By Paul English (https://gist.github.com/log0ymxm/d9c3fc9598cf2d92b8ae89a9ce5341d8)
///
/// This implementation has some performance issues related to extra memory allocations
/// (see https://github.com/Geal/nom/issues/251), but it works for the moment.

use nom::{IResult, Err, ErrorKind, Needed};
use std::str;

macro_rules! caseless_tag (
    ($i:expr, $inp: expr) => (
        {
            #[inline(always)]
            fn as_lower(b: &str) -> String {
                let s = b.to_string();
                s.to_lowercase()
            }

            let expected = $inp;
            let lower = as_lower(&expected);
            let bytes = lower.as_bytes();

            caseless_tag_bytes!($i,bytes)
        }
    );
);

macro_rules! caseless_tag_bytes (
    ($i:expr, $bytes: expr) => (
        {
            use std::cmp::min;
            let len = $i.len();
            let blen = $bytes.len();
            let m   = min(len, blen);
            let reduced = &$i[..m];

            let s = str::from_utf8(reduced).unwrap();
            let s2 = s.to_string();
            let lowered = s2.to_lowercase();
            let lowered_bytes = lowered.as_bytes();

            let b       = &$bytes[..m];

            let res: IResult<_,_> = if lowered_bytes != b {
                IResult::Error(Err::Position(ErrorKind::Tag, $i))
            } else if m < blen {
                IResult::Incomplete(Needed::Size(blen))
            } else {
                IResult::Done(&$i[blen..], reduced)
            };
            res
        }
    );
);

#[test]
fn test_caseless_tag() {
    named!(x, caseless_tag!("AbCD"));
    let r = x(&b"abcdefGH"[..]);
    assert_eq!(r, IResult::Done(&b"efGH"[..], &b"abcd"[..]));
    let r = x(&b"aBcdefGH"[..]);
    assert_eq!(r, IResult::Done(&b"efGH"[..], &b"aBcd"[..]));
}
<commit_msg>Fix unused import warnings<commit_after>/// Case-insensitive tag! variant for nom
///
/// By Paul English (https://gist.github.com/log0ymxm/d9c3fc9598cf2d92b8ae89a9ce5341d8)
///
/// This implementation has some performance issues related to extra memory allocations
/// (see https://github.com/Geal/nom/issues/251), but it works for the moment.

macro_rules! caseless_tag (
    ($i:expr, $inp: expr) => (
        {
            #[inline(always)]
            fn as_lower(b: &str) -> String {
                let s = b.to_string();
                s.to_lowercase()
            }

            let expected = $inp;
            let lower = as_lower(&expected);
            let bytes = lower.as_bytes();

            caseless_tag_bytes!($i,bytes)
        }
    );
);

macro_rules! caseless_tag_bytes (
    ($i:expr, $bytes: expr) => (
        {
            use std::cmp::min;
            let len = $i.len();
            let blen = $bytes.len();
            let m   = min(len, blen);
            let reduced = &$i[..m];

            let s = str::from_utf8(reduced).unwrap();
            let s2 = s.to_string();
            let lowered = s2.to_lowercase();
            let lowered_bytes = lowered.as_bytes();

            let b       = &$bytes[..m];

            let res: IResult<_,_> = if lowered_bytes != b {
                IResult::Error(Err::Position(ErrorKind::Tag, $i))
            } else if m < blen {
                IResult::Incomplete(Needed::Size(blen))
            } else {
                IResult::Done(&$i[blen..], reduced)
            };
            res
        }
    );
);

#[test]
fn test_caseless_tag() {
    use nom::{IResult, Err, ErrorKind, Needed};
    use std::str;

    named!(x, caseless_tag!("AbCD"));
    let r = x(&b"abcdefGH"[..]);
    assert_eq!(r, IResult::Done(&b"efGH"[..], &b"abcd"[..]));
    let r = x(&b"aBcdefGH"[..]);
    assert_eq!(r, IResult::Done(&b"efGH"[..], &b"aBcd"[..]));
}
<|endoftext|>


<commit_before>// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use protobuf::Message;

/// Asserts that a given protobuf message object can be serialized to bytes and
/// read back into a value of the same type, preserving equality between the
/// original object and the one read back.
///
/// This is helpful for confirming that proto files have been compiled
/// successfully and are exposed as intended by the public API of the parent
/// crate. It also lets us exercise the full set of methods we intend to use for
/// building particular proto objects so that we notice breakage early in the
/// event that a proto external to this repository would make a breaking change.
///
/// Note: assumes that the given `message` is not just `T::new` so that we can
/// tell that deserialization has happened successfully.
///
/// # Example
///
/// ```ignore
/// let mut request = SendCommandRequest::new();
/// request.set_command(b"...".to_vec());
/// test_round_trip(request);
/// ```
pub fn test_round_trip<T: Message + PartialEq>(message: T) {
    let serialized = message.write_to_bytes().unwrap();

    let mut back = T::new();
    assert_ne!(message, back);

    back.merge_from_bytes(&serialized).unwrap();
    assert_eq!(message, back);
}
<commit_msg>protos: delete unused test helper function<commit_after><|endoftext|>


<commit_before>//! This module is for statically evaluating a position.

use std::iter::AdditiveIterator;

use super::game::{Position, Color, Piece, PieceType, Plies};
use super::game::{Pawn, King, Queen, Bishop, Knight, Rook};
use super::game::{is_checkmated, is_stalemated};

pub use self::Result::{Score, WinIn, LoseIn, Draw};

type ScoreUnit = int;

/// An assessment of the position.
pub enum Result {
    // Positive: advantage for side to move.
    // Negative: disadvantage for side to move.
    Score(ScoreUnit),
    // Side to move can checkmate in x moves.
    // WinIn(Plies(1)): can checkmate now.
    // WinIn(Plies(2)): can checkmate next move.
    WinIn(Plies),
    // Side to move will be checkmated in x moves.
    // WinIn(Plies(0)): already checkmated.
    // WinIn(Plies(1)): Will be immediately checkmated after any move.
    LoseIn(Plies),
    Draw,
}

/// Evaluates the position without searching.
pub fn eval(p: &Position) -> Result {
    if is_checkmated(p.clone()) {
        LoseIn(Plies(0))
    } else if is_stalemated(p.clone()) {
        Draw
    } else {
        let c = p.side_to_move();
        Score(p.piece_iter().map( |(piece, _pos)| val_for_color(piece, c) ).sum())
    }
}

fn val_for_color(piece: Piece, c: Color) -> ScoreUnit {
    let val = val_of_piece_type(piece.piece_type());
    if piece.color() == c {
        val
    } else {
        -val
    }
}

fn val_of_piece_type(piece_type: PieceType) -> ScoreUnit {
    match piece_type {
        King   => 100000,
        Pawn   =>    100,
        Queen  =>    900,
        Bishop =>    300,
        Knight =>    300,
        Rook   =>    500,
    }
}
<commit_msg>Allow dead code in eval module.<commit_after>//! This module is for statically evaluating a position.

#![allow(dead_code)]

use std::iter::AdditiveIterator;

use super::game::{Position, Color, Piece, PieceType, Plies};
use super::game::{Pawn, King, Queen, Bishop, Knight, Rook};
use super::game::{is_checkmated, is_stalemated};

pub use self::Result::{Score, WinIn, LoseIn, Draw};

type ScoreUnit = int;

/// An assessment of the position.
pub enum Result {
    // Positive: advantage for side to move.
    // Negative: disadvantage for side to move.
    Score(ScoreUnit),
    // Side to move can checkmate in x moves.
    // WinIn(Plies(1)): can checkmate now.
    // WinIn(Plies(2)): can checkmate next move.
    WinIn(Plies),
    // Side to move will be checkmated in x moves.
    // WinIn(Plies(0)): already checkmated.
    // WinIn(Plies(1)): Will be immediately checkmated after any move.
    LoseIn(Plies),
    Draw,
}

/// Evaluates the position without searching.
pub fn eval(p: &Position) -> Result {
    if is_checkmated(p.clone()) {
        LoseIn(Plies(0))
    } else if is_stalemated(p.clone()) {
        Draw
    } else {
        let c = p.side_to_move();
        Score(p.piece_iter().map( |(piece, _pos)| val_for_color(piece, c) ).sum())
    }
}

fn val_for_color(piece: Piece, c: Color) -> ScoreUnit {
    let val = val_of_piece_type(piece.piece_type());
    if piece.color() == c {
        val
    } else {
        -val
    }
}

fn val_of_piece_type(piece_type: PieceType) -> ScoreUnit {
    match piece_type {
        King   => 100000,
        Pawn   =>    100,
        Queen  =>    900,
        Bishop =>    300,
        Knight =>    300,
        Rook   =>    500,
    }
}
<|endoftext|>


<commit_before>//! [Church pairs](https://en.wikipedia.org/wiki/Church_encoding#Church_pairs)

use term::{Term, Error, abs};
use term::Term::*;
use term::Error::*;
use church::booleans::{tru, fls};

/// Produces a Church-encoded pair; applying it to two other terms puts them inside it.
///
/// PAIR := λxyz.z x y = λ λ λ 1 3 2
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::pair;
/// use lambda_calculus::church::numerals::{zero, one};
///
/// let pair01 = app!(pair(), zero(), one());
///
/// assert_eq!(pair01.fst_ref(), Ok(&zero()));
/// assert_eq!(pair01.snd_ref(), Ok(&one()));
/// # }
/// ```
pub fn pair() -> Term {
    abs(abs(abs(
        app!(Var(1), Var(3), Var(2))
    )))
}

/// Applied to a Church-encoded pair `(a, b)` it yields `a`.
///
/// FST := λp.p TRUE = λ 1 TRUE
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, fst};
/// use lambda_calculus::church::numerals::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_0_1 = app!(pair(), zero(), one());
///
/// assert_eq!(beta(fst().app(pair_0_1), NOR, 0, false), zero());
/// # }
/// ```
pub fn fst() -> Term { abs(Var(1).app(tru())) }

/// Applied to a Church-encoded pair `(a, b)` it yields `b`.
///
/// SND := λp.p FALSE = λ 1 FALSE
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, snd};
/// use lambda_calculus::church::numerals::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_0_1 = app!(pair(), zero(), one());
///
/// assert_eq!(beta(snd().app(pair_0_1), NOR, 0, false), one());
/// # }
/// ```
pub fn snd() -> Term { abs(Var(1).app(fls())) }

/// Applied to a function and a Church-encoded pair `(a, b)` it uncurrys the pair and applies the
/// function to `a` and then `b`.
///
/// UNCURRY := λf.λp.f (FST p) (SND p) = λ λ 2 (FST 1) (SND 1)
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, uncurry};
/// use lambda_calculus::church::numerals::{succ, one, plus};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_3_5 = app!(pair(), 3.into(), 5.into());
///
/// assert_eq!(beta(app!(uncurry(), plus(), pair_3_5), NOR, 0, false), 8.into());
/// assert_eq!(beta(uncurry(), NOR, 0, false), uncurry());
/// # }
/// ```
/// λλ2(1(λλ2))(1(λλ1))
pub fn uncurry() -> Term {
    abs(abs(
        app!(Var(2), Var(1).app(abs(abs(Var(2)))), Var(1).app(abs(abs(Var(1)))))
    ))
}

impl Term {
    /// Checks whether `self` is a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert!(pair01.is_pair());
    /// # }
    /// ```
    pub fn is_pair(&self) -> bool {
        self.unpair_ref().is_ok()
    }

    /// Splits a Church-encoded pair into a pair of terms, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair(), Ok((zero(), one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair(self) -> Result<(Term, Term), Error> {
        let candidate = if let Abs(abstracted) = self { *abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp() {
            Ok((try!(wrapped_a.rhs()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Splits a Church-encoded pair into a pair of references to its underlying terms.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair_ref(), Ok((&zero(), &one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair_ref(&self) -> Result<(&Term, &Term), Error> {
        let candidate = if let Abs(ref abstracted) = *self { abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp_ref() {
            Ok((try!(wrapped_a.rhs_ref()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Splits a Church-encoded pair into a pair of mutable references to its underlying terms.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair_mut(), Ok((&mut zero(), &mut one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        let candidate = if let Abs(ref mut abstracted) = *self { abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp_mut() {
            Ok((try!(wrapped_a.rhs_mut()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Returns the first term from a Church-encoded pair, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst(), Ok(zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst(self) -> Result<Term, Error> {
        Ok(try!(self.unpair()).0)
    }

    /// Returns a reference to the first term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst_ref(), Ok(&zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.unpair_ref()).0)
    }

    /// Returns a mutable reference to the first term of a Church-encoded pair.
    /// Returns a reference to the first term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst_mut(), Ok(&mut zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.unpair_mut()).0)
    }

    /// Returns the second term from a Church-encoded pair, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd(), Ok(one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd(self) -> Result<Term, Error> {
        Ok(try!(self.unpair()).1)
    }

    /// Returns a reference to the second term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd_ref(), Ok(&one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.unpair_ref()).1)
    }

    /// Returns a mutable reference to the second term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd_mut(), Ok(&mut one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.unpair_mut()).1)
    }
}

impl From<(Term, Term)> for Term {
    fn from((t1, t2): (Term, Term)) -> Self {
        abs(app!(Var(1), t1, t2))
    }
}
<commit_msg>use abs!() in pairs.rs<commit_after>//! [Church pairs](https://en.wikipedia.org/wiki/Church_encoding#Church_pairs)

use term::{Term, Error, abs, app};
use term::Term::*;
use term::Error::*;
use church::booleans::{tru, fls};

/// Produces a Church-encoded pair; applying it to two other terms puts them inside it.
///
/// PAIR := λxyz.z x y = λ λ λ 1 3 2
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::pair;
/// use lambda_calculus::church::numerals::{zero, one};
///
/// let pair01 = app!(pair(), zero(), one());
///
/// assert_eq!(pair01.fst_ref(), Ok(&zero()));
/// assert_eq!(pair01.snd_ref(), Ok(&one()));
/// # }
/// ```
pub fn pair() -> Term {
    abs!(3, app!(Var(1), Var(3), Var(2)))
}

/// Applied to a Church-encoded pair `(a, b)` it yields `a`.
///
/// FST := λp.p TRUE = λ 1 TRUE
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, fst};
/// use lambda_calculus::church::numerals::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_0_1 = app!(pair(), zero(), one());
///
/// assert_eq!(beta(fst().app(pair_0_1), NOR, 0, false), zero());
/// # }
/// ```
pub fn fst() -> Term { abs(app(Var(1), tru())) }

/// Applied to a Church-encoded pair `(a, b)` it yields `b`.
///
/// SND := λp.p FALSE = λ 1 FALSE
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, snd};
/// use lambda_calculus::church::numerals::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_0_1 = app!(pair(), zero(), one());
///
/// assert_eq!(beta(snd().app(pair_0_1), NOR, 0, false), one());
/// # }
/// ```
pub fn snd() -> Term { abs(app(Var(1), fls())) }

/// Applied to a function and a Church-encoded pair `(a, b)` it uncurrys the pair and applies the
/// function to `a` and then `b`.
///
/// UNCURRY := λf.λp.f (FST p) (SND p) = λ λ 2 (FST 1) (SND 1)
///
/// # Example
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::church::pairs::{pair, uncurry};
/// use lambda_calculus::church::numerals::{succ, one, plus};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// let pair_3_5 = app!(pair(), 3.into(), 5.into());
///
/// assert_eq!(beta(app!(uncurry(), plus(), pair_3_5), NOR, 0, false), 8.into());
/// assert_eq!(beta(uncurry(), NOR, 0, false), uncurry());
/// # }
/// ```
/// λλ2(1(λλ2))(1(λλ1))
pub fn uncurry() -> Term {
    abs!(2, app!(
        Var(2),
        app(Var(1), abs!(2, Var(2))),
        app(Var(1), abs!(2, Var(1)))
    ))
}

impl Term {
    /// Checks whether `self` is a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert!(pair01.is_pair());
    /// # }
    /// ```
    pub fn is_pair(&self) -> bool {
        self.unpair_ref().is_ok()
    }

    /// Splits a Church-encoded pair into a pair of terms, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair(), Ok((zero(), one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair(self) -> Result<(Term, Term), Error> {
        let candidate = if let Abs(abstracted) = self { *abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp() {
            Ok((try!(wrapped_a.rhs()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Splits a Church-encoded pair into a pair of references to its underlying terms.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair_ref(), Ok((&zero(), &one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair_ref(&self) -> Result<(&Term, &Term), Error> {
        let candidate = if let Abs(ref abstracted) = *self { abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp_ref() {
            Ok((try!(wrapped_a.rhs_ref()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Splits a Church-encoded pair into a pair of mutable references to its underlying terms.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.unpair_mut(), Ok((&mut zero(), &mut one())));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn unpair_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
        let candidate = if let Abs(ref mut abstracted) = *self { abstracted } else { self };

        if let Ok((wrapped_a, b)) = candidate.unapp_mut() {
            Ok((try!(wrapped_a.rhs_mut()), b))
        } else {
            Err(NotAPair)
        }
    }

    /// Returns the first term from a Church-encoded pair, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst(), Ok(zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst(self) -> Result<Term, Error> {
        Ok(try!(self.unpair()).0)
    }

    /// Returns a reference to the first term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst_ref(), Ok(&zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.unpair_ref()).0)
    }

    /// Returns a mutable reference to the first term of a Church-encoded pair.
    /// Returns a reference to the first term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.fst_mut(), Ok(&mut zero()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn fst_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.unpair_mut()).0)
    }

    /// Returns the second term from a Church-encoded pair, consuming `self`.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd(), Ok(one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd(self) -> Result<Term, Error> {
        Ok(try!(self.unpair()).1)
    }

    /// Returns a reference to the second term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd_ref(), Ok(&one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd_ref(&self) -> Result<&Term, Error> {
        Ok(try!(self.unpair_ref()).1)
    }

    /// Returns a mutable reference to the second term of a Church-encoded pair.
    ///
    /// # Example
    /// ```
    /// # #[macro_use] extern crate lambda_calculus;
    /// # fn main() {
    /// use lambda_calculus::church::pairs::pair;
    /// use lambda_calculus::church::numerals::{zero, one};
    ///
    /// let mut pair01 = app!(pair(), zero(), one());
    ///
    /// assert_eq!(pair01.snd_mut(), Ok(&mut one()));
    /// # }
    /// ```
    /// # Errors
    ///
    /// The function will return an error if `self` is not a Church pair.
    pub fn snd_mut(&mut self) -> Result<&mut Term, Error> {
        Ok(try!(self.unpair_mut()).1)
    }
}

impl From<(Term, Term)> for Term {
    fn from((t1, t2): (Term, Term)) -> Self {
        abs(app!(Var(1), t1, t2))
    }
}
<|endoftext|>


<commit_before>use xpath_reader::Reader;
use crate::{Error, ErrorKind};

/// Checks if there is an error in the document provided by the reader and
/// returns Ok if there
/// wasn't and Err parsing the MusicBrainz error if the API actually returned
/// an error.
pub fn check_response_error<'d>(reader: &'d Reader<'d>) -> Result<(), Error> {
    let r: Result<Vec<String>, _> = reader.read("//error/text");

    match r {
        Ok(errs) => {
            if errs.len() > 0 {
                let text = errs.join("\n");
                Err(Error::new(text, ErrorKind::ServerError))
            } else {
                Ok(())
            }
        }
        Err(_) => Ok(()),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use xpath_reader::Reader;

    const XML_ERR: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?>
<error>
  <text>Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.</text>
  <text>For usage, please see: http://musicbrainz.org/development/mmd</text>
</error>"#;

    const XML_OK: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><release-group type="Album" type-id="f529b476-6e62-324f-b0aa-1f3e33d313fc" id="23c74936-ad4f-45bb-8b6b-527d4aeaaad6"><title>A.I Complex</title><first-release-date>2014-07-26</first-release-date><primary-type id="f529b476-6e62-324f-b0aa-1f3e33d313fc">Album</primary-type></release-group></metadata>"#;

    #[test]
    fn error() {
        let context = crate::util::musicbrainz_context();
        let reader = Reader::from_str(XML_ERR, Some(&context)).unwrap();

        let res = check_response_error(&reader);
        let err = res.err().unwrap();

        assert!(format!("{}", err).starts_with(
            "[server error]: Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.\nFor usage, please see: http://musicbrainz.org/development/mmd"));
    }

    #[test]
    fn ok() {
        let context = crate::util::musicbrainz_context();
        let reader = Reader::from_str(XML_OK, Some(&context)).unwrap();

        // should not raise error
        check_response_error(&reader).unwrap();
    }
}
<commit_msg>Fix error from earlier commit.<commit_after>use xpath_reader::Reader;
use crate::error::{Error, ErrorKind};

/// Checks if there is an error in the document provided by the reader and
/// returns Ok if there
/// wasn't and Err parsing the MusicBrainz error if the API actually returned
/// an error.
pub fn check_response_error<'d>(reader: &'d Reader<'d>) -> Result<(), Error> {
    let r: Result<Vec<String>, _> = reader.read("//error/text");

    match r {
        Ok(errs) => {
            if errs.len() > 0 {
                let text = errs.join("\n");
                Err(Error::new(text, ErrorKind::ServerError))
            } else {
                Ok(())
            }
        }
        Err(_) => Ok(()),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use xpath_reader::Reader;

    const XML_ERR: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?>
<error>
  <text>Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.</text>
  <text>For usage, please see: http://musicbrainz.org/development/mmd</text>
</error>"#;

    const XML_OK: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><release-group type="Album" type-id="f529b476-6e62-324f-b0aa-1f3e33d313fc" id="23c74936-ad4f-45bb-8b6b-527d4aeaaad6"><title>A.I Complex</title><first-release-date>2014-07-26</first-release-date><primary-type id="f529b476-6e62-324f-b0aa-1f3e33d313fc">Album</primary-type></release-group></metadata>"#;

    #[test]
    fn error() {
        let context = crate::util::musicbrainz_context();
        let reader = Reader::from_str(XML_ERR, Some(&context)).unwrap();

        let res = check_response_error(&reader);
        let err = res.err().unwrap();

        assert!(format!("{}", err).starts_with(
            "[server error]: Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.\nFor usage, please see: http://musicbrainz.org/development/mmd"));
    }

    #[test]
    fn ok() {
        let context = crate::util::musicbrainz_context();
        let reader = Reader::from_str(XML_OK, Some(&context)).unwrap();

        // should not raise error
        check_response_error(&reader).unwrap();
    }
}
<|endoftext|>


<commit_before>//! File utilities

use {std};

//
//  file_new_append_incremental
//
/// Calls `file_new_append` on the path returned by feeding the file path
/// to `file_path_incremental`.

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file (☞ see [`file_path_incremental`](fn.file_path_incremental.html))

pub fn file_new_append_incremental (file_path : &std::path::Path)
  -> Result <(std::path::PathBuf, std::fs::File), std::io::Error>
{
  let file_pathbuf = file_path_incremental (file_path)?;
  let file         = file_new_append (file_pathbuf.as_path())?;
  Ok ((file_pathbuf, file))
}

//
//  file_new_append
//
/// Opens a new file at specified path for writing in append mode,
/// recursively creating parent directories

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_new_append;
/// let e = file_new_append (Path::new ("somepath/")).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(e.description(), "not a file");
/// ```
///
/// - File already exists:
///
/// ```
/// extern crate tempdir;
/// # extern crate rs_utils;
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_new_append;
/// # fn main () {
///
/// let temp_dir = tempdir::TempDir::new_in ("", "tmp").unwrap();
/// let file_path = temp_dir.path().join (Path::new ("somefile"));
/// let file_path = file_path.as_path();
/// assert! (!file_path.exists());
/// file_new_append (file_path).unwrap();
/// let e = file_new_append (file_path).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::AlreadyExists);
/// assert_eq!(e.description(), "entity already exists");
/// # }
/// ```

pub fn file_new_append (file_path : &std::path::Path)
  -> Result <std::fs::File, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (std::io::ErrorKind::InvalidInput,
      "not a file".to_string()))
  }

  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  std::fs::create_dir_all (dir)?;

  std::fs::OpenOptions::new().append (true).create_new (true).open (file_path)
} // end file_new_append

//
//  file_path_incremental
//
/// Returns the file path appended with suffix `-N` where `N` gives
/// the first available non-pre-existing filename starting from `0`.
///
/// This function only queries for the next available filename, no
/// directories or files are created.

/// # Examples
///
/// ```
/// # use std::path::Path;
/// # use rs_utils::file::file_path_incremental;
/// let file_path = Path::new ("somedir/somefile");
/// assert_eq!(
///   file_path_incremental (file_path).unwrap().to_str().unwrap(),
///   "somedir/somefile-0"
/// );
/// ```

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_path_incremental;
/// let e = file_path_incremental (Path::new ("somepath/")).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(e.description(), "not a file");
/// ```

pub fn file_path_incremental (file_path : &std::path::Path)
  -> Result <std::path::PathBuf, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (
      std::io::ErrorKind::InvalidInput, "not a file".to_string()))
  }

  // unwrap failure should have been caught by `is_file` test
  let file_name = file_path.file_name().unwrap_or_else (
    || panic!("fatal: path should be a valid file")
  ).to_str().unwrap_or_else (
    || panic!("fatal: `file_path.file_name()` \
      returned invalid os str: {:?}", file_path.file_name()));
  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  for i in 0.. {
    let name = String::from (file_name) + &format!("-{}", i);
    let fp   = dir.join (name);
    if !fp.exists() {
      return Ok (fp)
    }
  }
  unreachable!("fatal: incremental file name loop should have returned")
} // end file_path_incremental

//
//  file_path_incremental_with_extension
//
/// Like file path incremental but preserves the file extension if one is
/// present.

pub fn file_path_incremental_with_extension (file_path : &std::path::Path)
  -> Result <std::path::PathBuf, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (
      std::io::ErrorKind::InvalidInput, "not a file".to_string()))
  }

  if file_path.extension().is_none() {
    return file_path_incremental (file_path)
  }
  let extension = file_path.extension().unwrap().to_str().unwrap();

  // unwrap failure should have been caught by `is_file` test
  let file_stem = file_path.file_stem().unwrap_or_else (
    || panic!("fatal: path should be a valid file")
  ).to_str().unwrap_or_else (
    || panic!("fatal: `file_path.file_name()` \
      returned invalid os str: {:?}", file_path.file_name()));
  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  for i in 0.. {
    let name = &format!("{}-{}.{}", file_stem, i, extension);
    let fp   = dir.join (name);
    if !fp.exists() {
      return Ok (fp)
    }
  }
  unreachable!("fatal: incremental file name loop should have returned")
} // end file_path_incremental_with_extension

//
//  is_file
//
/// If this returns true then `std::fs::File::create` will not fail
/// with "is a directory" error.
///
/// This is not the same as `std::path::Path::is_file` which also
/// tests whether the file actually exists.
///
/// # Examples
///
/// ```
/// # use std::path::Path; use rs_utils::file::is_file;
/// assert!(is_file (Path::new ("path/to/file")).unwrap());
/// assert!(!is_file (Path::new ("path/to/directory/")).unwrap());
/// assert!(!is_file (Path::new ("..")).unwrap());
/// ```
///
/// # Errors
///
/// - Invalid unicode:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind;
/// # use std::path::Path; use std::ffi::OsStr;
/// # use rs_utils::file::is_file;
/// use std::os::unix::ffi::OsStrExt;
/// let garbage = [192u8, 192u8, 192u8, 192u8];
/// let garbage_path = Path::new (OsStr::from_bytes (&garbage));
/// let e = is_file (&garbage_path).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(e.description(), "not valid unicode");
/// ```

pub fn is_file (file_path : &std::path::Path) -> Result <bool, std::io::Error> {
  let s = file_path.to_str().ok_or (std::io::Error::new (
    std::io::ErrorKind::InvalidInput, "not valid unicode".to_string()))?;

  if s.ends_with (std::path::MAIN_SEPARATOR) {
    return Ok (false)
  }

  if let None = std::path::Path::new (file_path).file_name() {
    return Ok (false)
  }

  Ok (true)
} //  end is_file

//
//  tests
//
#[cfg(test)]
mod tests {
  use {std, tempdir, quickcheck};
  use quickcheck_macros::quickcheck;
  use super::*;

  //
  //  prop_is_file_implies_not_directory
  //
  // test that is_file() implies file creation will not give an "is a
  // directory" error: as of Rust 1.16 (2017-01-23) this error is
  // simply indicated by an ErrorKind::Other (other os error)
  #[ignore] // to run test use `cargo test -- --ignored`
  #[quickcheck]
  fn prop_is_file_implies_not_directory (file_path : String)
    -> quickcheck::TestResult
  {
    let file_path = std::path::Path::new (file_path.as_str());
    if !is_file (file_path).unwrap() {
      return quickcheck::TestResult::discard()
    }
    if let Some (s) = file_path.parent() {
      if !s.to_str().unwrap().is_empty() {
        return quickcheck::TestResult::discard()
      }
    }
    let temp_dir = tempdir::TempDir::new_in ("", "tmp").unwrap();
    let file_path = temp_dir.path().join (file_path);
    quickcheck::TestResult::from_bool (
      if let Err(e) = std::fs::OpenOptions::new()
        .append (true).create (true).open (file_path.clone())
      {
        e.kind() != std::io::ErrorKind::Other
      } else {
        true
      }
    )
  }

} // end mod tests
<commit_msg>fix deprecated error description warning<commit_after>//! File utilities

use {std};

//
//  file_new_append_incremental
//
/// Calls `file_new_append` on the path returned by feeding the file path
/// to `file_path_incremental`.

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file (☞ see [`file_path_incremental`](fn.file_path_incremental.html))

pub fn file_new_append_incremental (file_path : &std::path::Path)
  -> Result <(std::path::PathBuf, std::fs::File), std::io::Error>
{
  let file_pathbuf = file_path_incremental (file_path)?;
  let file         = file_new_append (file_pathbuf.as_path())?;
  Ok ((file_pathbuf, file))
}

//
//  file_new_append
//
/// Opens a new file at specified path for writing in append mode,
/// recursively creating parent directories

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_new_append;
/// let e = file_new_append (Path::new ("somepath/")).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(&e.to_string(), "not a file");
/// ```
///
/// - File already exists:
///
/// ```
/// extern crate tempdir;
/// # extern crate rs_utils;
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_new_append;
/// # fn main () {
///
/// let temp_dir = tempdir::TempDir::new_in ("", "tmp").unwrap();
/// let file_path = temp_dir.path().join (Path::new ("somefile"));
/// let file_path = file_path.as_path();
/// assert! (!file_path.exists());
/// file_new_append (file_path).unwrap();
/// let e = file_new_append (file_path).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::AlreadyExists);
/// assert_eq!(e.description(), "entity already exists");
/// # }
/// ```

pub fn file_new_append (file_path : &std::path::Path)
  -> Result <std::fs::File, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (std::io::ErrorKind::InvalidInput,
      "not a file".to_string()))
  }

  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  std::fs::create_dir_all (dir)?;

  std::fs::OpenOptions::new().append (true).create_new (true).open (file_path)
} // end file_new_append

//
//  file_path_incremental
//
/// Returns the file path appended with suffix `-N` where `N` gives
/// the first available non-pre-existing filename starting from `0`.
///
/// This function only queries for the next available filename, no
/// directories or files are created.

/// # Examples
///
/// ```
/// # use std::path::Path;
/// # use rs_utils::file::file_path_incremental;
/// let file_path = Path::new ("somedir/somefile");
/// assert_eq!(
///   file_path_incremental (file_path).unwrap().to_str().unwrap(),
///   "somedir/somefile-0"
/// );
/// ```

/// # Errors
///
/// - Invalid unicode (☞ see [`is_file`](fn.is_file.html))
/// - Not a file:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind; use std::path::Path;
/// # use rs_utils::file::file_path_incremental;
/// let e = file_path_incremental (Path::new ("somepath/")).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(e.description(), "not a file");
/// ```

pub fn file_path_incremental (file_path : &std::path::Path)
  -> Result <std::path::PathBuf, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (
      std::io::ErrorKind::InvalidInput, "not a file".to_string()))
  }

  // unwrap failure should have been caught by `is_file` test
  let file_name = file_path.file_name().unwrap_or_else (
    || panic!("fatal: path should be a valid file")
  ).to_str().unwrap_or_else (
    || panic!("fatal: `file_path.file_name()` \
      returned invalid os str: {:?}", file_path.file_name()));
  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  for i in 0.. {
    let name = String::from (file_name) + &format!("-{}", i);
    let fp   = dir.join (name);
    if !fp.exists() {
      return Ok (fp)
    }
  }
  unreachable!("fatal: incremental file name loop should have returned")
} // end file_path_incremental

//
//  file_path_incremental_with_extension
//
/// Like file path incremental but preserves the file extension if one is
/// present.

pub fn file_path_incremental_with_extension (file_path : &std::path::Path)
  -> Result <std::path::PathBuf, std::io::Error>
{
  if !is_file (file_path)? {
    return Err (std::io::Error::new (
      std::io::ErrorKind::InvalidInput, "not a file".to_string()))
  }

  if file_path.extension().is_none() {
    return file_path_incremental (file_path)
  }
  let extension = file_path.extension().unwrap().to_str().unwrap();

  // unwrap failure should have been caught by `is_file` test
  let file_stem = file_path.file_stem().unwrap_or_else (
    || panic!("fatal: path should be a valid file")
  ).to_str().unwrap_or_else (
    || panic!("fatal: `file_path.file_name()` \
      returned invalid os str: {:?}", file_path.file_name()));
  let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
  for i in 0.. {
    let name = &format!("{}-{}.{}", file_stem, i, extension);
    let fp   = dir.join (name);
    if !fp.exists() {
      return Ok (fp)
    }
  }
  unreachable!("fatal: incremental file name loop should have returned")
} // end file_path_incremental_with_extension

//
//  is_file
//
/// If this returns true then `std::fs::File::create` will not fail
/// with "is a directory" error.
///
/// This is not the same as `std::path::Path::is_file` which also
/// tests whether the file actually exists.
///
/// # Examples
///
/// ```
/// # use std::path::Path; use rs_utils::file::is_file;
/// assert!(is_file (Path::new ("path/to/file")).unwrap());
/// assert!(!is_file (Path::new ("path/to/directory/")).unwrap());
/// assert!(!is_file (Path::new ("..")).unwrap());
/// ```
///
/// # Errors
///
/// - Invalid unicode:
///
/// ```
/// # use std::error::Error; use std::io::ErrorKind;
/// # use std::path::Path; use std::ffi::OsStr;
/// # use rs_utils::file::is_file;
/// use std::os::unix::ffi::OsStrExt;
/// let garbage = [192u8, 192u8, 192u8, 192u8];
/// let garbage_path = Path::new (OsStr::from_bytes (&garbage));
/// let e = is_file (&garbage_path).err().unwrap();
/// assert_eq!(e.kind(), ErrorKind::InvalidInput);
/// assert_eq!(e.description(), "not valid unicode");
/// ```

pub fn is_file (file_path : &std::path::Path) -> Result <bool, std::io::Error> {
  let s = file_path.to_str().ok_or (std::io::Error::new (
    std::io::ErrorKind::InvalidInput, "not valid unicode".to_string()))?;

  if s.ends_with (std::path::MAIN_SEPARATOR) {
    return Ok (false)
  }

  if let None = std::path::Path::new (file_path).file_name() {
    return Ok (false)
  }

  Ok (true)
} //  end is_file

//
//  tests
//
#[cfg(test)]
mod tests {
  use {std, tempdir, quickcheck};
  use quickcheck_macros::quickcheck;
  use super::*;

  //
  //  prop_is_file_implies_not_directory
  //
  // test that is_file() implies file creation will not give an "is a
  // directory" error: as of Rust 1.16 (2017-01-23) this error is
  // simply indicated by an ErrorKind::Other (other os error)
  #[ignore] // to run test use `cargo test -- --ignored`
  #[quickcheck]
  fn prop_is_file_implies_not_directory (file_path : String)
    -> quickcheck::TestResult
  {
    let file_path = std::path::Path::new (file_path.as_str());
    if !is_file (file_path).unwrap() {
      return quickcheck::TestResult::discard()
    }
    if let Some (s) = file_path.parent() {
      if !s.to_str().unwrap().is_empty() {
        return quickcheck::TestResult::discard()
      }
    }
    let temp_dir = tempdir::TempDir::new_in ("", "tmp").unwrap();
    let file_path = temp_dir.path().join (file_path);
    quickcheck::TestResult::from_bool (
      if let Err(e) = std::fs::OpenOptions::new()
        .append (true).create (true).open (file_path.clone())
      {
        e.kind() != std::io::ErrorKind::Other
      } else {
        true
      }
    )
  }

} // end mod tests
<|endoftext|>