<commit_before>
extern crate mprovision;
#[macro_use(version)]
extern crate version;
extern crate chrono;
#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
extern crate clap;

use mprovision as mp;
use cli::Command;
use std::env;
use std::path::PathBuf;
use chrono::*;

mod cli;

fn main() {
    let result = cli::parse(env::args()).and_then(|command| {
        match command {
            Command::List(cli::ListParams { filter, expire_in_days, directory }) => {
                list(filter, expire_in_days, directory)
            }
            Command::ShowUuid(uuid, directory) => show_uuid(uuid, directory),
            Command::ShowFile(path) => show_file(path),
            Command::Remove(uuid, directory) => remove(uuid, directory),
            Command::Clean(directory) => clean(directory),
        }
    });
    if let Err(e) = result {
        e.exit();
    }
}

fn list(filter: Option<String>,
        expires_in_days: Option<i64>,
        directory: Option<PathBuf>)
        -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let total = entries.len();
            let date = expires_in_days.map(|days| UTC::now() + Duration::days(days));
            let filter_string = filter.as_ref();
            let mut profiles = mp::filter(entries, |profile| {
                match (date, filter_string) {
                    (Some(date), Some(string)) => {
                        profile.expiration_date <= date && profile.contains(string)
                    }
                    (Some(date), _) => profile.expiration_date <= date,
                    (_, Some(string)) => profile.contains(string),
                    (_, _) => true,
                }
            });
            profiles.sort_by(|a, b| a.creation_date.cmp(&b.creation_date));
            (total, profiles)
        })
        .and_then(|(total, profiles)| {
            if profiles.is_empty() {
                Ok(println!("Nothing found"))
            } else {
                for profile in &profiles {
                    println!("\n{}", profile.description());
                }
                Ok(println!("\nFound {} of {}", profiles.len(), total))
            }
        })
}

fn show_uuid(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::show(&profile.path).map(|xml| println!("{}", xml)))
        })
        .map_err(|err| err.into())
}

fn show_file(path: PathBuf) -> Result<(), cli::Error> {
    mp::show(&path)
        .map(|xml| println!("{}", xml))
        .map_err(|err| err.into())
}

fn remove(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::remove(&profile.path))
                .map(|_| println!("'{}' was removed", uuid))
        })
        .map_err(|err| err.into())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {

    fn concat(mut s1: String, s2: String) -> String {
        s1.push_str(&s2);
        s1
    }

    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let date = UTC::now();
            mp::filter(entries, |profile| profile.expiration_date <= date)
        })
        .and_then(|profiles| {
            if profiles.is_empty() {
                Ok(println!("All provisioning profiles are valid"))
            } else {
                profiles.iter()
                    .map(|profile| {
                        std::fs::remove_file(&profile.path)
                            .map(|_| format!("'{}' was removed\n", profile.uuid))
                            .map_err(|err| format!("'{}' {}\n", profile.uuid, err))
                    })
                    .fold(Ok(String::new()), |acc, s| {
                        match (acc, s) {
                            (Ok(acc), Ok(s)) => Ok(concat(acc, s)),
                            (Ok(acc), Err(s)) |
                            (Err(acc), Ok(s)) |
                            (Err(acc), Err(s)) => Err(concat(acc, s)),
                        }
                    })
                    .map(|s| println!("{}", s))
                    .map_err(cli::Error::Custom)
            }
        })
}
<commit_msg>Reformat<commit_after>
extern crate mprovision;
#[macro_use(version)]
extern crate version;
extern crate chrono;
#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
extern crate clap;

use mprovision as mp;
use cli::Command;
use std::env;
use std::path::PathBuf;
use chrono::*;

mod cli;

fn main() {
    let result = cli::parse(env::args()).and_then(|command| match command {
        Command::List(cli::ListParams { filter, expire_in_days, directory }) => {
            list(filter, expire_in_days, directory)
        }
        Command::ShowUuid(uuid, directory) => show_uuid(uuid, directory),
        Command::ShowFile(path) => show_file(path),
        Command::Remove(uuid, directory) => remove(uuid, directory),
        Command::Clean(directory) => clean(directory),
    });
    if let Err(e) = result {
        e.exit();
    }
}

fn list(filter: Option<String>,
        expires_in_days: Option<i64>,
        directory: Option<PathBuf>)
        -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let total = entries.len();
            let date = expires_in_days.map(|days| UTC::now() + Duration::days(days));
            let filter_string = filter.as_ref();
            let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
                (Some(date), Some(string)) => {
                    profile.expiration_date <= date && profile.contains(string)
                }
                (Some(date), _) => profile.expiration_date <= date,
                (_, Some(string)) => profile.contains(string),
                (_, _) => true,
            });
            profiles.sort_by(|a, b| a.creation_date.cmp(&b.creation_date));
            (total, profiles)
        })
        .and_then(|(total, profiles)| if profiles.is_empty() {
            Ok(println!("Nothing found"))
        } else {
            for profile in &profiles {
                println!("\n{}", profile.description());
            }
            Ok(println!("\nFound {} of {}", profiles.len(), total))
        })
}

fn show_uuid(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::show(&profile.path).map(|xml| println!("{}", xml)))
        })
        .map_err(|err| err.into())
}

fn show_file(path: PathBuf) -> Result<(), cli::Error> {
    mp::show(&path)
        .map(|xml| println!("{}", xml))
        .map_err(|err| err.into())
}

fn remove(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::remove(&profile.path))
                .map(|_| println!("'{}' was removed", uuid))
        })
        .map_err(|err| err.into())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {

    fn concat(mut s1: String, s2: String) -> String {
        s1.push_str(&s2);
        s1
    }

    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let date = UTC::now();
            mp::filter(entries, |profile| profile.expiration_date <= date)
        })
        .and_then(|profiles| if profiles.is_empty() {
            Ok(println!("All provisioning profiles are valid"))
        } else {
            profiles.iter()
                .map(|profile| {
                    std::fs::remove_file(&profile.path)
                        .map(|_| format!("'{}' was removed\n", profile.uuid))
                        .map_err(|err| format!("'{}' {}\n", profile.uuid, err))
                })
                .fold(Ok(String::new()), |acc, s| match (acc, s) {
                    (Ok(acc), Ok(s)) => Ok(concat(acc, s)),
                    (Ok(acc), Err(s)) |
                    (Err(acc), Ok(s)) |
                    (Err(acc), Err(s)) => Err(concat(acc, s)),
                })
                .map(|s| println!("{}", s))
                .map_err(cli::Error::Custom)
        })
}
<|endoftext|>


<commit_before>//! A small server that writes as many nul bytes on all connections it receives.
//!
//! There is no concurrency in this server, only one connection is written to at
//! a time.

#[macro_use]
extern crate futures;
extern crate futures_io;
extern crate futures_mio;

use std::env;
use std::net::SocketAddr;

use futures::Future;
use futures::stream::Stream;
use futures_io::IoFuture;

fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = futures_mio::Loop::new().unwrap();
    let server = l.handle().tcp_listen(&addr).and_then(|socket| {
        socket.incoming().and_then(|(socket, addr)| {
            println!("got a socket: {}", addr);
            write(socket).or_else(|_| Ok(()))
        }).for_each(|()| {
            println!("lost the socket");
            Ok(())
        })
    });
    println!("Listenering on: {}", addr);
    l.run(server).unwrap();
}

// TODO: this blows the stack...
fn write(socket: futures_mio::TcpStream) -> IoFuture<()> {
    static BUF: &'static [u8] = &[0; 1 * 1024 * 1024];
    futures_io::write_all(socket, BUF).and_then(|(socket, _)| {
        write(socket)
    }).boxed()
}
<commit_msg>Rewrite recursion with a loop in sink.rs<commit_after>//! A small server that writes as many nul bytes on all connections it receives.
//!
//! There is no concurrency in this server, only one connection is written to at
//! a time.

#[macro_use]
extern crate futures;
extern crate futures_io;
extern crate futures_mio;

use std::env;
use std::iter;
use std::net::SocketAddr;

use futures::Future;
use futures::stream::{self, Stream};
use futures_io::IoFuture;

fn main() {
    let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
    let addr = addr.parse::<SocketAddr>().unwrap();

    let mut l = futures_mio::Loop::new().unwrap();
    let server = l.handle().tcp_listen(&addr).and_then(|socket| {
        socket.incoming().and_then(|(socket, addr)| {
            println!("got a socket: {}", addr);
            write(socket).or_else(|_| Ok(()))
        }).for_each(|()| {
            println!("lost the socket");
            Ok(())
        })
    });
    println!("Listenering on: {}", addr);
    l.run(server).unwrap();
}

fn write(socket: futures_mio::TcpStream) -> IoFuture<()> {
    static BUF: &'static [u8] = &[0; 64 * 1024];
    let iter = iter::repeat(()).map(|()| Ok(()));
    stream::iter(iter).fold(socket, |socket, ()| {
        futures_io::write_all(socket, BUF).map(|(socket, _)| socket)
    }).map(|_| ()).boxed()
}
<|endoftext|>


<commit_before>//! Defines the BSDF which acts as a container for composing the various BRDFs
//! and BTDFs that describe the surface's properties

use std::cmp;
use enum_set::EnumSet;

use linalg::{self, Normal, Vector, Point};
use film::Colorf;
use geometry::DifferentialGeometry;
use bxdf::{BxDF, BxDFType};
use sampler::Sample;

/// The BSDF contains the various BRDFs and BTDFs that describe the surface's properties
/// at some point. It also transforms incident and outgoing light directions into
/// shading space to make the BxDFs easier to implement.
/// TODO: We really need the memory pool. Each time we get the bsdf from a
/// material we need to allocate a decent amount of stuff since they each need
/// their own tangent, bitangent and differential geometry reference.
pub struct BSDF<'a> {
    /// The hit point
    pub p: Point,
    /// Shading normal, may be perturbed by bump mapping
    pub n: Normal,
    /// The actual geometry normal
    pub ng: Normal,
    /// Tangent vector for the surface
    pub tan: Vector,
    /// Bitangent vector for the surface
    pub bitan: Vector,
    /// Refractive index of the geometry
    pub eta: f32,
    bxdfs: &'a [&'a BxDF],
}

impl<'a> BSDF<'a> {
    /// Create a new BSDF using the BxDFs passed to shade the differential geometry with
    /// refractive index `eta`
    pub fn new<'b>(bxdfs: &'a [&'a BxDF], eta: f32, dg: &DifferentialGeometry<'b>) -> BSDF<'a> {
        let n = dg.n.normalized();
        let mut bitan = dg.dp_du.normalized();
        let tan = linalg::cross(&n, &bitan);
        bitan = linalg::cross(&tan, &n);
        BSDF { p: dg.p, n: n, ng: dg.ng, tan: tan, bitan: bitan, bxdfs: bxdfs, eta: eta }
    }
    /// Return the total number of BxDFs
    pub fn num_bxdfs(&self) -> usize { self.bxdfs.len() }
    /// Return the number of BxDFs matching the flags
    pub fn num_matching(&self, flags: EnumSet<BxDFType>) -> usize {
        self.bxdfs.iter().filter(|x| x.matches(flags)).count()
    }
    /// Transform the vector from world space to shading space
    pub fn to_shading(&self, v: &Vector) -> Vector {
        Vector::new(linalg::dot(v, &self.bitan), linalg::dot(v, &self.tan),
                    linalg::dot(v, &self.n))
    }
    /// Transform the vectro from shading space to world space
    pub fn from_shading(&self, v: &Vector) -> Vector {
        Vector::new(self.bitan.x * v.x + self.tan.x * v.y + self.n.x * v.z,
                    self.bitan.y * v.x + self.tan.y * v.y + self.n.y * v.z,
                    self.bitan.z * v.x + self.tan.z * v.y + self.n.z * v.z)
    }
    /// Evaluate the BSDF for the outgoing and incident light directions
    /// `w_o` and `w_i` in world space, sampling the desired subset of BxDFs
    /// selected by the flags passed. `wo_world` and `wi_world` should point from
    /// the hit point in the outgoing and incident light directions respectively.
    pub fn eval(&self, wo_world: &Vector, wi_world: &Vector, mut flags: EnumSet<BxDFType>) -> Colorf {
        let w_o = self.to_shading(wo_world);
        let w_i = self.to_shading(wi_world);
        // Determine if we should evaluate reflection or transmission based on the
        // geometry normal and the light directions
        if linalg::dot(wo_world, &self.ng) * linalg::dot(wi_world, &self.ng) > 0.0 {
            flags.remove(&BxDFType::Transmission);
        } else {
            flags.remove(&BxDFType::Reflection);
        }
        // Find all matching BxDFs and add their contribution to the material's color
        self.bxdfs.iter().filter_map(|x| if x.matches(flags) { Some(x.eval(&w_o, &w_i)) } else { None })
            .fold(Colorf::broadcast(0.0), |x, y| x + y)
    }
    /// Sample a component of the BSDF to get an incident light direction for light
    /// leaving the surface along `w_o`.
    /// `samples` are the 3 random values to use when sampling a component of the BSDF
    /// and a the chosen BSDF
    /// Returns the color, direction, pdf and the type of BxDF that was sampled.
    pub fn sample(&self, wo_world: &Vector, flags: EnumSet<BxDFType>, samples: &Sample)
        -> (Colorf, Vector, f32, EnumSet<BxDFType>) {
        let n_matching = self.num_matching(flags);
        if n_matching == 0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
        }
        let comp = cmp::min((samples.one_d * n_matching as f32) as usize, n_matching - 1);
        let bxdf = self.matching_at(comp, flags);
        let w_o = self.to_shading(wo_world).normalized();
        let (mut f, w_i, mut pdf) = bxdf.sample(&w_o, &samples.two_d);
        if w_i.length_sqr() == 0.0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
        }
        let wi_world = self.from_shading(&w_i).normalized();

        // TODO: We re-use our functions but actually do a lot of redundant computation. I'm not
        // sure that the compiler will eliminate it. Should just copy in the code from pdf and eval
        if !bxdf.bxdf_type().contains(&BxDFType::Specular) && n_matching > 1 {
            pdf = self.pdf(wo_world, &wi_world, flags);
        }
        if n_matching > 1 {
            pdf /= n_matching as f32;
        }

        if !bxdf.bxdf_type().contains(&BxDFType::Specular) {
            f = self.eval(wo_world, &wi_world, flags);
        }
        (f, wi_world, pdf, bxdf.bxdf_type())
    }
    /// Compute the pdf for sampling the pair of incident and outgoing light directions for
    /// the BxDFs matching the flags set
    pub fn pdf(&self, wo_world: &Vector, wi_world: &Vector, flags: EnumSet<BxDFType>) -> f32 {
        // should also normalize?
        let w_o = self.to_shading(wo_world).normalized();
        let w_i = self.to_shading(wi_world).normalized();
        let (pdf_val, n_comps) = self.bxdfs.iter()
            .filter_map(|x| if x.matches(flags) { Some(x.pdf(&w_o, &w_i)) } else { None })
            .fold((0.0, 0), |(p, n), y| (p + y, n + 1));
        if n_comps > 0 {
            pdf_val / n_comps as f32
        } else {
            0.0
        }
    }
    /// Get the `i`th BxDF that matches the flags passed. There should not be fewer than i
    /// BxDFs that match the flags
    fn matching_at(&self, i: usize, flags: EnumSet<BxDFType>) -> &BxDF {
        let mut it = self.bxdfs.iter().filter(|x| x.matches(flags)).skip(i);
        match it.next() {
            Some(b) => *b,
            None => panic!("Out of bounds index for BxDF type {:?}", flags)
        }
    }
}

<commit_msg>small reformatting, hunting another bug w/ glass<commit_after>//! Defines the BSDF which acts as a container for composing the various BRDFs
//! and BTDFs that describe the surface's properties

use std::cmp;
use enum_set::EnumSet;

use linalg::{self, Normal, Vector, Point};
use film::Colorf;
use geometry::DifferentialGeometry;
use bxdf::{BxDF, BxDFType};
use sampler::Sample;

/// The BSDF contains the various BRDFs and BTDFs that describe the surface's properties
/// at some point. It also transforms incident and outgoing light directions into
/// shading space to make the BxDFs easier to implement.
/// TODO: We really need the memory pool. Each time we get the bsdf from a
/// material we need to allocate a decent amount of stuff since they each need
/// their own tangent, bitangent and differential geometry reference.
pub struct BSDF<'a> {
    /// The hit point
    pub p: Point,
    /// Shading normal, may be perturbed by bump mapping
    pub n: Normal,
    /// The actual geometry normal
    pub ng: Normal,
    /// Tangent vector for the surface
    pub tan: Vector,
    /// Bitangent vector for the surface
    pub bitan: Vector,
    /// Refractive index of the geometry
    pub eta: f32,
    bxdfs: &'a [&'a BxDF],
}

impl<'a> BSDF<'a> {
    /// Create a new BSDF using the BxDFs passed to shade the differential geometry with
    /// refractive index `eta`
    pub fn new<'b>(bxdfs: &'a [&'a BxDF], eta: f32, dg: &DifferentialGeometry<'b>) -> BSDF<'a> {
        let n = dg.n.normalized();
        let mut bitan = dg.dp_du.normalized();
        let tan = linalg::cross(&n, &bitan);
        bitan = linalg::cross(&tan, &n);
        BSDF { p: dg.p, n: n, ng: dg.ng, tan: tan, bitan: bitan, bxdfs: bxdfs, eta: eta }
    }
    /// Return the total number of BxDFs
    pub fn num_bxdfs(&self) -> usize { self.bxdfs.len() }
    /// Return the number of BxDFs matching the flags
    pub fn num_matching(&self, flags: EnumSet<BxDFType>) -> usize {
        self.bxdfs.iter().filter(|x| x.matches(flags)).count()
    }
    /// Transform the vector from world space to shading space
    pub fn to_shading(&self, v: &Vector) -> Vector {
        Vector::new(linalg::dot(v, &self.bitan), linalg::dot(v, &self.tan),
                    linalg::dot(v, &self.n))
    }
    /// Transform the vectro from shading space to world space
    pub fn from_shading(&self, v: &Vector) -> Vector {
        Vector::new(self.bitan.x * v.x + self.tan.x * v.y + self.n.x * v.z,
                    self.bitan.y * v.x + self.tan.y * v.y + self.n.y * v.z,
                    self.bitan.z * v.x + self.tan.z * v.y + self.n.z * v.z)
    }
    /// Evaluate the BSDF for the outgoing and incident light directions
    /// `w_o` and `w_i` in world space, sampling the desired subset of BxDFs
    /// selected by the flags passed. `wo_world` and `wi_world` should point from
    /// the hit point in the outgoing and incident light directions respectively.
    pub fn eval(&self, wo_world: &Vector, wi_world: &Vector, mut flags: EnumSet<BxDFType>) -> Colorf {
        let w_o = self.to_shading(wo_world).normalized();
        let w_i = self.to_shading(wi_world).normalized();
        // Determine if we should evaluate reflection or transmission based on the
        // geometry normal and the light directions
        if w_o.z * w_i.z > 0.0 {
            flags.remove(&BxDFType::Transmission);
        } else {
            flags.remove(&BxDFType::Reflection);
        }
        // Find all matching BxDFs and add their contribution to the material's color
        self.bxdfs.iter().filter_map(|x| if x.matches(flags) { Some(x.eval(&w_o, &w_i)) } else { None })
            .fold(Colorf::broadcast(0.0), |x, y| x + y)
    }
    /// Sample a component of the BSDF to get an incident light direction for light
    /// leaving the surface along `w_o`.
    /// `samples` are the 3 random values to use when sampling a component of the BSDF
    /// and a the chosen BSDF
    /// Returns the color, direction, pdf and the type of BxDF that was sampled.
    pub fn sample(&self, wo_world: &Vector, flags: EnumSet<BxDFType>, samples: &Sample)
        -> (Colorf, Vector, f32, EnumSet<BxDFType>)
    {
        let n_matching = self.num_matching(flags);
        if n_matching == 0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
        }
        let comp = cmp::min((samples.one_d * n_matching as f32) as usize, n_matching - 1);
        let bxdf = self.matching_at(comp, flags);
        let w_o = self.to_shading(wo_world).normalized();
        let (mut f, w_i, mut pdf) = bxdf.sample(&w_o, &samples.two_d);
        if w_i.length_sqr() == 0.0 {
            return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
        }
        let wi_world = self.from_shading(&w_i).normalized();

        // TODO: We re-use our functions but actually do a lot of redundant computation. I'm not
        // sure that the compiler will eliminate it. Should just copy in the code from pdf and eval
        if !bxdf.bxdf_type().contains(&BxDFType::Specular) && n_matching > 1 {
            pdf = self.pdf(wo_world, &wi_world, flags);
        }
        if n_matching > 1 {
            pdf /= n_matching as f32;
        }

        if !bxdf.bxdf_type().contains(&BxDFType::Specular) {
            f = self.eval(wo_world, &wi_world, flags);
        }
        (f, wi_world, pdf, bxdf.bxdf_type())
    }
    /// Compute the pdf for sampling the pair of incident and outgoing light directions for
    /// the BxDFs matching the flags set
    pub fn pdf(&self, wo_world: &Vector, wi_world: &Vector, flags: EnumSet<BxDFType>) -> f32 {
        let w_o = self.to_shading(wo_world).normalized();
        let w_i = self.to_shading(wi_world).normalized();
        let (pdf_val, n_comps) = self.bxdfs.iter()
            .filter_map(|x| if x.matches(flags) { Some(x.pdf(&w_o, &w_i)) } else { None })
            .fold((0.0, 0), |(p, n), y| (p + y, n + 1));
        if n_comps > 0 {
            pdf_val / n_comps as f32
        } else {
            0.0
        }
    }
    /// Get the `i`th BxDF that matches the flags passed. There should not be fewer than i
    /// BxDFs that match the flags
    fn matching_at(&self, i: usize, flags: EnumSet<BxDFType>) -> &BxDF {
        let mut it = self.bxdfs.iter().filter(|x| x.matches(flags)).skip(i);
        match it.next() {
            Some(b) => *b,
            None => panic!("Out of bounds index for BxDF type {:?}", flags)
        }
    }
}

<|endoftext|>


<commit_before>pub trait Merge: Ord + Copy {
    fn merge(self, other: Self) -> Option<Self>;
}

pub trait Coalesce<T: Ord + Copy + Merge> {
    fn coalesce(&mut self, index: usize, el: T);
}

impl<T: Ord + Copy + Merge> Coalesce<T> for Vec<T> {
    fn coalesce(&mut self, index: usize, el: T)  {
        let merge = T::merge;
        if self.is_empty() {
            self.insert(index, el);
        } else if index == 0 {
            if let Some(coalesced) = merge(el, self[0]) {
                self[index] = coalesced;
            } else {
                self.insert(index, el);
            }
        } else if index == self.len() {
            if let Some(coalesced) = merge(self[index - 1], el) {
                self[index - 1] = coalesced;
            } else {
                self.insert(index, el);
            }
        } else {
            if let Some(coalesced) = merge(self[index - 1], el) {
                self[index - 1] = coalesced;
                if let Some(coalesced) = merge(self[index - 1], self[index]) {
                    self[index - 1] = coalesced;
                    self.remove(index);
                }
            } else if let Some(coalesced) = merge(el, self[index]) {
                self[index ] = coalesced;
            } else {
                self.insert(index, el);
            }
        }
    }
}

macro_rules! impl_merge_tuples {
    ($tp:ident) => (
        impl Merge for ($tp, $tp) {
            fn merge(self, (after, y): ($tp, $tp))  -> Option<($tp, $tp)>
            {
                let (x, before) = self;
                if before >= after { Some((x, y)) } else { None }
            }
        }
    )
}

impl_merge_tuples!(isize);
impl_merge_tuples!(usize);
impl_merge_tuples!(u32);
impl_merge_tuples!(u16);
impl_merge_tuples!(u8);
impl_merge_tuples!(i32);
impl_merge_tuples!(i16);
impl_merge_tuples!(i8);

#[test]
fn test_coalesce_empty() {
    let mut v = vec![];
    v.coalesce(0, (0, 1));
    assert_eq!(v, [(0, 1)]);
}

#[test]
fn test_coalesce_first() {
    let mut v = vec![(1, 1)];
    v.coalesce(0, (0, 1));
    assert_eq!(v, [(0, 1)])
}

#[test]
fn test_coalesce_last() {
    let mut v = vec![(1, 1)];
    v.coalesce(1, (1, 2));
    assert_eq!(v, [(1, 2)])
}

#[test]
fn test_coalesce_both() {
    let mut v = vec![(1, 1), (2, 2)];
    v.coalesce(1, (1, 2));
    assert_eq!(v, [(1, 2)])
}

#[test]
fn test_coalesce_none() {
    let mut v = vec![(1, 1), (3, 3)];
    v.coalesce(1, (2, 2));
    assert_eq!(v, [(1, 1), (2, 2), (3, 3)])
}

#[test]
fn test_coalesce_twice() {
    let mut v = vec![];
    v.coalesce(0, (0, 1));
    v.coalesce(0, (-2, -1));
    v.coalesce(1, (-1, 0));
    assert_eq!(v, [(-2, 1)]);
}

#[test]
fn test_search_and_coalesce() {
    let mut v = vec![];
    for el in vec![(0, 1), (-2, -1), (-1, 0)] {
        let index = v.binary_search(&el).err().unwrap();
        v.coalesce(index, el);
    }
    assert_eq!(v, [(-2, 1)]);

}
<commit_msg>Bug fix in coalesce<commit_after>pub trait Merge: Ord + Copy {
    fn merge(self, other: Self) -> Option<Self>;
}

pub trait Coalesce<T: Ord + Copy + Merge> {
    fn coalesce(&mut self, index: usize, el: T);
}

impl<T: Ord + Copy + Merge> Coalesce<T> for Vec<T> {
    fn coalesce(&mut self, index: usize, el: T)  {
        let merge = T::merge;
        if self.is_empty() {
            self.insert(index, el);
        } else if index == 0 {
            if let Some(coalesced) = merge(el, self[0]) {
                self[index] = coalesced;
            } else {
                self.insert(index, el);
            }
        } else if index == self.len() {
            if let Some(coalesced) = merge(self[index - 1], el) {
                self[index - 1] = coalesced;
            } else {
                self.insert(index, el);
            }
        } else {
            if let Some(coalesced) = merge(self[index - 1], el) {
                self[index - 1] = coalesced;
                if let Some(coalesced) = merge(self[index - 1], self[index]) {
                    self[index - 1] = coalesced;
                    self.remove(index);
                }
            } else if let Some(coalesced) = merge(el, self[index]) {
                self[index ] = coalesced;
            } else {
                self.insert(index, el);
            }
        }
    }
}

macro_rules! impl_merge_tuples {
    ($tp:ident) => (
        impl Merge for ($tp, $tp) {
            fn merge(self, (x2, y2): ($tp, $tp))  -> Option<($tp, $tp)> {
                let (x1, y1) = self;
                if y1 >= x2 {
                    if y1 < y2 { Some((x1, y2)) } else { Some((x1, y1)) }
                } else {
                    None
                }
            }
        }
    )
}

impl_merge_tuples!(isize);
impl_merge_tuples!(usize);
impl_merge_tuples!(u32);
impl_merge_tuples!(u16);
impl_merge_tuples!(u8);
impl_merge_tuples!(i32);
impl_merge_tuples!(i16);
impl_merge_tuples!(i8);

#[test]
fn test_coalesce_empty() {
    let mut v = vec![];
    v.coalesce(0, (0, 1));
    assert_eq!(v, [(0, 1)]);
}

#[test]
fn test_coalesce_first() {
    let mut v = vec![(1, 1)];
    v.coalesce(0, (0, 1));
    assert_eq!(v, [(0, 1)])
}

#[test]
fn test_coalesce_last() {
    let mut v = vec![(1, 1)];
    v.coalesce(1, (1, 2));
    assert_eq!(v, [(1, 2)])
}

#[test]
fn test_coalesce_both() {
    let mut v = vec![(1, 1), (2, 2)];
    v.coalesce(1, (1, 2));
    assert_eq!(v, [(1, 2)])
}

#[test]
fn test_coalesce_none() {
    let mut v = vec![(1, 1), (3, 3)];
    v.coalesce(1, (2, 2));
    assert_eq!(v, [(1, 1), (2, 2), (3, 3)])
}

#[test]
fn test_coalesce_twice() {
    let mut v = vec![];
    v.coalesce(0, (0, 1));
    v.coalesce(0, (-2, -1));
    v.coalesce(1, (-1, 0));
    assert_eq!(v, [(-2, 1)]);
}

#[test]
fn test_search_and_coalesce() {
    let mut v = vec![];
    for el in vec![(0, 1), (-2, -1), (-1, 0)] {
        let index = v.binary_search(&el).err().unwrap();
        v.coalesce(index, el);
    }
    assert_eq!(v, [(-2, 1)]);
}

#[test]
fn test_coalesce_subrange() {
    let mut v = vec![(0, 3)];
    v.coalesce(1, (1, 2));
    assert_eq!(v, [(0, 3)]);
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{Read, Write};
use std::fs;
use std::fs::{DirBuilder, File};
use std::path::Path;
use std::error::Error;
use super::generation::PageGenerator;
use super::config::Config;
use hyper::server::{Request, Response, Server};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use hyper::method::Method;
use walkdir::WalkDir;
use pulldown_cmark::{Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};

const DEFAULT_CONFIG_FILE: &'static str = "\
source: pages
output: _site
port: 4000
markdown_options:
  - tables
  - footnotes
";

const BAD_REQUEST: &'static str = "\

400 Bad Request

";

const NOT_FOUND: &'static str = "\

404 Not Found

";

pub fn new_project(parent_dir: &str) -> Result<(), io::Error> {
    DirBuilder::new().recursive(true).create(parent_dir)?;

    DirBuilder::new().recursive(false).create(format!("{}/pages", parent_dir))?;

    let mut config_file = File::create(format!("{}/_config.yml", parent_dir))?;

    config_file.write_all(DEFAULT_CONFIG_FILE.as_bytes())?;

    Ok(())
}

pub fn build_project(config: &Config) -> Result<(), io::Error> {
    let pages_path = &*config.source_dir;
    let output_dir = &*config.output_dir;
    let mut markdown_options = Options::empty();

    if config.markdown_options.contains(&"footnotes".to_string()) {
        markdown_options.insert(OPTION_ENABLE_FOOTNOTES);
    }

    if config.markdown_options.contains(&"tables".to_string()) {
        markdown_options.insert(OPTION_ENABLE_TABLES);
    }

    let mut page_generator = PageGenerator::new();

    let directory_iterator = WalkDir::new(pages_path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file() && !e.file_name().to_str().unwrap().starts_with("_"));

    if !Path::new(output_dir).exists() {
        DirBuilder::new().create(output_dir)?;
    }

    for file in directory_iterator {
        // Name of the file?
        let file_name = file.file_name().to_str().unwrap().to_string();

        // File stem of thing.md -> thing
        let file_stem = file.path().file_stem().expect("File Stem").to_string_lossy().into_owned();

        let destination_file = format!("{}/{}", output_dir, file
            .path()
            .strip_prefix(pages_path)
            .unwrap()
            .with_extension("html")
            .display()
        );

        fs::create_dir_all(Path::new(&destination_file).parent().unwrap());

        if file_name.contains(".md") {
            page_generator.set_input_file(file.path().to_str().expect("Couldn't convert for some reason"))
                .set_output_file(destination_file.as_str())
                .set_wrap(true)
                .set_parse_options(markdown_options.clone())
                .generate()?;
        } else if file_name.contains(".html") {
            let output_file_name = format!("{}/{}", config.output_dir, file_name);
            let output_file_path = Path::new(&output_file_name);

            let output_file = File::create(Path::new(&output_file_path))?;

            fs::copy(file.path(), output_file_path);
        }
    }

    Ok(())
}

pub fn clean_project(config: &Config) -> Result<(), io::Error> {
    fs::remove_dir_all(&*config.output_dir)?;

    Ok(())
}

pub fn serve(config: &Config) -> Result<(), io::Error> {
    build_project(&config)?;

    let server_addr = format!("127.0.0.1:{}", &*config.port);
    let server = match Server::http(server_addr.as_str()) {
        Ok(server) => server,
        Err(what) => panic!("{}", Error::description(&what))
    };

    let serve_dir = config.output_dir.clone();
    match server.handle(move |request: Request, response: Response| {
        match handle_static_file(&serve_dir, request, response) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }) {
        Ok(_) => {},
        Err(what) => panic!("{}", Error::description(&what))
    }

    Ok(())
}

fn handle_static_file(page_dir: &str, request: Request, mut response: Response) -> Result<(), io::Error> {
    let path = match request.uri {
        RequestUri::AbsolutePath(ref uri) if request.method == Method::Get => uri,
        _ => {
            *response.status_mut() = StatusCode::BadRequest;
            let body = BAD_REQUEST.as_bytes();
            response.send(body)?;
            return Ok(())
        }
    };

    let file_path = Path::new(page_dir).join(&path[1..]);

    if file_path.exists() && file_path.is_file() {
        let mut file = File::open(file_path)?;
        let mut file_contents = String::new();

        file.read_to_string(&mut file_contents)?;

        *response.status_mut() = StatusCode::Ok;
        response.send(&file_contents.into_bytes())?;
        return Ok(())
    } else {
        *response.status_mut() = StatusCode::NotFound;
        let body = NOT_FOUND.as_bytes();
        response.send(body)?;
        return Ok(())
    }
}
<commit_msg>Remove code that cause warnings when running tests<commit_after>use std::io;
use std::io::{Read, Write};
use std::fs;
use std::fs::{DirBuilder, File};
use std::path::Path;
use std::error::Error;
use super::generation::PageGenerator;
use super::config::Config;
use hyper::server::{Request, Response, Server};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use hyper::method::Method;
use walkdir::WalkDir;
use pulldown_cmark::{Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};

const DEFAULT_CONFIG_FILE: &'static str = "\
source: pages
output: _site
port: 4000
markdown_options:
  - tables
  - footnotes
";

const BAD_REQUEST: &'static str = "\

400 Bad Request

";

const NOT_FOUND: &'static str = "\

404 Not Found

";

pub fn new_project(parent_dir: &str) -> Result<(), io::Error> {
    DirBuilder::new().recursive(true).create(parent_dir)?;

    DirBuilder::new().recursive(false).create(format!("{}/pages", parent_dir))?;

    let mut config_file = File::create(format!("{}/_config.yml", parent_dir))?;

    config_file.write_all(DEFAULT_CONFIG_FILE.as_bytes())?;

    Ok(())
}

pub fn build_project(config: &Config) -> Result<(), io::Error> {
    let pages_path = &*config.source_dir;
    let output_dir = &*config.output_dir;
    let mut markdown_options = Options::empty();

    if config.markdown_options.contains(&"footnotes".to_string()) {
        markdown_options.insert(OPTION_ENABLE_FOOTNOTES);
    }

    if config.markdown_options.contains(&"tables".to_string()) {
        markdown_options.insert(OPTION_ENABLE_TABLES);
    }

    let mut page_generator = PageGenerator::new();

    let directory_iterator = WalkDir::new(pages_path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file() && !e.file_name().to_str().unwrap().starts_with("_"));

    if !Path::new(output_dir).exists() {
        DirBuilder::new().create(output_dir)?;
    }

    for file in directory_iterator {
        // Name of the file?
        let file_name = file.file_name().to_str().unwrap().to_string();

        let destination_file = format!("{}/{}", output_dir, file
            .path()
            .strip_prefix(pages_path)
            .unwrap()
            .with_extension("html")
            .display()
        );

        fs::create_dir_all(Path::new(&destination_file).parent().unwrap()).unwrap();

        if file_name.contains(".md") {
            page_generator.set_input_file(file.path().to_str().expect("Couldn't convert for some reason"))
                .set_output_file(destination_file.as_str())
                .set_wrap(true)
                .set_parse_options(markdown_options.clone())
                .generate()?;
        } else if file_name.contains(".html") {
            let output_file_name = format!("{}/{}", config.output_dir, file_name);
            let output_file_path = Path::new(&output_file_name);

            fs::copy(file.path(), output_file_path).unwrap();
        }
    }

    Ok(())
}

pub fn clean_project(config: &Config) -> Result<(), io::Error> {
    fs::remove_dir_all(&*config.output_dir)?;

    Ok(())
}

pub fn serve(config: &Config) -> Result<(), io::Error> {
    build_project(&config)?;

    let server_addr = format!("127.0.0.1:{}", &*config.port);
    let server = match Server::http(server_addr.as_str()) {
        Ok(server) => server,
        Err(what) => panic!("{}", Error::description(&what))
    };

    let serve_dir = config.output_dir.clone();
    match server.handle(move |request: Request, response: Response| {
        match handle_static_file(&serve_dir, request, response) {
            Ok(_) => {},
            Err(what) => panic!("{}", Error::description(&what))
        }
    }) {
        Ok(_) => {},
        Err(what) => panic!("{}", Error::description(&what))
    }

    Ok(())
}

fn handle_static_file(page_dir: &str, request: Request, mut response: Response) -> Result<(), io::Error> {
    let path = match request.uri {
        RequestUri::AbsolutePath(ref uri) if request.method == Method::Get => uri,
        _ => {
            *response.status_mut() = StatusCode::BadRequest;
            let body = BAD_REQUEST.as_bytes();
            response.send(body)?;
            return Ok(())
        }
    };

    let file_path = Path::new(page_dir).join(&path[1..]);

    if file_path.exists() && file_path.is_file() {
        let mut file = File::open(file_path)?;
        let mut file_contents = String::new();

        file.read_to_string(&mut file_contents)?;

        *response.status_mut() = StatusCode::Ok;
        response.send(&file_contents.into_bytes())?;
        return Ok(())
    } else {
        *response.status_mut() = StatusCode::NotFound;
        let body = NOT_FOUND.as_bytes();
        response.send(body)?;
        return Ok(())
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::rc::{Rc, Weak};
use entity::{Vec3f64, Entity};

struct Address {
    x: u64,
    y: u64,
    z: u64,
    depth: u8,
}

struct Element {
    name: String,
}

pub enum ChunkOption {
    Local(Rc<LocalChunk>),
    Remote(Rc<RemoteChunk>)
}

pub enum ChildOption {
    Local(Rc<LocalChunk>),
    Remote(Rc<RemoteChunk>),
    None
}

pub enum ParentOption {
    Local(Weak<LocalChunk>),
    Remote(Rc<RemoteChunk>),
    None
}

pub struct LocalChunk {
    address: Address,
    center: Vec3f64,
    children: [ChildOption; 8],
    child_number: Option<u8>,
    entities: Vec<Rc<Entity>>,
    mass: f64,
    parent: ParentOption,
    scale: u8,
    structure: u64,
    summary: Rc<Element>,
}

pub struct RemoteChunk {
    connection: i32,
}

impl Address {
    fn from_string(address: String) -> Option<Address> {
        let mut xyz: (u64, u64, u64) = (1, 1, 1);
        let mut xyz_add: (u64, u64, u64);

        for (i, c) in address.chars().enumerate() {
            if i > 63 {
                break;
            }
            xyz_add =  match c.to_digit(8) {
                Some(0) => (0, 0, 0),
                Some(1) => (0, 0, 1),
                Some(2) => (0, 1, 0),
                Some(3) => (0, 1, 1),
                Some(4) => (1, 0, 0),
                Some(5) => (1, 0, 1),
                Some(6) => (1, 1, 0),
                Some(7) => (1, 1, 1),
                _ => return None,
            };
            xyz = (xyz.0 << 1, xyz.1 << 1, xyz.2 << 1);
            xyz = (xyz.0 + xyz_add.0, xyz.1 + xyz_add.1, xyz.2 + xyz_add.2);
        }
        Some(Address {x: xyz.0, y: xyz.1, z: xyz.2})
    }
}

impl ToString for Address {
    fn to_string(&self) -> String {
        String::from_str("")
    }
}

impl PartialEq for Address {
    fn eq(&self, other: &Self) -> bool {
        (((self.x == other.x) && (self.y == other.y)) && (self.z == other.z))
    }
}

pub trait Chunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption;
    fn tick(&self, time_delta: f64);
}

impl Chunk for LocalChunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption {

        let index = match (x, y, z) {
            (0, 0, 0) => Some(0),
            (0, 0, 1) => Some(1),
            (0, 1, 0) => Some(2),
            (0, 1, 1) => Some(3),
            (1, 0, 0) => Some(4),
            (1, 0, 1) => Some(5),
            (1, 1, 0) => Some(6),
            (1, 1, 1) => Some(7),
            _ => None,
        };

        match index {
            Some(i) => match self.children[i] {
                ChildOption::Local(ref chunk) => ChildOption::Local(chunk.clone()),
                ChildOption::Remote(ref chunk) => ChildOption::Remote(chunk.clone()),
                ChildOption::None => ChildOption::None
            },
            None => ChildOption::None
        }
    }

    fn tick(&self, time_delta: f64) {
    }
}

impl Chunk for RemoteChunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption {
        ChildOption::None
    }
    fn tick (&self, time_delta: f64) {
    }
}

<commit_msg>Add index<commit_after>#![allow(dead_code)]

use std::rc::{Rc, Weak};
use entity::{Vec3f64, Entity};

struct Address {
    x: u64,
    y: u64,
    z: u64,
    depth: u8,
}

struct Element {
    name: String,
}

pub enum ChunkOption {
    Local(Rc<LocalChunk>),
    Remote(Rc<RemoteChunk>)
}

pub enum ChildOption {
    Local(Rc<LocalChunk>),
    Remote(Rc<RemoteChunk>),
    None
}

pub enum ParentOption {
    Local(Weak<LocalChunk>),
    Remote(Rc<RemoteChunk>),
    None
}

pub struct LocalChunk {
    address: Address,
    center: Vec3f64,
    children: [ChildOption; 8],
    child_number: Option<u8>,
    entities: Vec<Rc<Entity>>,
    mass: f64,
    parent: ParentOption,
    scale: u8,
    structure: u64,
    summary: Rc<Element>,
}

pub struct RemoteChunk {
    connection: i32,
}

impl Address {
    fn from_string(address: String) -> Option<Address> {
        let mut xyz: (u64, u64, u64) = (1, 1, 1);
        let mut xyz_add: (u64, u64, u64);
        let mut index: u8;

        for (iedex, character) in address.chars().enumerate() {
            if index > 63 {
                index = 63
                break;
            }
            xyz_add =  match character.to_digit(8) {
                Some(0) => (0, 0, 0),
                Some(1) => (0, 0, 1),
                Some(2) => (0, 1, 0),
                Some(3) => (0, 1, 1),
                Some(4) => (1, 0, 0),
                Some(5) => (1, 0, 1),
                Some(6) => (1, 1, 0),
                Some(7) => (1, 1, 1),
                _ => return None,
            };
            xyz = (xyz.0 << 1, xyz.1 << 1, xyz.2 << 1);
            xyz = (xyz.0 + xyz_add.0, xyz.1 + xyz_add.1, xyz.2 + xyz_add.2);
        }
        Some(Address {x: xyz.0, y: xyz.1, z: xyz.2, depth: index})
    }
}

impl ToString for Address {
    fn to_string(&self) -> String {
        String::from_str("")
    }
}

impl PartialEq for Address {
    fn eq(&self, other: &Self) -> bool {
        (((self.x == other.x) && (self.y == other.y)) && (self.z == other.z))
    }
}

pub trait Chunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption;
    fn tick(&self, time_delta: f64);
}

impl Chunk for LocalChunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption {

        let index = match (x, y, z) {
            (0, 0, 0) => Some(0),
            (0, 0, 1) => Some(1),
            (0, 1, 0) => Some(2),
            (0, 1, 1) => Some(3),
            (1, 0, 0) => Some(4),
            (1, 0, 1) => Some(5),
            (1, 1, 0) => Some(6),
            (1, 1, 1) => Some(7),
            _ => None,
        };

        match index {
            Some(i) => match self.children[i] {
                ChildOption::Local(ref chunk) => ChildOption::Local(chunk.clone()),
                ChildOption::Remote(ref chunk) => ChildOption::Remote(chunk.clone()),
                ChildOption::None => ChildOption::None
            },
            None => ChildOption::None
        }
    }

    fn tick(&self, time_delta: f64) {
    }
}

impl Chunk for RemoteChunk {
    fn get_child(&self, x: u8, y: u8, z: u8) -> ChildOption {
        ChildOption::None
    }
    fn tick (&self, time_delta: f64) {
    }
}

<|endoftext|>


<commit_before>use super::{LzfError, LzfResult};
use std::cmp;

const HLOG: usize = 16;
const HSIZE: u32 = 1 << HLOG;
const MAX_OFF: usize = 1 << 13;
const MAX_REF: usize = (1 << 8) + (1 << 3);
const MAX_LIT: i32 = 1 << 5;

fn first(p: &[u8], off: usize) -> u32 {
    ((p[off] as u32) << 8) | p[off + 1] as u32
}

fn next(v: u32, p: &[u8], off: usize) -> u32 {
    (v << 8) | p[off + 2] as u32
}

fn idx(h: u32) -> usize {
    let h = h as u64;
    (
        // 8 = 3*8-HLOG, but HLOG is constant at 16
        (h.wrapping_shr(8).wrapping_sub(h * 5)) & (HSIZE - 1) as u64
    ) as usize
}

fn not(i: i32) -> i32 {
    if i == 0 {
        1
    } else {
        0
    }
}

/// Compress the given data, if possible.
/// The return value will be set to the error if compression fails.
///
/// The buffer is always set to the same size as the input buffer.
/// If that is not enough to hold the lzf-compressed data,
/// an error will be returned.
///
/// Example:
///
/// ```rust
/// let data = "aaaaaaaaa";
/// let compressed = lzf::compress(data.as_bytes()).unwrap();
/// ```
pub fn compress(data: &[u8]) -> LzfResult<Vec<u8>> {
    let in_len = data.len();
    let out_buf_len = in_len;
    let mut out = vec![0; out_buf_len];

    let mut out_len: i32 = 1; /* start run by default */

    let mut htab = vec![0; 1 << HLOG];

    let mut current_offset = 0;

    if in_len < 2 {
        return Err(LzfError::NoCompressionPossible);
    }

    let mut lit: i32 = 0;

    let mut hval: u32;
    let mut ref_offset;

    hval = first(data, current_offset);

    while current_offset < in_len - 2 {
        hval = next(hval, data, current_offset);
        let hslot_idx = idx(hval);

        ref_offset = htab[hslot_idx];
        htab[hslot_idx] = current_offset;

        let off = current_offset.wrapping_sub(ref_offset).wrapping_sub(1);
        if off < MAX_OFF
            && current_offset + 4 < in_len
            && ref_offset > 0
            && ref_offset < in_len - 2
            && data[ref_offset] == data[current_offset]
            && data[ref_offset + 1] == data[current_offset + 1]
            && data[ref_offset + 2] == data[current_offset + 2]
        {
            let mut len = 2;
            let maxlen = cmp::min(in_len - current_offset - len, MAX_REF);

            /* stop run */
            out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
            out_len -= not(lit); /* undo run if length is zero */

            if out_len as i32 + 3 + 1 >= out_buf_len as i32 {
                return Err(LzfError::NoCompressionPossible);
            }

            loop {
                len += 1;
                while len < maxlen && data[ref_offset + len] == data[current_offset + len] {
                    len += 1;
                }
                break;
            }

            len -= 2; /* len is now #octets - 1 */
            current_offset += 1;

            if len < 7 {
                out[out_len as usize] = (off >> 8) as u8 + (len << 5) as u8;
                out_len += 1;
            } else {
                out[out_len as usize] = (off >> 8) as u8 + (7 << 5);
                out[out_len as usize + 1] = (len as u8).wrapping_sub(7);
                out_len += 2;
            }

            out[out_len as usize] = off as u8;
            out_len += 2; /* start run */
            lit = 0;

            /* we add here, because we later substract from the total length */
            current_offset += len - 1;

            if current_offset >= in_len {
                break;
            }

            hval = first(data, current_offset);

            hval = next(hval, data, current_offset);
            htab[idx(hval)] = current_offset;
            current_offset += 1;

            hval = next(hval, data, current_offset);
            htab[idx(hval)] = current_offset;
            current_offset += 1;
        } else {
            /* one more literal byte we must copy */
            if out_len >= out_buf_len as i32 {
                return Err(LzfError::NoCompressionPossible);
            }

            lit += 1;
            out[out_len as usize] = data[current_offset];
            out_len += 1;
            current_offset += 1;

            if lit == MAX_LIT {
                /* stop run */
                out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
                lit = 0;
                out_len += 1; /* start run */
            }
        }
    }

    /* at most 3 bytes can be missing here */
    if out_len + 3 > out_buf_len as i32 {
        return Err(LzfError::NoCompressionPossible);
    }

    while current_offset < in_len {
        lit += 1;
        out[out_len as usize] = data[current_offset];
        out_len += 1;
        current_offset += 1;

        if lit == MAX_LIT {
            /* stop run */
            out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
            lit = 0;
            out_len += 1; /* start run */
        }
    }

    /* end run */
    out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
    out_len -= not(lit); /* undo run if length is zero */

    unsafe { out.set_len(out_len as usize) };

    Ok(out)
}

#[test]
fn test_compress_skips_short() {
    match compress("foo".as_bytes()) {
        Ok(_) => panic!("Compression did _something_, which is wrong for 'foo'"),
        Err(err) => assert_eq!(LzfError::NoCompressionPossible, err),
    }
}

#[test]
fn test_compress_lorem() {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    match compress(lorem.as_bytes()) {
        Ok(compressed) => {
            assert_eq!(272, compressed.len())
        }
        Err(err) => panic!("Compression failed with error {:?}", err),
    }
}

#[test]
fn test_compress_decompress_lorem_round() {
    use super::decompress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = match compress(lorem.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    match decompress(&compressed, lorem.len()) {
        Ok(decompressed) => {
            assert_eq!(lorem.len(), decompressed.len());
            assert_eq!(lorem.as_bytes(), &decompressed[..]);
        }
        Err(err) => panic!("Decompression failed with error {:?}", err),
    };
}

#[test]
fn test_alice_wonderland_both() {
    let alice = "\r\n\r\n\r\n\r\n                ALICE'S ADVENTURES IN WONDERLAND\r\n";

    let compressed = match compress(alice.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    let c_compressed = match super::compress(alice.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    assert_eq!(&compressed[..], &c_compressed[..]);
}

#[test]
fn quickcheck_found_bug() {
    let inp = vec![
        0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 0, 5, 0,
        0, 6, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 10, 0, 0, 11, 0, 1, 5, 0, 1, 6, 0, 1, 7, 0, 1, 8, 0,
        1, 9, 0, 1, 10, 0, 0,
    ];

    assert_eq!(LzfError::NoCompressionPossible, compress(&inp).unwrap_err());
}

#[test]
fn quickcheck_found_bug2() {
    let inp = vec![0];

    assert_eq!(LzfError::NoCompressionPossible, compress(&inp).unwrap_err());
}
<commit_msg>fix: Remove useless loop<commit_after>use super::{LzfError, LzfResult};
use std::cmp;

const HLOG: usize = 16;
const HSIZE: u32 = 1 << HLOG;
const MAX_OFF: usize = 1 << 13;
const MAX_REF: usize = (1 << 8) + (1 << 3);
const MAX_LIT: i32 = 1 << 5;

fn first(p: &[u8], off: usize) -> u32 {
    ((p[off] as u32) << 8) | p[off + 1] as u32
}

fn next(v: u32, p: &[u8], off: usize) -> u32 {
    (v << 8) | p[off + 2] as u32
}

fn idx(h: u32) -> usize {
    let h = h as u64;
    (
        // 8 = 3*8-HLOG, but HLOG is constant at 16
        (h.wrapping_shr(8).wrapping_sub(h * 5)) & (HSIZE - 1) as u64
    ) as usize
}

fn not(i: i32) -> i32 {
    if i == 0 {
        1
    } else {
        0
    }
}

/// Compress the given data, if possible.
/// The return value will be set to the error if compression fails.
///
/// The buffer is always set to the same size as the input buffer.
/// If that is not enough to hold the lzf-compressed data,
/// an error will be returned.
///
/// Example:
///
/// ```rust
/// let data = "aaaaaaaaa";
/// let compressed = lzf::compress(data.as_bytes()).unwrap();
/// ```
pub fn compress(data: &[u8]) -> LzfResult<Vec<u8>> {
    let in_len = data.len();
    let out_buf_len = in_len;
    let mut out = vec![0; out_buf_len];

    let mut out_len: i32 = 1; /* start run by default */

    let mut htab = vec![0; 1 << HLOG];

    let mut current_offset = 0;

    if in_len < 2 {
        return Err(LzfError::NoCompressionPossible);
    }

    let mut lit: i32 = 0;

    let mut hval: u32;
    let mut ref_offset;

    hval = first(data, current_offset);

    while current_offset < in_len - 2 {
        hval = next(hval, data, current_offset);
        let hslot_idx = idx(hval);

        ref_offset = htab[hslot_idx];
        htab[hslot_idx] = current_offset;

        let off = current_offset.wrapping_sub(ref_offset).wrapping_sub(1);
        if off < MAX_OFF
            && current_offset + 4 < in_len
            && ref_offset > 0
            && ref_offset < in_len - 2
            && data[ref_offset] == data[current_offset]
            && data[ref_offset + 1] == data[current_offset + 1]
            && data[ref_offset + 2] == data[current_offset + 2]
        {
            let mut len = 2;
            let maxlen = cmp::min(in_len - current_offset - len, MAX_REF);

            /* stop run */
            out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
            out_len -= not(lit); /* undo run if length is zero */

            if out_len as i32 + 3 + 1 >= out_buf_len as i32 {
                return Err(LzfError::NoCompressionPossible);
            }

            len += 1;
            while len < maxlen && data[ref_offset + len] == data[current_offset + len] {
                len += 1;
            }

            len -= 2; /* len is now #octets - 1 */
            current_offset += 1;

            if len < 7 {
                out[out_len as usize] = (off >> 8) as u8 + (len << 5) as u8;
                out_len += 1;
            } else {
                out[out_len as usize] = (off >> 8) as u8 + (7 << 5);
                out[out_len as usize + 1] = (len as u8).wrapping_sub(7);
                out_len += 2;
            }

            out[out_len as usize] = off as u8;
            out_len += 2; /* start run */
            lit = 0;

            /* we add here, because we later substract from the total length */
            current_offset += len - 1;

            if current_offset >= in_len {
                break;
            }

            hval = first(data, current_offset);

            hval = next(hval, data, current_offset);
            htab[idx(hval)] = current_offset;
            current_offset += 1;

            hval = next(hval, data, current_offset);
            htab[idx(hval)] = current_offset;
            current_offset += 1;
        } else {
            /* one more literal byte we must copy */
            if out_len >= out_buf_len as i32 {
                return Err(LzfError::NoCompressionPossible);
            }

            lit += 1;
            out[out_len as usize] = data[current_offset];
            out_len += 1;
            current_offset += 1;

            if lit == MAX_LIT {
                /* stop run */
                out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
                lit = 0;
                out_len += 1; /* start run */
            }
        }
    }

    /* at most 3 bytes can be missing here */
    if out_len + 3 > out_buf_len as i32 {
        return Err(LzfError::NoCompressionPossible);
    }

    while current_offset < in_len {
        lit += 1;
        out[out_len as usize] = data[current_offset];
        out_len += 1;
        current_offset += 1;

        if lit == MAX_LIT {
            /* stop run */
            out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
            lit = 0;
            out_len += 1; /* start run */
        }
    }

    /* end run */
    out[(out_len - lit - 1) as usize] = (lit as u8).wrapping_sub(1);
    out_len -= not(lit); /* undo run if length is zero */

    unsafe { out.set_len(out_len as usize) };

    Ok(out)
}

#[test]
fn test_compress_skips_short() {
    match compress("foo".as_bytes()) {
        Ok(_) => panic!("Compression did _something_, which is wrong for 'foo'"),
        Err(err) => assert_eq!(LzfError::NoCompressionPossible, err),
    }
}

#[test]
fn test_compress_lorem() {
    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    match compress(lorem.as_bytes()) {
        Ok(compressed) => {
            assert_eq!(272, compressed.len())
        }
        Err(err) => panic!("Compression failed with error {:?}", err),
    }
}

#[test]
fn test_compress_decompress_lorem_round() {
    use super::decompress;

    let lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod \
                 tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At \
                 vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, \
                 no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit \
                 amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut \
                 labore et dolore magna aliquyam erat, sed diam voluptua.";

    let compressed = match compress(lorem.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    match decompress(&compressed, lorem.len()) {
        Ok(decompressed) => {
            assert_eq!(lorem.len(), decompressed.len());
            assert_eq!(lorem.as_bytes(), &decompressed[..]);
        }
        Err(err) => panic!("Decompression failed with error {:?}", err),
    };
}

#[test]
fn test_alice_wonderland_both() {
    let alice = "\r\n\r\n\r\n\r\n                ALICE'S ADVENTURES IN WONDERLAND\r\n";

    let compressed = match compress(alice.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    let c_compressed = match super::compress(alice.as_bytes()) {
        Ok(c) => c,
        Err(err) => panic!("Compression failed with error {:?}", err),
    };

    assert_eq!(&compressed[..], &c_compressed[..]);
}

#[test]
fn quickcheck_found_bug() {
    let inp = vec![
        0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 0, 5, 0,
        0, 6, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 0, 10, 0, 0, 11, 0, 1, 5, 0, 1, 6, 0, 1, 7, 0, 1, 8, 0,
        1, 9, 0, 1, 10, 0, 0,
    ];

    assert_eq!(LzfError::NoCompressionPossible, compress(&inp).unwrap_err());
}

#[test]
fn quickcheck_found_bug2() {
    let inp = vec![0];

    assert_eq!(LzfError::NoCompressionPossible, compress(&inp).unwrap_err());
}
<|endoftext|>


<commit_before>use crate::{get_book_dir, open};
use clap::{App, ArgMatches, SubCommand};
use mdbook::errors::Result;
use mdbook::utils;
use mdbook::MDBook;
use notify::Watcher;
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::thread::sleep;
use std::time::Duration;

// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name("watch")
        .about("Watches a book's files and rebuilds it on changes")
        .arg_from_usage(
            "-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
             Relative paths are interpreted relative to the book's root directory.{n}\
             If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
        )
        .arg_from_usage(
            "[dir] 'Root directory for the book{n}\
             (Defaults to the Current Directory when omitted)'",
        )
        .arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
}

// Watch command implementation
pub fn execute(args: &ArgMatches) -> Result<()> {
    let book_dir = get_book_dir(args);
    let book = MDBook::load(&book_dir)?;

    if args.is_present("open") {
        book.build()?;
        open(book.build_dir_for("html").join("index.html"));
    }

    trigger_on_change(&book, |paths, book_dir| {
        info!("Files changed: {:?}\nBuilding book...\n", paths);
        let result = MDBook::load(&book_dir).and_then(|b| b.build());

        if let Err(e) = result {
            error!("Unable to build the book");
            utils::log_backtrace(&e);
        }
    });

    Ok(())
}

fn remove_ignored_files(book_root: &PathBuf, paths: &[PathBuf]) -> Vec<PathBuf> {
    if paths.is_empty() {
        return vec![];
    }

    let gitignore_path = book_root.with_file_name(".gitignore");

    match gitignore::File::new(gitignore_path.as_path()) {
        Ok(exclusion_checker) => paths
            .iter()
            .filter(|path| match exclusion_checker.is_excluded(path) {
                Ok(exclude) => !exclude,
                Err(error) => {
                    warn!(
                        "Unable to determine if {:?} is excluded: {:?}. Including it.",
                        &path, error
                    );
                    true
                }
            })
            .map(|path| path.to_path_buf())
            .collect(),
        Err(error) => {
            warn!(
                "Unable to read gitignore file at {:?} file: {:?}. All files will be allowed.",
                gitignore_path, error
            );
            paths.iter().map(|path| path.to_path_buf()).collect()
        }
    }
}

/// Calls the closure when a book source file is changed, blocking indefinitely.
pub fn trigger_on_change<F>(book: &MDBook, closure: F)
where
    F: Fn(Vec<PathBuf>, &Path),
{
    use notify::DebouncedEvent::*;
    use notify::RecursiveMode::*;

    // Create a channel to receive the events.
    let (tx, rx) = channel();

    let mut watcher = match notify::watcher(tx, Duration::from_secs(1)) {
        Ok(w) => w,
        Err(e) => {
            error!("Error while trying to watch the files:\n\n\t{:?}", e);
            std::process::exit(1)
        }
    };

    // Add the source directory to the watcher
    if let Err(e) = watcher.watch(book.source_dir(), Recursive) {
        error!("Error while watching {:?}:\n    {:?}", book.source_dir(), e);
        std::process::exit(1);
    };

    let _ = watcher.watch(book.theme_dir(), Recursive);

    // Add the book.toml file to the watcher if it exists
    let _ = watcher.watch(book.root.join("book.toml"), NonRecursive);

    info!("Listening for changes...");

    loop {
        let first_event = rx.recv().unwrap();
        sleep(Duration::from_millis(50));
        let other_events = rx.try_iter();

        let all_events = std::iter::once(first_event).chain(other_events);

        let paths = all_events
            .filter_map(|event| {
                debug!("Received filesystem event: {:?}", event);

                match event {
                    Create(path) | Write(path) | Remove(path) | Rename(_, path) => Some(path),
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        let paths = remove_ignored_files(&book.root, &paths[..]);

        if !paths.is_empty() {
            closure(paths, &book.root);
        }
    }
}
<commit_msg>This fixes a small bug where the gitignore location can be misinterpreted to be in the folder "above" where it actually is.<commit_after>use crate::{get_book_dir, open};
use clap::{App, ArgMatches, SubCommand};
use mdbook::errors::Result;
use mdbook::utils;
use mdbook::MDBook;
use notify::Watcher;
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::thread::sleep;
use std::time::Duration;

// Create clap subcommand arguments
pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
    SubCommand::with_name("watch")
        .about("Watches a book's files and rebuilds it on changes")
        .arg_from_usage(
            "-d, --dest-dir=[dest-dir] 'Output directory for the book{n}\
             Relative paths are interpreted relative to the book's root directory.{n}\
             If omitted, mdBook uses build.build-dir from book.toml or defaults to `./book`.'",
        )
        .arg_from_usage(
            "[dir] 'Root directory for the book{n}\
             (Defaults to the Current Directory when omitted)'",
        )
        .arg_from_usage("-o, --open 'Open the compiled book in a web browser'")
}

// Watch command implementation
pub fn execute(args: &ArgMatches) -> Result<()> {
    let book_dir = get_book_dir(args);
    let book = MDBook::load(&book_dir)?;

    if args.is_present("open") {
        book.build()?;
        open(book.build_dir_for("html").join("index.html"));
    }

    trigger_on_change(&book, |paths, book_dir| {
        info!("Files changed: {:?}\nBuilding book...\n", paths);
        let result = MDBook::load(&book_dir).and_then(|b| b.build());

        if let Err(e) = result {
            error!("Unable to build the book");
            utils::log_backtrace(&e);
        }
    });

    Ok(())
}

fn remove_ignored_files(book_root: &PathBuf, paths: &[PathBuf]) -> Vec<PathBuf> {
    if paths.is_empty() {
        return vec![];
    }

    let gitignore_path = book_root.join(".gitignore");

    match gitignore::File::new(gitignore_path.as_path()) {
        Ok(exclusion_checker) => paths
            .iter()
            .filter(|path| match exclusion_checker.is_excluded(path) {
                Ok(exclude) => !exclude,
                Err(error) => {
                    warn!(
                        "Unable to determine if {:?} is excluded: {:?}. Including it.",
                        &path, error
                    );
                    true
                }
            })
            .map(|path| path.to_path_buf())
            .collect(),
        Err(error) => {
            warn!(
                "Unable to read gitignore file at {:?} file: {:?}. All files will be allowed.",
                gitignore_path, error
            );
            paths.iter().map(|path| path.to_path_buf()).collect()
        }
    }
}

/// Calls the closure when a book source file is changed, blocking indefinitely.
pub fn trigger_on_change<F>(book: &MDBook, closure: F)
where
    F: Fn(Vec<PathBuf>, &Path),
{
    use notify::DebouncedEvent::*;
    use notify::RecursiveMode::*;

    // Create a channel to receive the events.
    let (tx, rx) = channel();

    let mut watcher = match notify::watcher(tx, Duration::from_secs(1)) {
        Ok(w) => w,
        Err(e) => {
            error!("Error while trying to watch the files:\n\n\t{:?}", e);
            std::process::exit(1)
        }
    };

    // Add the source directory to the watcher
    if let Err(e) = watcher.watch(book.source_dir(), Recursive) {
        error!("Error while watching {:?}:\n    {:?}", book.source_dir(), e);
        std::process::exit(1);
    };

    let _ = watcher.watch(book.theme_dir(), Recursive);

    // Add the book.toml file to the watcher if it exists
    let _ = watcher.watch(book.root.join("book.toml"), NonRecursive);

    info!("Listening for changes...");

    loop {
        let first_event = rx.recv().unwrap();
        sleep(Duration::from_millis(50));
        let other_events = rx.try_iter();

        let all_events = std::iter::once(first_event).chain(other_events);

        let paths = all_events
            .filter_map(|event| {
                debug!("Received filesystem event: {:?}", event);

                match event {
                    Create(path) | Write(path) | Remove(path) | Rename(_, path) => Some(path),
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        let paths = remove_ignored_files(&book.root, &paths[..]);

        if !paths.is_empty() {
            closure(paths, &book.root);
        }
    }
}
<|endoftext|>


<commit_before>//! Directory module for managing Algorithmia Data Directories
//!
//! # Examples
//!
//! ```no_run
//! use algorithmia::Algorithmia;
//! use algorithmia::data::DataAcl;
//! use std::fs::File;
//!
//! let client = Algorithmia::client("111112222233333444445555566");
//! let my_dir = client.dir(".my/my_dir");
//!
//! my_dir.create(DataAcl::default());
//! my_dir.put_file("/path/to/file");
//! ```

extern crate chrono;

use client::HttpClient;
use error::{self, Error};
use data::{self, DataFile, DeletedResult, XDataType};
use data::file::FileAdded;
use data::HasDataPath;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::vec::IntoIter;
use std::ops::Deref;
use std::error::Error as StdError;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::Url;
use rustc_serialize::{json, Decoder, Decodable};
use self::chrono::{DateTime, UTC};

/// Algorithmia Data Directory
pub struct DataDir {
    path: String,
    client: HttpClient,
}

#[derive(RustcDecodable, Debug)]
pub struct DirectoryUpdated {
    pub acl: Option<DataAcl>,
}

/// Response when deleting a new Directory
#[derive(RustcDecodable, Debug)]
pub struct DirectoryDeleted {
    // Omitting deleted.number and error.number for now
    pub result: DeletedResult,
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct FolderItem {
    pub name: String,
    pub acl: Option<DataAcl>,
}

#[derive(Debug)]
struct FileItem {
    pub filename: String,
    pub size: u64,
    pub last_modified: DateTime<UTC>,
}

// Manual implemented Decodable: https://github.com/lifthrasiir/rust-chrono/issues/43
impl Decodable for FileItem {
    fn decode<D: Decoder>(d: &mut D) -> Result<FileItem, D::Error> {
        d.read_struct("root", 0, |d| {
            Ok(FileItem{
                filename: try!(d.read_struct_field("filename", 0, |d| Decodable::decode(d))),
                size: try!(d.read_struct_field("size", 0, |d| Decodable::decode(d))),
                last_modified: {
                    let json_str: String = try!(d.read_struct_field("last_modified", 0, |d| Decodable::decode(d)));
                    match json_str.parse() {
                        Ok(datetime) => datetime,
                        Err(err) => return Err(d.error(err.description())),
                    }
                },
            })
        })
    }
}

/// ACL that indicates permissions for a DataDirectory
/// See also: [ReadAcl](enum.ReadAcl.html) enum to construct a DataACL
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct DataAcl {
    pub read: Vec<String>
}

pub enum ReadAcl {
  /// Readable only by owner
  Private,
  /// Readable by owner's algorithms (regardless of caller)
  MyAlgorithms,
  /// Readable by any user
  Public,
}

impl Default for DataAcl {
  fn default() -> Self {
    ReadAcl::MyAlgorithms.into()
  }
}

impl From<ReadAcl> for DataAcl {
    fn from(acl: ReadAcl) -> Self {
      match acl {
        ReadAcl::Private => DataAcl { read: vec![] },
        ReadAcl::MyAlgorithms => DataAcl { read: vec!["algo://.my/*".into()] },
        ReadAcl::Public => DataAcl { read: vec!["user://*".into()] },
      }
    }
}

/// Response when querying an existing Directory
#[derive(RustcDecodable, Debug)]
struct DirectoryShow {
    pub acl: Option<DataAcl>,
    pub folders: Option<Vec<FolderItem>>,
    pub files: Option<Vec<FileItem>>,
    pub marker: Option<String>,
}

pub struct DirectoryListing<'a> {
    pub acl: Option<DataAcl>,
    dir: &'a DataDir,
    folders: IntoIter<FolderItem>,
    files: IntoIter<FileItem>,
    marker: Option<String>,
    query_count: u32,
}

impl <'a> DirectoryListing<'a> {
    fn new(dir: &'a DataDir) -> DirectoryListing<'a> {
        DirectoryListing {
            acl: None,
            dir: dir,
            folders: Vec::new().into_iter(),
            files: Vec::new().into_iter(),
            marker: None,
            query_count: 0,
        }
    }
}

pub struct DataFileEntry {
    pub size: u64,
    pub last_modified: DateTime<UTC>,
    file: DataFile,
}

impl Deref for DataFileEntry {
    type Target = DataFile;
    fn deref(&self) -> &DataFile {&self.file}
}

pub enum DirEntry {
    File(DataFileEntry),
    Dir(DataDir),
}

impl <'a> Iterator for DirectoryListing<'a> {
    type Item = Result<DirEntry, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.folders.next() {
            // Return folders first
            Some(d) => Some(Ok(DirEntry::Dir(self.dir.child(&d.name)))),
            None => match self.files.next() {
                // Return files second
                Some(f) => Some(Ok(DirEntry::File(
                    DataFileEntry{
                        size: f.size,
                        last_modified: f.last_modified,
                        file: self.dir.child(&f.filename),
                    }
                ))),
                None => {
                    // Query if there is another page of files/folders
                    if self.query_count == 0 || self.marker.is_some() {
                        match get_directory(self.dir, self.marker.clone()) {
                            Ok(ds) => {
                                self.query_count = self.query_count + 1;
                                self.folders = ds.folders.unwrap_or(Vec::new()).into_iter();
                                self.files = ds.files.unwrap_or(Vec::new()).into_iter();
                                self.marker = ds.marker;
                                self.next()
                            }
                            Err(err) => Some(Err(err)),
                        }
                    } else {
                        None
                    }
                }
            }
        }
    }
}

fn get_directory(dir: &DataDir, marker: Option<String>) -> Result<DirectoryShow, Error> {
    let url = match marker {
        Some(m) => Url::parse(&format!("{}?marker={}", dir.to_url(), m)).unwrap(),
        None => dir.to_url(),
    };

    let req = dir.client.get(url);
    let mut res = try!(req.send());

    if res.status.is_success() {
        if let Some(data_type) = res.headers.get::<XDataType>() {
            if "directory" != data_type.to_string() {
                return Err(Error::DataTypeError(format!("Expected directory, Received {}", data_type)));
            }
        }
    }

    let mut res_json = String::new();
    try!(res.read_to_string(&mut res_json));

    match res.status.is_success() {
        true => json::decode(&res_json).map_err(|err| err.into()),
        false => Err(error::decode(&res_json)),
    }
}

impl HasDataPath for DataDir {
    fn new(client: HttpClient, path: &str) -> Self { DataDir { client: client, path: data::parse_data_uri(path).to_string() } }
    fn path(&self) -> &str { &self.path }
    fn client(&self) -> &HttpClient { &self.client }
}

impl DataDir {
    /// Display Directory details if it exists
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::{DirEntry, HasDataPath};
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// let dir_list = my_dir.list();
    /// for entry in dir_list {
    ///     match entry {
    ///         Ok(DirEntry::File(f)) => println!("File: {}", f.to_data_uri()),
    ///         Ok(DirEntry::Dir(d)) => println!("Dir: {}", d.to_data_uri()),
    ///         Err(err) => { println!("Error: {}", err); break; },
    ///     }
    /// };
    /// ```
    pub fn list(&self) -> DirectoryListing {
        DirectoryListing::new(self)
    }

    /// Create a Directory
    ///
    /// Use `DataAcl::default()` or the `ReadAcl` enum to set the ACL
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::DataAcl;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.create(DataAcl::default()) {
    ///   Ok(_) => println!("Successfully created Directory"),
    ///   Err(e) => println!("Error created directory: {}", e),
    /// };
    /// ```
    pub fn create<Acl: Into<DataAcl>>(&self, acl: Acl) -> Result<(), Error> {
        let parent = try!(self.parent().ok_or(Error::DataPathError("has no parent".into())));
        let url = parent.to_url();

        // TODO: address complete abuse of this structure
        let input_data = FolderItem {
            name: try!(self.basename().ok_or(Error::DataPathError("has no basename".into()))).into(),
            acl: Some(acl.into()),
        };
        let raw_input = try!(json::encode(&input_data));

        // POST request
        let req = self.client.post(url)
            .header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![])))
            .body(&raw_input);

        // Parse response
        let mut res = try!(req.send());

        match res.status.is_success() {
            true => Ok(()),
            false => {
                let mut res_json = String::new();
                try!(res.read_to_string(&mut res_json));
                Err(error::decode(&res_json))
            }
        }
    }

    /// Delete a Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.delete(false) {
    ///   Ok(_) => println!("Successfully deleted Directory"),
    ///   Err(err) => println!("Error deleting directory: {}", err),
    /// };
    /// ```
    pub fn delete(&self, force: bool) -> Result<DirectoryDeleted, Error> {
        // DELETE request
        let url_string = format!("{}?force={}", self.to_url(), force.to_string());
        let url = Url::parse(&url_string).unwrap();
        let req = self.client.delete(url);

        // Parse response
        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    /// Upload a file to an existing Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    ///
    /// match my_dir.put_file("/path/to/file") {
    ///   Ok(response) => println!("Successfully uploaded to: {}", response.result),
    ///   Err(err) => println!("Error uploading file: {}", err),
    /// };
    /// ```
    pub fn put_file<P: AsRef<Path>>(&self, file_path: P) -> Result<FileAdded, Error> {
        // FIXME: A whole lot of unwrap going on here...
        let path_ref = file_path.as_ref();
        let url_string = format!("{}/{}",
            self.to_url(),
            path_ref.file_name().unwrap().to_str().unwrap()
        );
        let url = Url::parse(&url_string).unwrap();

        let mut file = File::open(path_ref).unwrap();
        let req = self.client.put(url).body(&mut file);

        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    pub fn child<T: HasDataPath>(&self, filename: &str) -> T {
        T::new(self.client.clone(), &format!("{}/{}", self.to_data_uri(), filename))
    }
}

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

    fn mock_client() -> Algorithmia { Algorithmia::client("") }

    #[test]
    fn test_to_url() {
        let dir = mock_client().dir("data://anowell/foo");
        assert_eq!(dir.to_url().serialize_path().unwrap(), "/v1/data/anowell/foo");
    }

    #[test]
    fn test_to_data_uri() {
        let dir = mock_client().dir("/anowell/foo");
        assert_eq!(dir.to_data_uri(), "data://anowell/foo".to_string());
    }

    #[test]
    fn test_parent() {
        let dir = mock_client().dir("data://anowell/foo");
        let expected = mock_client().dir("data://anowell");
        assert_eq!(dir.parent().unwrap().path, expected.path);
    }

    #[test]
    fn test_default_acl() {
        let acl: DataAcl = DataAcl::default();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

    #[test]
    fn test_private_acl() {
        let acl: DataAcl = ReadAcl::Private.into();
        assert!(acl.read.is_empty());
    }

    #[test]
    fn test_public_acl() {
        let acl: DataAcl = ReadAcl::Public.into();
        assert_eq!(acl.read, vec!["user://*".to_string()]);
    }

    #[test]
    fn test_myalgos_acl() {
        let acl: DataAcl = ReadAcl::MyAlgorithms.into();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

}<commit_msg>Fix infinite error loop when dir listing fails<commit_after>//! Directory module for managing Algorithmia Data Directories
//!
//! # Examples
//!
//! ```no_run
//! use algorithmia::Algorithmia;
//! use algorithmia::data::DataAcl;
//! use std::fs::File;
//!
//! let client = Algorithmia::client("111112222233333444445555566");
//! let my_dir = client.dir(".my/my_dir");
//!
//! my_dir.create(DataAcl::default());
//! my_dir.put_file("/path/to/file");
//! ```

extern crate chrono;

use client::HttpClient;
use error::{self, Error};
use data::{self, DataFile, DeletedResult, XDataType};
use data::file::FileAdded;
use data::HasDataPath;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::vec::IntoIter;
use std::ops::Deref;
use std::error::Error as StdError;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::Url;
use rustc_serialize::{json, Decoder, Decodable};
use self::chrono::{DateTime, UTC};

/// Algorithmia Data Directory
pub struct DataDir {
    path: String,
    client: HttpClient,
}

#[derive(RustcDecodable, Debug)]
pub struct DirectoryUpdated {
    pub acl: Option<DataAcl>,
}

/// Response when deleting a new Directory
#[derive(RustcDecodable, Debug)]
pub struct DirectoryDeleted {
    // Omitting deleted.number and error.number for now
    pub result: DeletedResult,
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct FolderItem {
    pub name: String,
    pub acl: Option<DataAcl>,
}

#[derive(Debug)]
struct FileItem {
    pub filename: String,
    pub size: u64,
    pub last_modified: DateTime<UTC>,
}

// Manual implemented Decodable: https://github.com/lifthrasiir/rust-chrono/issues/43
impl Decodable for FileItem {
    fn decode<D: Decoder>(d: &mut D) -> Result<FileItem, D::Error> {
        d.read_struct("root", 0, |d| {
            Ok(FileItem{
                filename: try!(d.read_struct_field("filename", 0, |d| Decodable::decode(d))),
                size: try!(d.read_struct_field("size", 0, |d| Decodable::decode(d))),
                last_modified: {
                    let json_str: String = try!(d.read_struct_field("last_modified", 0, |d| Decodable::decode(d)));
                    match json_str.parse() {
                        Ok(datetime) => datetime,
                        Err(err) => return Err(d.error(err.description())),
                    }
                },
            })
        })
    }
}

/// ACL that indicates permissions for a DataDirectory
/// See also: [ReadAcl](enum.ReadAcl.html) enum to construct a DataACL
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct DataAcl {
    pub read: Vec<String>
}

pub enum ReadAcl {
  /// Readable only by owner
  Private,
  /// Readable by owner's algorithms (regardless of caller)
  MyAlgorithms,
  /// Readable by any user
  Public,
}

impl Default for DataAcl {
  fn default() -> Self {
    ReadAcl::MyAlgorithms.into()
  }
}

impl From<ReadAcl> for DataAcl {
    fn from(acl: ReadAcl) -> Self {
      match acl {
        ReadAcl::Private => DataAcl { read: vec![] },
        ReadAcl::MyAlgorithms => DataAcl { read: vec!["algo://.my/*".into()] },
        ReadAcl::Public => DataAcl { read: vec!["user://*".into()] },
      }
    }
}

/// Response when querying an existing Directory
#[derive(RustcDecodable, Debug)]
struct DirectoryShow {
    pub acl: Option<DataAcl>,
    pub folders: Option<Vec<FolderItem>>,
    pub files: Option<Vec<FileItem>>,
    pub marker: Option<String>,
}

pub struct DirectoryListing<'a> {
    pub acl: Option<DataAcl>,
    dir: &'a DataDir,
    folders: IntoIter<FolderItem>,
    files: IntoIter<FileItem>,
    marker: Option<String>,
    query_count: u32,
}

impl <'a> DirectoryListing<'a> {
    fn new(dir: &'a DataDir) -> DirectoryListing<'a> {
        DirectoryListing {
            acl: None,
            dir: dir,
            folders: Vec::new().into_iter(),
            files: Vec::new().into_iter(),
            marker: None,
            query_count: 0,
        }
    }
}

pub struct DataFileEntry {
    pub size: u64,
    pub last_modified: DateTime<UTC>,
    file: DataFile,
}

impl Deref for DataFileEntry {
    type Target = DataFile;
    fn deref(&self) -> &DataFile {&self.file}
}

pub enum DirEntry {
    File(DataFileEntry),
    Dir(DataDir),
}

impl <'a> Iterator for DirectoryListing<'a> {
    type Item = Result<DirEntry, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.folders.next() {
            // Return folders first
            Some(d) => Some(Ok(DirEntry::Dir(self.dir.child(&d.name)))),
            None => match self.files.next() {
                // Return files second
                Some(f) => Some(Ok(DirEntry::File(
                    DataFileEntry{
                        size: f.size,
                        last_modified: f.last_modified,
                        file: self.dir.child(&f.filename),
                    }
                ))),
                None => {
                    // Query if there is another page of files/folders
                    if self.query_count == 0 || self.marker.is_some() {
                        self.query_count = self.query_count + 1;
                        match get_directory(self.dir, self.marker.clone()) {
                            Ok(ds) => {
                                self.folders = ds.folders.unwrap_or(Vec::new()).into_iter();
                                self.files = ds.files.unwrap_or(Vec::new()).into_iter();
                                self.marker = ds.marker;
                                self.next()
                            }
                            Err(err) => Some(Err(err)),
                        }
                    } else {
                        None
                    }
                }
            }
        }
    }
}

fn get_directory(dir: &DataDir, marker: Option<String>) -> Result<DirectoryShow, Error> {
    let url = match marker {
        Some(m) => Url::parse(&format!("{}?marker={}", dir.to_url(), m)).unwrap(),
        None => dir.to_url(),
    };

    let req = dir.client.get(url);
    let mut res = try!(req.send());

    if res.status.is_success() {
        if let Some(data_type) = res.headers.get::<XDataType>() {
            if "directory" != data_type.to_string() {
                return Err(Error::DataTypeError(format!("Expected directory, Received {}", data_type)));
            }
        }
    }

    let mut res_json = String::new();
    try!(res.read_to_string(&mut res_json));

    match res.status.is_success() {
        true => json::decode(&res_json).map_err(|err| err.into()),
        false => Err(error::decode(&res_json)),
    }
}

impl HasDataPath for DataDir {
    fn new(client: HttpClient, path: &str) -> Self { DataDir { client: client, path: data::parse_data_uri(path).to_string() } }
    fn path(&self) -> &str { &self.path }
    fn client(&self) -> &HttpClient { &self.client }
}

impl DataDir {
    /// Display Directory details if it exists
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::{DirEntry, HasDataPath};
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// let dir_list = my_dir.list();
    /// for entry in dir_list {
    ///     match entry {
    ///         Ok(DirEntry::File(f)) => println!("File: {}", f.to_data_uri()),
    ///         Ok(DirEntry::Dir(d)) => println!("Dir: {}", d.to_data_uri()),
    ///         Err(err) => { println!("Error: {}", err); break; },
    ///     }
    /// };
    /// ```
    pub fn list(&self) -> DirectoryListing {
        DirectoryListing::new(self)
    }

    /// Create a Directory
    ///
    /// Use `DataAcl::default()` or the `ReadAcl` enum to set the ACL
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::DataAcl;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.create(DataAcl::default()) {
    ///   Ok(_) => println!("Successfully created Directory"),
    ///   Err(e) => println!("Error created directory: {}", e),
    /// };
    /// ```
    pub fn create<Acl: Into<DataAcl>>(&self, acl: Acl) -> Result<(), Error> {
        let parent = try!(self.parent().ok_or(Error::DataPathError("has no parent".into())));
        let url = parent.to_url();

        // TODO: address complete abuse of this structure
        let input_data = FolderItem {
            name: try!(self.basename().ok_or(Error::DataPathError("has no basename".into()))).into(),
            acl: Some(acl.into()),
        };
        let raw_input = try!(json::encode(&input_data));

        // POST request
        let req = self.client.post(url)
            .header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![])))
            .body(&raw_input);

        // Parse response
        let mut res = try!(req.send());

        match res.status.is_success() {
            true => Ok(()),
            false => {
                let mut res_json = String::new();
                try!(res.read_to_string(&mut res_json));
                Err(error::decode(&res_json))
            }
        }
    }

    /// Delete a Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.delete(false) {
    ///   Ok(_) => println!("Successfully deleted Directory"),
    ///   Err(err) => println!("Error deleting directory: {}", err),
    /// };
    /// ```
    pub fn delete(&self, force: bool) -> Result<DirectoryDeleted, Error> {
        // DELETE request
        let url_string = format!("{}?force={}", self.to_url(), force.to_string());
        let url = Url::parse(&url_string).unwrap();
        let req = self.client.delete(url);

        // Parse response
        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    /// Upload a file to an existing Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    ///
    /// match my_dir.put_file("/path/to/file") {
    ///   Ok(response) => println!("Successfully uploaded to: {}", response.result),
    ///   Err(err) => println!("Error uploading file: {}", err),
    /// };
    /// ```
    pub fn put_file<P: AsRef<Path>>(&self, file_path: P) -> Result<FileAdded, Error> {
        // FIXME: A whole lot of unwrap going on here...
        let path_ref = file_path.as_ref();
        let url_string = format!("{}/{}",
            self.to_url(),
            path_ref.file_name().unwrap().to_str().unwrap()
        );
        let url = Url::parse(&url_string).unwrap();

        let mut file = File::open(path_ref).unwrap();
        let req = self.client.put(url).body(&mut file);

        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    pub fn child<T: HasDataPath>(&self, filename: &str) -> T {
        T::new(self.client.clone(), &format!("{}/{}", self.to_data_uri(), filename))
    }
}

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

    fn mock_client() -> Algorithmia { Algorithmia::client("") }

    #[test]
    fn test_to_url() {
        let dir = mock_client().dir("data://anowell/foo");
        assert_eq!(dir.to_url().serialize_path().unwrap(), "/v1/data/anowell/foo");
    }

    #[test]
    fn test_to_data_uri() {
        let dir = mock_client().dir("/anowell/foo");
        assert_eq!(dir.to_data_uri(), "data://anowell/foo".to_string());
    }

    #[test]
    fn test_parent() {
        let dir = mock_client().dir("data://anowell/foo");
        let expected = mock_client().dir("data://anowell");
        assert_eq!(dir.parent().unwrap().path, expected.path);
    }

    #[test]
    fn test_default_acl() {
        let acl: DataAcl = DataAcl::default();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

    #[test]
    fn test_private_acl() {
        let acl: DataAcl = ReadAcl::Private.into();
        assert!(acl.read.is_empty());
    }

    #[test]
    fn test_public_acl() {
        let acl: DataAcl = ReadAcl::Public.into();
        assert_eq!(acl.read, vec!["user://*".to_string()]);
    }

    #[test]
    fn test_myalgos_acl() {
        let acl: DataAcl = ReadAcl::MyAlgorithms.into();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

}<|endoftext|>