<commit_before>extern crate libc;
extern crate rsnl;

use libc::{c_int};
use rsnl::message::{NetlinkMessage, nl_msg, nlmsghdr};
use rsnl::message::expose::{nl_msg_ptr, nlmsghdr_ptr};

#[repr(C)]
struct genlmsghdr;

#[link(name="nl-genl-3")]
extern "C" {
    fn genlmsg_valid_hdr(hdr: *const nlmsghdr, hdrlen: c_int) -> i32;
    fn genlmsg_hdr(hdr: *const nlmsghdr) -> *const genlmsghdr;
}

// leaky!
pub struct GenlHeader {
    ptr: *const genlmsghdr,
}

pub fn valid_hdr(msg: &NetlinkMessage, hdrlen: i32) -> i32 {
    let hdrptr = nlmsghdr_ptr(msg);

    unsafe { genlmsg_valid_hdr(hdrptr, hdrlen) }
}

pub fn hdr(msg: &NetlinkMessage) -> GenlHeader {
    GenlHeader {
        ptr: unsafe { genlmsg_hdr(nlmsghdr_ptr(msg)) }
    }
}
<commit_msg>Add msg_put<commit_after>extern crate libc;
extern crate rsnl;

use libc::{c_int, c_void};
use rsnl::message::{NetlinkMessage, nl_msg, nlmsghdr};
use rsnl::message::expose::{nl_msg_ptr, nlmsghdr_ptr};

#[repr(C)]
struct genlmsghdr;

#[link(name="nl-genl-3")]
extern "C" {
    fn genlmsg_valid_hdr(hdr: *const nlmsghdr, hdrlen: c_int) -> i32;
    fn genlmsg_hdr(hdr: *const nlmsghdr) -> *const genlmsghdr;
    fn genlmsg_put(msg: *const nl_msg, port: u32, seq: u32, family: c_int, hdrlen: c_int, flags: c_int, cmd: u8, version: u8) -> *const c_void;
}

// leaky!
pub struct GenlHeader {
    ptr: *const genlmsghdr,
}

pub fn valid_hdr(msg: &NetlinkMessage, hdrlen: i32) -> i32 {
    let hdrptr = nlmsghdr_ptr(msg);

    unsafe { genlmsg_valid_hdr(hdrptr, hdrlen) }
}

pub fn hdr(msg: &NetlinkMessage) -> GenlHeader {
    GenlHeader {
        ptr: unsafe { genlmsg_hdr(nlmsghdr_ptr(msg)) }
    }
}

pub fn put(msg: &mut NetlinkMessage, port: u32, seq: u32, family: i32, hdrlen: i32, flags: i32, cmd: u8, version: u8) {
    unsafe { genlmsg_put(nl_msg_ptr(msg), port, seq, family, hdrlen, flags, cmd, version); }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;

use collada::document::ColladaDocument;
use collada;

use math::*;
use skeleton::Skeleton;
use transform::Transform;

/// A single skeletal pose
#[derive(Debug)]
pub struct AnimationSample<T: Transform>
{

    /// Local pose transforms for each joint in the targeted skeleton
    /// (relative to parent joint)
    pub local_poses: Vec<T>,

}

/// A sequence of skeletal pose samples at some sample rate
#[derive(Debug)]
pub struct AnimationClip<T: Transform> {

    /// The sequence of skeletal poses
    pub samples: Vec<AnimationSample<T>>,

    /// Sample rate for the clip. Assumes a constant sample rate.
    pub samples_per_second: f32,

}

#[derive(Debug, RustcDecodable)]
pub struct AnimationClipDef {
    pub name: String,
    pub source: String,
    pub duration: f32,
    pub rotate_z: f32,
}

#[derive(Debug, RustcDecodable)]
pub struct DifferenceClipDef {
    pub name: String,
    pub source_clip: String,
    pub reference_clip: String,
}

impl<T: Transform> AnimationClip<T> {

    pub fn from_def(clip_def: &AnimationClipDef) -> AnimationClip<T> {

        // Wacky. Shouldn't it be an error if the struct field isn't present?
        // FIXME - use an Option
        let adjust = if !clip_def.rotate_z.is_nan() {
            mat4_rotate_z(clip_def.rotate_z.to_radians())
        } else {
            mat4_id()
        };

        // FIXME - load skeleton separately?
        let collada_document = ColladaDocument::from_path(&Path::new(&clip_def.source[..])).unwrap();
        let animations = collada_document.get_animations();
        let skeleton_set = collada_document.get_skeletons().unwrap();
        let skeleton = Skeleton::from_collada(&skeleton_set[0]);

        let mut clip = AnimationClip::from_collada(&skeleton, &animations, &adjust);

        if !clip_def.duration.is_nan() {
            clip.set_duration(clip_def.duration);
        }
        clip

    }

    /// Overrides the sampling rate of the clip to give the given duration (in seconds).
    pub fn set_duration(&mut self, duration: f32) {
        self.samples_per_second = self.samples.len() as f32 / duration;
    }

    /// Return the duration of the clip in seconds
    pub fn get_duration(&self) -> f32 {
        self.samples.len() as f32 / self.samples_per_second
    }

    /// Obtains the interpolated skeletal pose at the given sampling time.
    ///
    /// # Arguments
    ///
    /// * `time` - The time to sample with, relative to the start of the animation
    /// * `blended_poses` - The output array slice of joint transforms that will be populated
    ///                     for each joint in the skeleton.
    pub fn get_pose_at_time(&self, elapsed_time: f32, blended_poses: &mut [T]) {

        let interpolated_index = elapsed_time * self.samples_per_second;

        let index_1 = interpolated_index.floor() as usize;
        let index_2 = interpolated_index.ceil() as usize;

        let blend_factor = interpolated_index - index_1 as f32;

        let index_1 = index_1 % self.samples.len();
        let index_2 = index_2 % self.samples.len();

        let sample_1 = &self.samples[index_1];
        let sample_2 = &self.samples[index_2];

        for i in (0 .. sample_1.local_poses.len()) {

            let pose_1 = sample_1.local_poses[i];
            let pose_2 = sample_2.local_poses[i];

            let blended_pose = &mut blended_poses[i];
            *blended_pose = pose_1.lerp(pose_2, blend_factor);
        }

    }

    /// Create a difference clip from a source and reference clip for additive blending.
    pub fn as_difference_clip(source_clip: &AnimationClip<T>, reference_clip: &AnimationClip<T>) -> AnimationClip<T> {

        let samples = (0 .. source_clip.samples.len()).map(|sample_index| {

            let ref source_sample = source_clip.samples[sample_index];

            // Extrapolate reference clip by wrapping, if reference clip is shorter than source clip
            let ref reference_sample = reference_clip.samples[sample_index % reference_clip.samples.len()];

            let difference_poses = (0 .. source_sample.local_poses.len()).map(|joint_index| {
                let ref source_pose = source_sample.local_poses[joint_index];
                let reference_pose = reference_sample.local_poses[joint_index];
                source_pose.inverse().concat(reference_pose)
            }).collect();

            AnimationSample {
                local_poses: difference_poses,
            }

        }).collect();

        AnimationClip {
            samples_per_second: source_clip.samples_per_second,
            samples: samples,
        }
    }

    /// Creates an `AnimationClip` from a collection of `collada::Animation`.
    ///
    /// # Arguments
    ///
    /// * `skeleton` - The `Skeleton` that the `AnimationClip` will be created for.
    /// * `animations` - The collection of `collada::Animation`s that will be converted into an
    ///                  `AnimationClip`, using the given `Skeleton`.
    /// * `transform` - An offset transform to apply to the root pose of each animation sample,
    ///                 useful for applying rotation, translation, or scaling when loading an
    ///                 animation.
    pub fn from_collada(skeleton: &Skeleton, animations: &Vec<collada::Animation>, transform: &Matrix4<f32>) -> AnimationClip<T> {
        use std::f32::consts::PI;

        // Z-axis is 'up' in COLLADA, so need to rotate root pose about x-axis so y-axis is 'up'
        let rotate_on_x =
            [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, (PI/2.0).cos(), (PI/2.0).sin(), 0.0],
                [0.0, (-PI/2.0).sin(), (PI/2.0).cos(), 0.0],
                [0.0, 0.0, 0.0, 1.0],
            ];

        let transform = row_mat4_mul(rotate_on_x, *transform);

        // Build an index of joint names to anims
        let mut joint_animations = HashMap::new();
        for anim in animations.iter() {
            let joint_name = anim.target.split('/').next().unwrap();
            joint_animations.insert(joint_name, anim);
        }

        // Assuming all ColladaAnims have the same number of samples..
        let sample_count = animations[0].sample_times.len();

        // Assuming all ColladaAnims have the same duration..
        let duration = *animations[0].sample_times.last().unwrap();

        // Assuming constant sample rate
        let samples_per_second = sample_count as f32 / duration;

        let samples = (0 .. sample_count).map(|sample_index| {

            // Grab local poses for each joint from COLLADA animation if available,
            // falling back to identity matrix
            let local_poses: Vec<Matrix4<f32>> = skeleton.joints.iter().map(|joint| {
                match joint_animations.get(&joint.name[..]) {
                    Some(a) if joint.is_root() => row_mat4_mul(transform, a.sample_poses[sample_index]),
                    Some(a) => a.sample_poses[sample_index],
                    None => mat4_id(),
                }
            }).collect();

            // Convert local poses to Transforms (for interpolation)
            let local_poses: Vec<T> = local_poses.iter().map(|pose_matrix| {
                T::from_matrix(*pose_matrix)
            }).collect();

            AnimationSample {
                local_poses: local_poses,
            }
        }).collect();

        AnimationClip {
            samples_per_second: samples_per_second,
            samples: samples,
        }
    }

}

/// An instance of an AnimationClip which tracks playback parameters
pub struct ClipInstance<T: Transform> {
    /// Shared clip reference
    pub clip: Rc<AnimationClip<T>>,

    /// Controller clock time at animation start
    pub start_time: f32,

    /// Playback rate modifier, where 1.0 is original speed
    pub playback_rate: f32,

    /// Used to account for changes in playback rate
    pub time_offset: f32,
}

impl<T: Transform> ClipInstance<T> {

    pub fn new(clip: Rc<AnimationClip<T>>) -> ClipInstance<T> {
        ClipInstance {
            clip: clip,
            start_time: 0.0,
            playback_rate: 1.0,
            time_offset: 0.0,
        }
    }

    /// Adjust the playback rate of the clip without affecting the
    /// value of get_local_time for a given global time.
    pub fn set_playback_rate(&mut self, global_time: f32, new_rate: f32) {
        if self.playback_rate != new_rate {
            let local_time = self.get_local_time(global_time);
            self.time_offset = local_time - (global_time - self.start_time) * new_rate;
            self.playback_rate = new_rate;
        }
    }

    pub fn get_pose_at_time(&self, global_time: f32, blended_poses: &mut [T]) {
        self.clip.get_pose_at_time(self.get_local_time(global_time), blended_poses);
    }

    pub fn get_duration(&self) -> f32 {
        self.clip.get_duration()
    }

    fn get_local_time(&self, global_time: f32) -> f32 {
        (global_time - self.start_time) * self.playback_rate + self.time_offset
    }
}
<commit_msg>Fixed difference-clip generation<commit_after>use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;

use collada::document::ColladaDocument;
use collada;

use math::*;
use skeleton::Skeleton;
use transform::Transform;

/// A single skeletal pose
#[derive(Debug)]
pub struct AnimationSample<T: Transform>
{

    /// Local pose transforms for each joint in the targeted skeleton
    /// (relative to parent joint)
    pub local_poses: Vec<T>,

}

/// A sequence of skeletal pose samples at some sample rate
#[derive(Debug)]
pub struct AnimationClip<T: Transform> {

    /// The sequence of skeletal poses
    pub samples: Vec<AnimationSample<T>>,

    /// Sample rate for the clip. Assumes a constant sample rate.
    pub samples_per_second: f32,

}

#[derive(Debug, RustcDecodable)]
pub struct AnimationClipDef {
    pub name: String,
    pub source: String,
    pub duration: f32,
    pub rotate_z: f32,
}

#[derive(Debug, RustcDecodable)]
pub struct DifferenceClipDef {
    pub name: String,
    pub source_clip: String,
    pub reference_clip: String,
}

impl<T: Transform> AnimationClip<T> {

    pub fn from_def(clip_def: &AnimationClipDef) -> AnimationClip<T> {

        // Wacky. Shouldn't it be an error if the struct field isn't present?
        // FIXME - use an Option
        let adjust = if !clip_def.rotate_z.is_nan() {
            mat4_rotate_z(clip_def.rotate_z.to_radians())
        } else {
            mat4_id()
        };

        // FIXME - load skeleton separately?
        let collada_document = ColladaDocument::from_path(&Path::new(&clip_def.source[..])).unwrap();
        let animations = collada_document.get_animations();
        let skeleton_set = collada_document.get_skeletons().unwrap();
        let skeleton = Skeleton::from_collada(&skeleton_set[0]);

        let mut clip = AnimationClip::from_collada(&skeleton, &animations, &adjust);

        if !clip_def.duration.is_nan() {
            clip.set_duration(clip_def.duration);
        }
        clip

    }

    /// Overrides the sampling rate of the clip to give the given duration (in seconds).
    pub fn set_duration(&mut self, duration: f32) {
        self.samples_per_second = self.samples.len() as f32 / duration;
    }

    /// Return the duration of the clip in seconds
    pub fn get_duration(&self) -> f32 {
        self.samples.len() as f32 / self.samples_per_second
    }

    /// Obtains the interpolated skeletal pose at the given sampling time.
    ///
    /// # Arguments
    ///
    /// * `time` - The time to sample with, relative to the start of the animation
    /// * `blended_poses` - The output array slice of joint transforms that will be populated
    ///                     for each joint in the skeleton.
    pub fn get_pose_at_time(&self, elapsed_time: f32, blended_poses: &mut [T]) {

        let interpolated_index = elapsed_time * self.samples_per_second;

        let index_1 = interpolated_index.floor() as usize;
        let index_2 = interpolated_index.ceil() as usize;

        let blend_factor = interpolated_index - index_1 as f32;

        let index_1 = index_1 % self.samples.len();
        let index_2 = index_2 % self.samples.len();

        let sample_1 = &self.samples[index_1];
        let sample_2 = &self.samples[index_2];

        for i in (0 .. sample_1.local_poses.len()) {

            let pose_1 = sample_1.local_poses[i];
            let pose_2 = sample_2.local_poses[i];

            let blended_pose = &mut blended_poses[i];
            *blended_pose = pose_1.lerp(pose_2, blend_factor);
        }

    }

    /// Create a difference clip from a source and reference clip for additive blending.
    pub fn as_difference_clip(source_clip: &AnimationClip<T>, reference_clip: &AnimationClip<T>) -> AnimationClip<T> {

        let samples = (0 .. source_clip.samples.len()).map(|sample_index| {

            let ref source_sample = source_clip.samples[sample_index];

            // Extrapolate reference clip by wrapping, if reference clip is shorter than source clip
            let ref reference_sample = reference_clip.samples[sample_index % reference_clip.samples.len()];

            let difference_poses = (0 .. source_sample.local_poses.len()).map(|joint_index| {
                let source_pose = source_sample.local_poses[joint_index];
                let reference_pose = reference_sample.local_poses[joint_index];
                reference_pose.inverse().concat(source_pose)
            }).collect();

            AnimationSample {
                local_poses: difference_poses,
            }

        }).collect();

        AnimationClip {
            samples_per_second: source_clip.samples_per_second,
            samples: samples,
        }
    }

    /// Creates an `AnimationClip` from a collection of `collada::Animation`.
    ///
    /// # Arguments
    ///
    /// * `skeleton` - The `Skeleton` that the `AnimationClip` will be created for.
    /// * `animations` - The collection of `collada::Animation`s that will be converted into an
    ///                  `AnimationClip`, using the given `Skeleton`.
    /// * `transform` - An offset transform to apply to the root pose of each animation sample,
    ///                 useful for applying rotation, translation, or scaling when loading an
    ///                 animation.
    pub fn from_collada(skeleton: &Skeleton, animations: &Vec<collada::Animation>, transform: &Matrix4<f32>) -> AnimationClip<T> {
        use std::f32::consts::PI;

        // Z-axis is 'up' in COLLADA, so need to rotate root pose about x-axis so y-axis is 'up'
        let rotate_on_x =
            [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, (PI/2.0).cos(), (PI/2.0).sin(), 0.0],
                [0.0, (-PI/2.0).sin(), (PI/2.0).cos(), 0.0],
                [0.0, 0.0, 0.0, 1.0],
            ];

        let transform = row_mat4_mul(rotate_on_x, *transform);

        // Build an index of joint names to anims
        let mut joint_animations = HashMap::new();
        for anim in animations.iter() {
            let joint_name = anim.target.split('/').next().unwrap();
            joint_animations.insert(joint_name, anim);
        }

        // Assuming all ColladaAnims have the same number of samples..
        let sample_count = animations[0].sample_times.len();

        // Assuming all ColladaAnims have the same duration..
        let duration = *animations[0].sample_times.last().unwrap();

        // Assuming constant sample rate
        let samples_per_second = sample_count as f32 / duration;

        let samples = (0 .. sample_count).map(|sample_index| {

            // Grab local poses for each joint from COLLADA animation if available,
            // falling back to identity matrix
            let local_poses: Vec<Matrix4<f32>> = skeleton.joints.iter().map(|joint| {
                match joint_animations.get(&joint.name[..]) {
                    Some(a) if joint.is_root() => row_mat4_mul(transform, a.sample_poses[sample_index]),
                    Some(a) => a.sample_poses[sample_index],
                    None => mat4_id(),
                }
            }).collect();

            // Convert local poses to Transforms (for interpolation)
            let local_poses: Vec<T> = local_poses.iter().map(|pose_matrix| {
                T::from_matrix(*pose_matrix)
            }).collect();

            AnimationSample {
                local_poses: local_poses,
            }
        }).collect();

        AnimationClip {
            samples_per_second: samples_per_second,
            samples: samples,
        }
    }

}

/// An instance of an AnimationClip which tracks playback parameters
pub struct ClipInstance<T: Transform> {
    /// Shared clip reference
    pub clip: Rc<AnimationClip<T>>,

    /// Controller clock time at animation start
    pub start_time: f32,

    /// Playback rate modifier, where 1.0 is original speed
    pub playback_rate: f32,

    /// Used to account for changes in playback rate
    pub time_offset: f32,
}

impl<T: Transform> ClipInstance<T> {

    pub fn new(clip: Rc<AnimationClip<T>>) -> ClipInstance<T> {
        ClipInstance {
            clip: clip,
            start_time: 0.0,
            playback_rate: 1.0,
            time_offset: 0.0,
        }
    }

    /// Adjust the playback rate of the clip without affecting the
    /// value of get_local_time for a given global time.
    pub fn set_playback_rate(&mut self, global_time: f32, new_rate: f32) {
        if self.playback_rate != new_rate {
            let local_time = self.get_local_time(global_time);
            self.time_offset = local_time - (global_time - self.start_time) * new_rate;
            self.playback_rate = new_rate;
        }
    }

    pub fn get_pose_at_time(&self, global_time: f32, blended_poses: &mut [T]) {
        self.clip.get_pose_at_time(self.get_local_time(global_time), blended_poses);
    }

    pub fn get_duration(&self) -> f32 {
        self.clip.get_duration()
    }

    fn get_local_time(&self, global_time: f32) -> f32 {
        (global_time - self.start_time) * self.playback_rate + self.time_offset
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use client::HttpRequest as ClientRequest;
use client::HttpStream as ClientStream;

use server::HttpRequest as ServerRequest;

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

use std::collections::HashMap;
use std::io::prelude::*;
use std::{io, iter};

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, String>,
    files: HashMap<String, Vec<u8>>,
}

#[test]
fn local_test() {
    let _ = ::env_logger::init();

    let test_fields = gen_test_fields();

    let buf = test_client(&test_fields);

    trace!(
        "\n--Test Buffer Begin--\n{}\n--Test Buffer End--",
        String::from_utf8_lossy(&buf.buf)
    );

    test_server(buf, test_fields);
}

fn gen_test_fields() -> TestFields {
    const MIN_FIELDS: usize = 1;
    const MAX_FIELDS: usize = 3;

    let mut rng = rand::weak_rng();

    let texts_count = rng.gen_range(MIN_FIELDS, MAX_FIELDS);
    let files_count = rng.gen_range(MIN_FIELDS, MAX_FIELDS);

    TestFields {
        texts: (0..texts_count).map(|_| (gen_string(), gen_string())).collect(),
        files: (0..files_count).map(|_| (gen_string(), gen_bytes())).collect(),
    }
}

fn gen_string() -> String {
    const MIN_LEN: usize = 2;
    const MAX_LEN: usize = 5;
    const MAX_DASHES: usize = 2;

    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = MockClientRequest::default();

    let mut test_files = test_fields.files.iter();

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in &test_fields.texts {
        if let Some((file_name, file)) = test_files.next() {
            multipart.write_stream(file_name, &mut &**file, Some(file_name), None).unwrap();
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, file) in test_files {
       multipart.write_stream(file_name, &mut &**file, None, None).unwrap();
    }

    multipart.send().unwrap()
}

fn test_server(buf: HttpBuffer, mut fields: TestFields) {
    use server::{Multipart, MultipartData};

    let mut multipart = Multipart::from_request(buf.for_server())
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    trace!("Fields for server test: {:?}", fields);

    while let Ok(Some(mut field)) = multipart.read_entry() {
        match field.data {
            MultipartData::Text(text) => {
                let test_text = fields.texts.remove(&field.name);

                assert!(
                    test_text.is_some(),
                    "Got text field that wasn't in original dataset: {:?} : {:?} ",
                    field.name, text
                );

                let test_text = test_text.unwrap();

                assert!(
                    text == test_text,
                    "Unexpected data for field {:?}: Expected {:?}, got {:?}",
                    field.name, test_text, text
                );

            },
            MultipartData::File(ref mut file) => {
                let test_bytes = fields.files.remove(&field.name).unwrap();

                let mut bytes = Vec::with_capacity(test_bytes.len());
                file.read_to_end(&mut bytes).unwrap();

                assert!(bytes == test_bytes, "Unexpected data for file {:?}: Expected {:?}, Got {:?}",
                        field.name, String::from_utf8_lossy(&test_bytes), String::from_utf8_lossy(&bytes)
                );
            },
        }
    }

    assert!(fields.texts.is_empty(), "Text fields were not exhausted! Text fields: {:?}", fields.texts);
    assert!(fields.files.is_empty(), "File fields were not exhausted! File fields: {:?}", fields.files);
}

#[derive(Default, Debug)]
pub struct MockClientRequest {
    boundary: Option<String>,
    content_len: Option<u64>,
}

impl ClientRequest for MockClientRequest {
    type Stream = HttpBuffer;
    type Error = io::Error;

    fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool {
        self.boundary = Some(boundary.into());
        self.content_len = content_len;
        true
    }

    fn open_stream(self) -> Result<HttpBuffer, io::Error> {
        debug!("MockClientRequest::open_stream called! {:?}", self);
        let boundary = self.boundary.expect("HttpRequest::set_headers() was not called!");

        Ok(HttpBuffer { buf: Vec::new(), boundary: boundary, content_len: self.content_len })
    }
}

#[derive(Debug)]
pub struct HttpBuffer {
    buf: Vec<u8>,
    boundary: String,
    content_len: Option<u64>,
}

impl HttpBuffer {
    pub fn for_server(&self) -> ServerBuffer {
        ServerBuffer {
            data: &self.buf,
            boundary: &self.boundary,
            content_len: self.content_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Write for HttpBuffer {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.buf.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl Read for HttpBuffer {
    fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
        unimplemented!()
    }
}

impl ClientStream for HttpBuffer {
    type Request = MockClientRequest;
    type Response = HttpBuffer;
    type Error = io::Error;

    fn finish(self) -> Result<Self, io::Error> { Ok(self) }
}

pub struct ServerBuffer<'a> {
    data: &'a [u8],
    boundary: &'a str,
    content_len: Option<u64>,
    rng: ThreadRng,
}

impl<'a> Read for ServerBuffer<'a> {
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, out.len());
        self.data.read(&mut out[..len])
    }
}

impl<'a> ServerRequest for ServerBuffer<'a> {
    type Body = Self;

    fn multipart_boundary(&self) -> Option<&str> { Some(&self.boundary) }

    fn body(self) -> Self::Body {
        self
    }
}

<commit_msg>Test content_len in local_test<commit_after>// Copyright 2016 `multipart` Crate Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use client::HttpRequest as ClientRequest;
use client::HttpStream as ClientStream;

use server::HttpRequest as ServerRequest;

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

use std::collections::HashMap;
use std::io::prelude::*;
use std::{io, iter};

#[derive(Debug)]
struct TestFields {
    texts: HashMap<String, String>,
    files: HashMap<String, Vec<u8>>,
}

#[test]
fn local_test() {
    let _ = ::env_logger::init();

    let test_fields = gen_test_fields();

    let buf = test_client(&test_fields);

    trace!(
        "\n--Test Buffer Begin--\n{}\n--Test Buffer End--",
        String::from_utf8_lossy(&buf.buf)
    );

    test_server(buf, test_fields);
}

fn gen_test_fields() -> TestFields {
    const MIN_FIELDS: usize = 1;
    const MAX_FIELDS: usize = 3;

    let mut rng = rand::weak_rng();

    let texts_count = rng.gen_range(MIN_FIELDS, MAX_FIELDS);
    let files_count = rng.gen_range(MIN_FIELDS, MAX_FIELDS);

    TestFields {
        texts: (0..texts_count).map(|_| (gen_string(), gen_string())).collect(),
        files: (0..files_count).map(|_| (gen_string(), gen_bytes())).collect(),
    }
}

fn gen_string() -> String {
    const MIN_LEN: usize = 2;
    const MAX_LEN: usize = 5;
    const MAX_DASHES: usize = 2;

    let mut rng_1 = rand::thread_rng();
    let mut rng_2 = rand::thread_rng();

    let str_len_1 = rng_1.gen_range(MIN_LEN, MAX_LEN + 1);
    let str_len_2 = rng_2.gen_range(MIN_LEN, MAX_LEN + 1);
    let num_dashes = rng_1.gen_range(0, MAX_DASHES + 1);

    rng_1.gen_ascii_chars().take(str_len_1)
        .chain(iter::repeat('-').take(num_dashes))
        .chain(rng_2.gen_ascii_chars().take(str_len_2))
        .collect()
}

fn gen_bytes() -> Vec<u8> {
    gen_string().into_bytes()
}

fn test_client(test_fields: &TestFields) -> HttpBuffer {
    use client::Multipart;

    let request = MockClientRequest::default();

    let mut test_files = test_fields.files.iter();

    let mut multipart = Multipart::from_request(request).unwrap();

    // Intersperse file fields amongst text fields
    for (name, text) in &test_fields.texts {
        if let Some((file_name, file)) = test_files.next() {
            multipart.write_stream(file_name, &mut &**file, Some(file_name), None).unwrap();
        }

        multipart.write_text(name, text).unwrap();
    }

    // Write remaining files
    for (file_name, file) in test_files {
       multipart.write_stream(file_name, &mut &**file, None, None).unwrap();
    }

    multipart.send().unwrap()
}

fn test_server(buf: HttpBuffer, mut fields: TestFields) {
    use server::{Multipart, MultipartData};

    let server_buf = buf.for_server();

    if let Some(content_len) = server_buf.content_len {
        assert!(content_len == server_buf.data.len() as u64, "Supplied content_len different from actual");
    }

    let mut multipart = Multipart::from_request(server_buf)
        .unwrap_or_else(|_| panic!("Buffer should be multipart!"));

    trace!("Fields for server test: {:?}", fields);

    while let Ok(Some(mut field)) = multipart.read_entry() {
        match field.data {
            MultipartData::Text(text) => {
                let test_text = fields.texts.remove(&field.name);

                assert!(
                    test_text.is_some(),
                    "Got text field that wasn't in original dataset: {:?} : {:?} ",
                    field.name, text
                );

                let test_text = test_text.unwrap();

                assert!(
                    text == test_text,
                    "Unexpected data for field {:?}: Expected {:?}, got {:?}",
                    field.name, test_text, text
                );

            },
            MultipartData::File(ref mut file) => {
                let test_bytes = fields.files.remove(&field.name).unwrap();

                let mut bytes = Vec::with_capacity(test_bytes.len());
                file.read_to_end(&mut bytes).unwrap();

                assert!(bytes == test_bytes, "Unexpected data for file {:?}: Expected {:?}, Got {:?}",
                        field.name, String::from_utf8_lossy(&test_bytes), String::from_utf8_lossy(&bytes)
                );
            },
        }
    }

    assert!(fields.texts.is_empty(), "Text fields were not exhausted! Text fields: {:?}", fields.texts);
    assert!(fields.files.is_empty(), "File fields were not exhausted! File fields: {:?}", fields.files);
}

#[derive(Default, Debug)]
pub struct MockClientRequest {
    boundary: Option<String>,
    content_len: Option<u64>,
}

impl ClientRequest for MockClientRequest {
    type Stream = HttpBuffer;
    type Error = io::Error;

    fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool {
        self.boundary = Some(boundary.into());
        self.content_len = content_len;
        true
    }

    fn open_stream(self) -> Result<HttpBuffer, io::Error> {
        debug!("MockClientRequest::open_stream called! {:?}", self);
        let boundary = self.boundary.expect("HttpRequest::set_headers() was not called!");

        Ok(HttpBuffer { buf: Vec::new(), boundary: boundary, content_len: self.content_len })
    }
}

#[derive(Debug)]
pub struct HttpBuffer {
    buf: Vec<u8>,
    boundary: String,
    content_len: Option<u64>,
}

impl HttpBuffer {
    pub fn for_server(&self) -> ServerBuffer {
        ServerBuffer {
            data: &self.buf,
            boundary: &self.boundary,
            content_len: self.content_len,
            rng: rand::thread_rng(),
        }
    }
}

impl Write for HttpBuffer {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.buf.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.buf.flush()
    }
}

impl Read for HttpBuffer {
    fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
        unimplemented!()
    }
}

impl ClientStream for HttpBuffer {
    type Request = MockClientRequest;
    type Response = HttpBuffer;
    type Error = io::Error;

    fn finish(self) -> Result<Self, io::Error> { Ok(self) }
}

pub struct ServerBuffer<'a> {
    data: &'a [u8],
    boundary: &'a str,
    content_len: Option<u64>,
    rng: ThreadRng,
}

impl<'a> Read for ServerBuffer<'a> {
    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
        // Simulate the randomness of a network connection by not always reading everything
        let len = self.rng.gen_range(1, out.len());
        self.data.read(&mut out[..len])
    }
}

impl<'a> ServerRequest for ServerBuffer<'a> {
    type Body = Self;

    fn multipart_boundary(&self) -> Option<&str> { Some(&self.boundary) }

    fn body(self) -> Self::Body {
        self
    }
}

<|endoftext|>


<commit_before>mod grammar {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

use chrono::Duration;
use chrono::prelude::*;
use std::error::Error;
use std::fmt;
use util::Rounding;

#[derive(Debug)]
pub enum ParseError {
    PE(grammar::ParseError),
    Overflow,
}

impl From<grammar::ParseError> for ParseError {
    fn from(err: grammar::ParseError) -> ParseError {
        ParseError::PE(err)
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::PE(ref err) => err.description(),
            ParseError::Overflow => "Invalid input",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::PE(ref err) => Some(err as &Error),
            ParseError::Overflow => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::PE(ref err) => fmt::Display::fmt(err, f),
            ParseError::Overflow => fmt::Display::fmt(self.description(), f),
        }
    }
}

pub fn parse_rounding(fmt: &str) -> Result<Rounding, ParseError> {
    let res = try!(grammar::rounding(fmt));
    return Ok(res);
}

pub fn parse_offset(offset: &str,
                    time: DateTime<FixedOffset>)
                    -> Result<DateTime<FixedOffset>, ParseError> {
    let offset = try!(grammar::offset(offset));
    let offset = Duration::seconds(offset as i64);
    time.checked_add_signed(offset).ok_or(ParseError::Overflow)

}

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

    #[test]
    fn parse_rounding_test() {
        assert_eq!(parse_rounding("+15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("U15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("u15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("7.5m").unwrap(), Rounding::Up(450.0));

        assert_eq!(parse_rounding("-30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("D30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("d7.5m").unwrap(), Rounding::Down(450.0));

        assert_eq!(parse_rounding("=1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("H1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("h7.5m").unwrap(), Rounding::Half(450.0));

        assert_eq!(parse_rounding("+30S").unwrap(), Rounding::Up(30.0));
        assert_eq!(parse_rounding("D30s").unwrap(), Rounding::Down(30.0));
        assert_eq!(parse_rounding("h30s").unwrap(), Rounding::Half(30.0));
        assert_eq!(parse_rounding("H1d").unwrap(), Rounding::Half(86400.0));
    }

    #[test]
    fn offset_test() {
        assert_eq!(grammar::offset("2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("+2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("1.55h").unwrap(), 5580.0);
        assert_eq!(grammar::offset("2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("+2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("-2:22").unwrap(), -8520.0);
    }

    #[test]
    fn parse_offset_test() {
        let ctime = now();
        let n = ctime.checked_add_signed(Duration::seconds(5580)).unwrap();
        assert_eq!(parse_offset("1.55h", ctime).unwrap(), n);
    }
}
<commit_msg>Added use timeclock:now to silence RLS warning<commit_after>mod grammar {
    include!(concat!(env!("OUT_DIR"), "/grammar.rs"));
}

use chrono::Duration;
use chrono::prelude::*;
use std::error::Error;
use std::fmt;
use util::Rounding;

#[derive(Debug)]
pub enum ParseError {
    PE(grammar::ParseError),
    Overflow,
}

impl From<grammar::ParseError> for ParseError {
    fn from(err: grammar::ParseError) -> ParseError {
        ParseError::PE(err)
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::PE(ref err) => err.description(),
            ParseError::Overflow => "Invalid input",
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            ParseError::PE(ref err) => Some(err as &Error),
            ParseError::Overflow => None,
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::PE(ref err) => fmt::Display::fmt(err, f),
            ParseError::Overflow => fmt::Display::fmt(self.description(), f),
        }
    }
}

pub fn parse_rounding(fmt: &str) -> Result<Rounding, ParseError> {
    let res = try!(grammar::rounding(fmt));
    return Ok(res);
}

pub fn parse_offset(offset: &str,
                    time: DateTime<FixedOffset>)
                    -> Result<DateTime<FixedOffset>, ParseError> {
    let offset = try!(grammar::offset(offset));
    let offset = Duration::seconds(offset as i64);
    time.checked_add_signed(offset).ok_or(ParseError::Overflow)

}

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

    #[test]
    fn parse_rounding_test() {
        assert_eq!(parse_rounding("+15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("U15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("u15m").unwrap(), Rounding::Up(900.0));
        assert_eq!(parse_rounding("7.5m").unwrap(), Rounding::Up(450.0));

        assert_eq!(parse_rounding("-30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("D30m").unwrap(), Rounding::Down(1800.0));
        assert_eq!(parse_rounding("d7.5m").unwrap(), Rounding::Down(450.0));

        assert_eq!(parse_rounding("=1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("H1h").unwrap(), Rounding::Half(3600.0));
        assert_eq!(parse_rounding("h7.5m").unwrap(), Rounding::Half(450.0));

        assert_eq!(parse_rounding("+30S").unwrap(), Rounding::Up(30.0));
        assert_eq!(parse_rounding("D30s").unwrap(), Rounding::Down(30.0));
        assert_eq!(parse_rounding("h30s").unwrap(), Rounding::Half(30.0));
        assert_eq!(parse_rounding("H1d").unwrap(), Rounding::Half(86400.0));
    }

    #[test]
    fn offset_test() {
        assert_eq!(grammar::offset("2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("+2.3s").unwrap(), 2.3);
        assert_eq!(grammar::offset("1.55h").unwrap(), 5580.0);
        assert_eq!(grammar::offset("2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("+2:22").unwrap(), 8520.0);
        assert_eq!(grammar::offset("-2:22").unwrap(), -8520.0);
    }

    #[test]
    fn parse_offset_test() {
        let ctime = now();
        let n = ctime.checked_add_signed(Duration::seconds(5580)).unwrap();
        assert_eq!(parse_offset("1.55h", ctime).unwrap(), n);
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crypto;
use crypto::{CipherText, CipherTextRef, PlainText};
use hash::Hash;
use hash::tree::HashRef;

use super::ChunkRef;
use capnp;

pub struct Blob {
    master_key: crypto::FixedKey,
    chunks: CipherText,
    footer: Vec<u8>,
    overhead: usize,
    max_size: usize,
}

impl Blob {
    pub fn new(max_size: usize) -> Blob {
        // TODO(jos): Plug an actual crypto key through somehow.
        let pubkey = crypto::sealed::desc::PublicKey::from_slice(&[215, 136, 80, 128, 158, 109,
                                                                   227, 141, 219, 63, 118, 91,
                                                                   123, 97, 1, 97, 65, 237, 62,
                                                                   171, 83, 159, 200, 11, 68,
                                                                   138, 40, 82, 24, 47, 187, 29])
            .unwrap();
        let seckey = crypto::sealed::desc::SecretKey::from_slice(&[94, 13, 181, 81, 97, 87, 76,
                                                                   37, 53, 92, 120, 232, 17, 126,
                                                                   234, 78, 12, 23, 141, 61, 40,
                                                                   10, 136, 127, 103, 192, 255,
                                                                   193, 142, 154, 101, 35])
            .unwrap();

        let master_key = crypto::FixedKey::new(pubkey, Some(seckey));

        Blob {
            master_key: master_key,
            chunks: CipherText::new(Vec::with_capacity(max_size)),
            footer: Vec::with_capacity(max_size / 2),
            overhead: crypto::sealed::desc::overhead(),
            max_size: max_size,
        }
    }

    pub fn read_chunk(blob: &[u8], hash: &Hash, cref: &ChunkRef) -> Result<Vec<u8>, String> {
        let ct = crypto::CipherTextRef::new(blob);
        match crypto::RefKey::unseal(hash, cref, ct) {
            Ok(pt) => Ok(pt.into_vec()),
            Err(()) => {
                Err(From::from(format!("unseal failed: wrong key or corrupt data (offset: {})",
                                       cref.offset)))
            }
        }
    }

    pub fn upperbound_len(&self) -> usize {
        if self.chunks.len() == 0 {
            0
        } else {
            self.chunks.len() + self.footer.len() + self.overhead
        }
    }

    pub fn try_append(&mut self, chunk: Vec<u8>, mut href: &mut HashRef) -> Result<(), Vec<u8>> {
        let mut ct = crypto::RefKey::seal(&mut href, PlainText::new(chunk));

        href.persistent_ref.offset = self.chunks.len();
        let mut href_bytes = href.as_bytes();
        assert!(href_bytes.len() < 255);

        if self.upperbound_len() + href_bytes.len() + ct.len() >= self.max_size {
            href.persistent_ref.offset = 0;
            let chunk = crypto::RefKey::unseal(&href.hash, &href.persistent_ref, ct.as_ref())
                .unwrap()
                .into_vec();
            if self.chunks.len() == 0 {
                panic!("Can never fit chunk of size {} in blob of size {}",
                       chunk.len(),
                       self.max_size);
            }
            return Err(chunk);
        }

        ct.empty_into(&mut self.chunks);

        // Generate footer entry.
        self.footer.push(href_bytes.len() as u8);
        self.footer.append(&mut href_bytes);

        Ok(())
    }

    pub fn into_bytes(&mut self, mut out: &mut Vec<u8>) {
        if self.chunks.len() == 0 {
            return;
        }

        let mut ct = CipherText::from(&mut self.chunks);
        ct.append(&mut self.chunks);

        let mut footer = self.master_key.seal(PlainText::from_vec(&mut self.footer));

        assert!(ct.len() + footer.len() <= self.max_size);
        ct.random_pad_upto(self.max_size - footer.len());
        ct.append(&mut footer);

        // Everything has been reset. We are ready to go again.
        assert_eq!(0, self.chunks.len());
        assert_eq!(0, self.footer.len());

        out.append(&mut ct.into_vec());
    }

    pub fn refs_from_bytes(&self, bytes: &[u8]) -> Result<Vec<HashRef>, capnp::Error> {
        if bytes.len() == 0 {
            return Ok(Vec::new());
        }

        let (_rest, footer_vec) = self.master_key.unseal(CipherTextRef::new(bytes)).unwrap();
        let mut footer_pos = footer_vec.as_bytes();

        let mut hrefs = Vec::new();
        while footer_pos.len() > 0 {
            let len = footer_pos[0] as usize;
            assert!(footer_pos.len() > len);

            hrefs.push(try!(HashRef::from_bytes(&mut &footer_pos[1..1 + len])));
            footer_pos = &footer_pos[len + 1..];
        }

        Ok(hrefs)
    }
}
<commit_msg>Correct padding: 1 extra byte for size.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crypto;
use crypto::{CipherText, CipherTextRef, PlainText};
use hash::Hash;
use hash::tree::HashRef;

use super::ChunkRef;
use capnp;

pub struct Blob {
    master_key: crypto::FixedKey,
    chunks: CipherText,
    footer: Vec<u8>,
    overhead: usize,
    max_size: usize,
}

impl Blob {
    pub fn new(max_size: usize) -> Blob {
        // TODO(jos): Plug an actual crypto key through somehow.
        let pubkey = crypto::sealed::desc::PublicKey::from_slice(&[215, 136, 80, 128, 158, 109,
                                                                   227, 141, 219, 63, 118, 91,
                                                                   123, 97, 1, 97, 65, 237, 62,
                                                                   171, 83, 159, 200, 11, 68,
                                                                   138, 40, 82, 24, 47, 187, 29])
            .unwrap();
        let seckey = crypto::sealed::desc::SecretKey::from_slice(&[94, 13, 181, 81, 97, 87, 76,
                                                                   37, 53, 92, 120, 232, 17, 126,
                                                                   234, 78, 12, 23, 141, 61, 40,
                                                                   10, 136, 127, 103, 192, 255,
                                                                   193, 142, 154, 101, 35])
            .unwrap();

        let master_key = crypto::FixedKey::new(pubkey, Some(seckey));

        Blob {
            master_key: master_key,
            chunks: CipherText::new(Vec::with_capacity(max_size)),
            footer: Vec::with_capacity(max_size / 2),
            overhead: crypto::sealed::desc::overhead(),
            max_size: max_size,
        }
    }

    pub fn read_chunk(blob: &[u8], hash: &Hash, cref: &ChunkRef) -> Result<Vec<u8>, String> {
        let ct = crypto::CipherTextRef::new(blob);
        match crypto::RefKey::unseal(hash, cref, ct) {
            Ok(pt) => Ok(pt.into_vec()),
            Err(()) => {
                Err(From::from(format!("unseal failed: wrong key or corrupt data (offset: {})",
                                       cref.offset)))
            }
        }
    }

    pub fn upperbound_len(&self) -> usize {
        if self.chunks.len() == 0 {
            0
        } else {
            self.chunks.len() + self.footer.len() + self.overhead
        }
    }

    pub fn try_append(&mut self, chunk: Vec<u8>, mut href: &mut HashRef) -> Result<(), Vec<u8>> {
        let mut ct = crypto::RefKey::seal(&mut href, PlainText::new(chunk));

        href.persistent_ref.offset = self.chunks.len();
        let mut href_bytes = href.as_bytes();
        assert!(href_bytes.len() < 255);

        if self.upperbound_len() + 1 + href_bytes.len() + ct.len() >= self.max_size {
            href.persistent_ref.offset = 0;
            let chunk = crypto::RefKey::unseal(&href.hash, &href.persistent_ref, ct.as_ref())
                .unwrap()
                .into_vec();
            if self.chunks.len() == 0 {
                panic!("Can never fit chunk of size {} in blob of size {}",
                       chunk.len(),
                       self.max_size);
            }
            return Err(chunk);
        }

        ct.empty_into(&mut self.chunks);

        // Generate footer entry.
        self.footer.push(href_bytes.len() as u8);
        self.footer.append(&mut href_bytes);

        Ok(())
    }

    pub fn into_bytes(&mut self, mut out: &mut Vec<u8>) {
        if self.chunks.len() == 0 {
            return;
        }

        let mut ct = CipherText::from(&mut self.chunks);
        ct.append(&mut self.chunks);

        let mut footer = self.master_key.seal(PlainText::from_vec(&mut self.footer));

        assert!(ct.len() + footer.len() <= self.max_size);
        ct.random_pad_upto(self.max_size - footer.len());
        ct.append(&mut footer);

        // Everything has been reset. We are ready to go again.
        assert_eq!(0, self.chunks.len());
        assert_eq!(0, self.footer.len());

        out.append(&mut ct.into_vec());
    }

    pub fn refs_from_bytes(&self, bytes: &[u8]) -> Result<Vec<HashRef>, capnp::Error> {
        if bytes.len() == 0 {
            return Ok(Vec::new());
        }

        let (_rest, footer_vec) = self.master_key.unseal(CipherTextRef::new(bytes)).unwrap();
        let mut footer_pos = footer_vec.as_bytes();

        let mut hrefs = Vec::new();
        while footer_pos.len() > 0 {
            let len = footer_pos[0] as usize;
            assert!(footer_pos.len() > len);

            hrefs.push(try!(HashRef::from_bytes(&mut &footer_pos[1..1 + len])));
            footer_pos = &footer_pos[len + 1..];
        }

        Ok(hrefs)
    }
}
<|endoftext|>


<commit_before>#![crate_id="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]

extern crate debug;
extern crate term;
extern crate url;
extern crate serialize;
extern crate semver;
extern crate toml;

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

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

#[cfg(test)]
extern crate hamcrest;

use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use std::io;
use std::io::{stdout, stderr};
use std::io::stdio::{stdout_raw, stderr_raw};
use hammer::{Flags, decode_args, usage};

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

#[deriving(Decodable)]
pub struct NoFlags;

hammer_config!(NoFlags)

#[deriving(Decodable)]
pub struct GlobalFlags {
    verbose: bool,
    help: bool,
    rest: Vec<String>
}

hammer_config!(GlobalFlags |c| {
    c.short("verbose", 'v').short("help", 'h')
})

pub fn execute_main<'a,
                    T: Flags,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>)
{
    fn call<'a,
            T: Flags,
            U: RepresentsJSON,
            V: Encodable<json::Encoder<'a>, io::IoError>>(
                exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                shell: &mut MultiShell,
                args: &[String])
        -> CliResult<Option<V>>
    {
        let flags = try!(flags_from_args::<T>(args));
        let json = try!(json_from_stdin::<U>());

        exec(flags, json, shell)
    }

    process::<T, V>(|rest, shell| call(exec, shell, rest));
}

pub fn execute_main_without_stdin<'a,
                                  T: Flags,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>)
{
    fn call<'a,
            T: Flags,
            V: Encodable<json::Encoder<'a>, io::IoError>>(
                exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                shell: &mut MultiShell,
                args: &[String])
        -> CliResult<Option<V>>
    {
        let flags = try!(flags_from_args::<T>(args));
        exec(flags, shell)
    }

    process::<T, V>(|rest, shell| call(exec, shell, rest));
}

fn process<'a,
           T: Flags,
           V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {

    match global_flags() {
        Err(e) => handle_error(e, &mut shell(false)),
        Ok(val) => {
            let mut shell = shell(val.verbose);

            if val.help {
                let (desc, options) = usage::<T>(true);

                desc.map(|d| println!("{}\n", d));

                println!("Options:\n");

                print!("{}", options);

                let (_, options) = usage::<GlobalFlags>(false);
                print!("{}", options);
            } else {
                process_executed(callback(val.rest.as_slice(), &mut shell), &mut shell)
            }
        }
    }
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell)
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::Encoder::str_encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown, .. } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else {
        let _ = shell.error(error.to_str());
    }

    if error.cause().is_some() {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        let _ = handle_cause(error, shell);
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

fn args() -> Vec<String> {
    std::os::args()
}

fn flags_from_args<T: Flags>(args: &[String]) -> CliResult<T> {
    decode_args(args).map_err(|e| {
        CliError::new(e.message, 1)
    })
}

fn global_flags() -> CliResult<GlobalFlags> {
    decode_args(args().tail()).map_err(|e| {
        CliError::new(e.message, 1)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_str().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<commit_msg>Fixing deprecated JSON call<commit_after>#![crate_id="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]

extern crate debug;
extern crate term;
extern crate url;
extern crate serialize;
extern crate semver;
extern crate toml;

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

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

#[cfg(test)]
extern crate hamcrest;

use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use std::io;
use std::io::{stdout, stderr};
use std::io::stdio::{stdout_raw, stderr_raw};
use hammer::{Flags, decode_args, usage};

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

#[deriving(Decodable)]
pub struct NoFlags;

hammer_config!(NoFlags)

#[deriving(Decodable)]
pub struct GlobalFlags {
    verbose: bool,
    help: bool,
    rest: Vec<String>
}

hammer_config!(GlobalFlags |c| {
    c.short("verbose", 'v').short("help", 'h')
})

pub fn execute_main<'a,
                    T: Flags,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>)
{
    fn call<'a,
            T: Flags,
            U: RepresentsJSON,
            V: Encodable<json::Encoder<'a>, io::IoError>>(
                exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                shell: &mut MultiShell,
                args: &[String])
        -> CliResult<Option<V>>
    {
        let flags = try!(flags_from_args::<T>(args));
        let json = try!(json_from_stdin::<U>());

        exec(flags, json, shell)
    }

    process::<T, V>(|rest, shell| call(exec, shell, rest));
}

pub fn execute_main_without_stdin<'a,
                                  T: Flags,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>)
{
    fn call<'a,
            T: Flags,
            V: Encodable<json::Encoder<'a>, io::IoError>>(
                exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                shell: &mut MultiShell,
                args: &[String])
        -> CliResult<Option<V>>
    {
        let flags = try!(flags_from_args::<T>(args));
        exec(flags, shell)
    }

    process::<T, V>(|rest, shell| call(exec, shell, rest));
}

fn process<'a,
           T: Flags,
           V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {

    match global_flags() {
        Err(e) => handle_error(e, &mut shell(false)),
        Ok(val) => {
            let mut shell = shell(val.verbose);

            if val.help {
                let (desc, options) = usage::<T>(true);

                desc.map(|d| println!("{}\n", d));

                println!("Options:\n");

                print!("{}", options);

                let (_, options) = usage::<GlobalFlags>(false);
                print!("{}", options);
            } else {
                process_executed(callback(val.rest.as_slice(), &mut shell), &mut shell)
            }
        }
    }
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell)
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown, .. } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else {
        let _ = shell.error(error.to_str());
    }

    if error.cause().is_some() {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        let _ = handle_cause(error, shell);
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

fn args() -> Vec<String> {
    std::os::args()
}

fn flags_from_args<T: Flags>(args: &[String]) -> CliResult<T> {
    decode_args(args).map_err(|e| {
        CliError::new(e.message, 1)
    })
}

fn global_flags() -> CliResult<GlobalFlags> {
    decode_args(args().tail()).map_err(|e| {
        CliError::new(e.message, 1)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_str().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error,Result, Source};
use html::HtmlRenderer;
use book::{Book, compile_str};
use token::Token;
use templates::img;
use resource_handler;
use renderer::Renderer;
use parser::Parser;

use std::io::{Read,Write};
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::borrow::Cow;
use std::convert::{AsRef, AsMut};

/// Multiple files HTML renderer
///
/// Renders HTML in a given directory.
pub struct HtmlDirRenderer<'a> {
    html: HtmlRenderer<'a>,
}

impl<'a> HtmlDirRenderer<'a> {
    /// Creates a new HtmlDirRenderer
    pub fn new(book: &'a Book) -> HtmlDirRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.handler.set_images_mapping(true);
        html.handler.set_base64(false);
        HtmlDirRenderer {
            html: html,
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<()> {
        // Add internal files to resource handler
        for (i, filename) in self.html.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        // Create the directory
        let dest_path = try!(self.html.book.options.get_path("output.html_dir"));
        match fs::metadata(&dest_path) {
            Ok(metadata) => if metadata.is_file() {
                return Err(Error::render(&self.html.book.source,
                                         format!("{} already exists and is not a directory", &dest_path)));
            } else if metadata.is_dir() {
                self.html.book.logger.warning(format!("{} already exists, deleting it", &dest_path));
                try!(fs::remove_dir_all(&dest_path)
                     .map_err(|e| Error::render(&self.html.book.source,
                                                format!("error deleting directory {}: {}", &dest_path, e))));
            },
            Err(_) => (),
        }
        try!(fs::DirBuilder::new()
             .recursive(true)
             .create(&dest_path)
             .map_err(|e| Error::render(&self.html.book.source,
                                        format!("could not create HTML directory {}:{}", &dest_path, e))));

        // Write CSS
        try!(self.write_css());
        // Write print.css
        try!(self.write_file("print.css",
                             &self.html.book.get_template("html.css.print").unwrap().as_bytes()));
        // Write index.html and chapter_xxx.html
        try!(self.write_html());
        // Write menu.svg
        try!(self.write_file("menu.svg", img::MENU_SVG));

        // Write highlight files if they are needed
        if self.html.book.options.get_bool("html.highlight_code") == Ok(true) {
            try!(self.write_file("highlight.js", self.html.book.get_template("html.highlight.js").unwrap().as_bytes()));
            try!(self.write_file("highlight.css", self.html.book.get_template("html.highlight.css").unwrap().as_bytes()));
        }

        // Write all images (including cover)
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.html.book.source,
                                                                                "image or cover",
                                                                                source.clone())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.html.book.source,
                                                                       format!("error while reading image file {}: {}", source, e))));
            try!(self.write_file(dest, &content));
        }

        // Write additional files
        if let Ok(list) = self.html.book.options.get_paths_list("resources.files") {
            let files_path = self.html.book.options.get_path("resources.base_path.files").unwrap();
            let data_path = Path::new(self.html.book.options.get_relative_path("resources.out_path").unwrap());
            let list = try!(resource_handler::get_files(list, &files_path));
            for path in list {
                let abs_path = Path::new(&files_path).join(&path);
                let mut f = try!(File::open(&abs_path)
                                 .map_err(|_| Error::file_not_found(&self.html.book.source,
                                                                    "additional resource from resources.files",
                                                                    abs_path.to_string_lossy().into_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.html.book.source,
                                                                           format!("error while reading resource file: {}", e))));
                try!(self.write_file(data_path.join(&path).to_str().unwrap(), &content));
            }
        }

        Ok(())
    }

    // Render each chapter and write them, and index.html too
    fn write_html(&mut self) -> Result<()> {
        let mut chapters = vec!();
        let mut titles = vec!();
        for (i, &(n, ref v)) in self.html.book.chapters.iter().enumerate() {
            self.html.chapter_config(i, n, filenamer(i));
            let mut title = String::new();
            for token in v {
                match *token {
                    Token::Header(1, ref vec) => {
                        if self.html.current_hide || self.html.current_numbering == 0 {
                            title = try!(self.html.render_vec(vec));
                        } else {
                            title = try!(self.html.book.get_chapter_header(self.html.current_chapter[0] + 1,
                                                                           try!(self.html.render_vec(vec)),
                                                                           |s| self.render_vec(&try!(Parser::new().parse_inline(s)))));
                        }
                        break;
                    },
                    _ => {
                        continue;
                    }
                }
            }
            titles.push(title);

            let chapter = HtmlRenderer::render_html(self, v, true);
            chapters.push(chapter);
        }
        self.html.source = Source::empty();
        let toc = self.html.toc.render();

        for (i, content) in chapters.into_iter().enumerate() {
            let prev_chapter = if i > 0 {
                format!("

    « {}

",
                        filenamer(i-1),
                        titles[i-1])
            } else {
                String::new()
            };

            let next_chapter = if i < titles.len() - 1 {
                format!("

    {} »

",
                        filenamer(i+1),
                        titles[i+1])
            } else {
                String::new()
            };

            // Render each HTML document
            let mut mapbuilder = try!(self.html.book.get_metadata(|s| self.render_vec(&try!(Parser::new().parse_inline(s)))))
                .insert_str("content", try!(content))
                .insert_str("chapter_title", format!("{} – {}",
                                             self.html.book.options.get_str("title").unwrap(),
                                             titles[i]))
                .insert_str("toc", toc.clone())
                .insert_str("prev_chapter", prev_chapter)
                .insert_str("next_chapter", next_chapter)
                .insert_str("footer", try!(HtmlRenderer::get_footer(self)))
                .insert_str("header", try!(HtmlRenderer::get_header(self)))
                .insert_str("script", self.html.book.get_template("html.js").unwrap())
                .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);

            if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
                mapbuilder = mapbuilder.insert_bool("highlight_code", true);
            }
            let data = mapbuilder.build();
            let template = try!(compile_str(try!(self.html.book.get_template("html_dir.chapter.html")).as_ref(),
                                            &self.html.book.source,
                                            "could not compile template 'html_dir.chapter.html"));
            let mut res = vec!();
            template.render_data(&mut res, &data);
            try!(self.write_file(&filenamer(i), &res));
        }

        let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") {
            // checks first that cover exists
            if fs::metadata(&cover).is_err() {
                return Err(Error::file_not_found(&self.html.book.source,
                                               "cover",
                                                 cover));

            }
            format!("

",
                    self.html.book.options.get_str("title").unwrap(),
                    try!(self.html.handler.map_image(&self.html.book.source,
                                                     Cow::Owned(cover))).as_ref())
        } else {
            String::new()
        };

        // Insert toc inline if option is set
        if self.html.book.options.get_bool("rendering.inline_toc").unwrap() {

            content.push_str(&format!(
                "
{}

{}

",
                try!(self.html.get_toc_name()),
                &toc));
        }

        if titles.len() > 1 {
            content.push_str(&format!("

    {} »

",
                        filenamer(0),
                        titles[0]));
        }
        // Render index.html and write it too
        let mut mapbuilder = try!(self.html.book.get_metadata(|s| self.render_vec(&try!(Parser::new().parse_inline(s)))))
            .insert_str("content", content)
            .insert_str("header", try!(HtmlRenderer::get_header(self)))
            .insert_str("footer", try!(HtmlRenderer::get_footer(self)))
            .insert_str("toc", toc.clone())
            .insert_str("script", self.html.book.get_template("html.js").unwrap())
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
        if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
            mapbuilder = mapbuilder.insert_bool("highlight_code", true);
        }
        let data = mapbuilder.build();
        let template = try!(compile_str(try!(self.html.book.get_template("html_dir.index.html")).as_ref(),
                                        &self.html.book.source,
                                        "could not compile template 'html_dir.index.html"));
        let mut res = vec!();
        template.render_data(&mut res, &data);
        try!(self.write_file("index.html", &res));

        Ok(())
    }

    // Render the CSS file and write it
    fn write_css(&self) -> Result<()> {
        // Render the CSS
        let template_css = try!(compile_str(try!(self.html.book.get_template("html.css")).as_ref(),
                                            &self.html.book.source,
                                            "could not compile template 'html.css"));
        let data = try!(self.html.book.get_metadata(|s| Ok(s.to_owned())))
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);

        // Write it
        self.write_file("stylesheet.css", css.as_bytes())
    }

    // Write content to a file
    fn write_file(&self, file: &str, content: &[u8]) -> Result<()> {
        let dest_path = PathBuf::from(&self.html.book.options.get_path("output.html_dir").unwrap());
        if dest_path.starts_with("..") {
            panic!("html dir is asked to create a file outside of its directory, no way!");
        }
        let dest_file = dest_path.join(file);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() { // dir does not exist, create it
            try!(fs::DirBuilder::new()
                 .recursive(true)
                 .create(&dest_dir)
                 .map_err(|e| Error::render(&self.html.book.source,
                                            format!("could not create directory in {}:{}", dest_dir.display(), e))));
        }
        let mut f = try!(File::create(&dest_file)
                         .map_err(|e| Error::render(&self.html.book.source,
                                                   format!("could not create file {}:{}", dest_file.display(), e))));
        f.write_all(content)
            .map_err(|e| Error::render(&self.html.book.source,
                                       format!("could not write to file {}:{}", dest_file.display(), e)))
    }
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.html", i)
}

derive_html!{HtmlDirRenderer<'a>, HtmlRenderer::static_render_token}
<commit_msg>Compile html_dir.chapter.html template only once<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Caribon is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received ba copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

use error::{Error,Result, Source};
use html::HtmlRenderer;
use book::{Book, compile_str};
use token::Token;
use templates::img;
use resource_handler;
use renderer::Renderer;
use parser::Parser;

use std::io::{Read,Write};
use std::fs;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::borrow::Cow;
use std::convert::{AsRef, AsMut};

/// Multiple files HTML renderer
///
/// Renders HTML in a given directory.
pub struct HtmlDirRenderer<'a> {
    html: HtmlRenderer<'a>,
}

impl<'a> HtmlDirRenderer<'a> {
    /// Creates a new HtmlDirRenderer
    pub fn new(book: &'a Book) -> HtmlDirRenderer<'a> {
        let mut html = HtmlRenderer::new(book);
        html.handler.set_images_mapping(true);
        html.handler.set_base64(false);
        HtmlDirRenderer {
            html: html,
        }
    }

    /// Render a book
    pub fn render_book(&mut self) -> Result<()> {
        // Add internal files to resource handler
        for (i, filename) in self.html.book.filenames.iter().enumerate() {
            self.html.handler.add_link(filename.clone(), filenamer(i));
        }

        // Create the directory
        let dest_path = try!(self.html.book.options.get_path("output.html_dir"));
        match fs::metadata(&dest_path) {
            Ok(metadata) => if metadata.is_file() {
                return Err(Error::render(&self.html.book.source,
                                         format!("{} already exists and is not a directory", &dest_path)));
            } else if metadata.is_dir() {
                self.html.book.logger.warning(format!("{} already exists, deleting it", &dest_path));
                try!(fs::remove_dir_all(&dest_path)
                     .map_err(|e| Error::render(&self.html.book.source,
                                                format!("error deleting directory {}: {}", &dest_path, e))));
            },
            Err(_) => (),
        }
        try!(fs::DirBuilder::new()
             .recursive(true)
             .create(&dest_path)
             .map_err(|e| Error::render(&self.html.book.source,
                                        format!("could not create HTML directory {}:{}", &dest_path, e))));

        // Write CSS
        try!(self.write_css());
        // Write print.css
        try!(self.write_file("print.css",
                             &self.html.book.get_template("html.css.print").unwrap().as_bytes()));
        // Write index.html and chapter_xxx.html
        try!(self.write_html());
        // Write menu.svg
        try!(self.write_file("menu.svg", img::MENU_SVG));

        // Write highlight files if they are needed
        if self.html.book.options.get_bool("html.highlight_code") == Ok(true) {
            try!(self.write_file("highlight.js", self.html.book.get_template("html.highlight.js").unwrap().as_bytes()));
            try!(self.write_file("highlight.css", self.html.book.get_template("html.highlight.css").unwrap().as_bytes()));
        }

        // Write all images (including cover)
        for (source, dest) in self.html.handler.images_mapping() {
            let mut f = try!(File::open(source).map_err(|_| Error::file_not_found(&self.html.book.source,
                                                                                "image or cover",
                                                                                source.clone())));
            let mut content = vec!();
            try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.html.book.source,
                                                                       format!("error while reading image file {}: {}", source, e))));
            try!(self.write_file(dest, &content));
        }

        // Write additional files
        if let Ok(list) = self.html.book.options.get_paths_list("resources.files") {
            let files_path = self.html.book.options.get_path("resources.base_path.files").unwrap();
            let data_path = Path::new(self.html.book.options.get_relative_path("resources.out_path").unwrap());
            let list = try!(resource_handler::get_files(list, &files_path));
            for path in list {
                let abs_path = Path::new(&files_path).join(&path);
                let mut f = try!(File::open(&abs_path)
                                 .map_err(|_| Error::file_not_found(&self.html.book.source,
                                                                    "additional resource from resources.files",
                                                                    abs_path.to_string_lossy().into_owned())));
                let mut content = vec!();
                try!(f.read_to_end(&mut content).map_err(|e| Error::render(&self.html.book.source,
                                                                           format!("error while reading resource file: {}", e))));
                try!(self.write_file(data_path.join(&path).to_str().unwrap(), &content));
            }
        }

        Ok(())
    }

    // Render each chapter and write them, and index.html too
    fn write_html(&mut self) -> Result<()> {
        let mut chapters = vec!();
        let mut titles = vec!();
        for (i, &(n, ref v)) in self.html.book.chapters.iter().enumerate() {
            self.html.chapter_config(i, n, filenamer(i));
            let mut title = String::new();
            for token in v {
                match *token {
                    Token::Header(1, ref vec) => {
                        if self.html.current_hide || self.html.current_numbering == 0 {
                            title = try!(self.html.render_vec(vec));
                        } else {
                            title = try!(self.html.book.get_chapter_header(self.html.current_chapter[0] + 1,
                                                                           try!(self.html.render_vec(vec)),
                                                                           |s| self.render_vec(&try!(Parser::new().parse_inline(s)))));
                        }
                        break;
                    },
                    _ => {
                        continue;
                    }
                }
            }
            titles.push(title);

            let chapter = HtmlRenderer::render_html(self, v, true);
            chapters.push(chapter);
        }
        self.html.source = Source::empty();
        let toc = self.html.toc.render();

        // render all chapters
        let template = try!(compile_str(try!(self.html.book.get_template("html_dir.chapter.html")).as_ref(),
                                        &self.html.book.source,
                                        "could not compile template 'html_dir.chapter.html"));
        for (i, content) in chapters.into_iter().enumerate() {
            let prev_chapter = if i > 0 {
                format!("

    « {}

",
                        filenamer(i-1),
                        titles[i-1])
            } else {
                String::new()
            };

            let next_chapter = if i < titles.len() - 1 {
                format!("

    {} »

",
                        filenamer(i+1),
                        titles[i+1])
            } else {
                String::new()
            };

            // Render each HTML document
            let mut mapbuilder = try!(self.html.book.get_metadata(|s| self.render_vec(&try!(Parser::new().parse_inline(s)))))
                .insert_str("content", try!(content))
                .insert_str("chapter_title", format!("{} – {}",
                                             self.html.book.options.get_str("title").unwrap(),
                                             titles[i]))
                .insert_str("toc", toc.clone())
                .insert_str("prev_chapter", prev_chapter)
                .insert_str("next_chapter", next_chapter)
                .insert_str("footer", try!(HtmlRenderer::get_footer(self)))
                .insert_str("header", try!(HtmlRenderer::get_header(self)))
                .insert_str("script", self.html.book.get_template("html.js").unwrap())
                .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);

            if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
                mapbuilder = mapbuilder.insert_bool("highlight_code", true);
            }
            let data = mapbuilder.build();
            let mut res = vec!();
            template.render_data(&mut res, &data);
            try!(self.write_file(&filenamer(i), &res));
        }

        let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") {
            // checks first that cover exists
            if fs::metadata(&cover).is_err() {
                return Err(Error::file_not_found(&self.html.book.source,
                                               "cover",
                                                 cover));

            }
            format!("

",
                    self.html.book.options.get_str("title").unwrap(),
                    try!(self.html.handler.map_image(&self.html.book.source,
                                                     Cow::Owned(cover))).as_ref())
        } else {
            String::new()
        };

        // Insert toc inline if option is set
        if self.html.book.options.get_bool("rendering.inline_toc").unwrap() {

            content.push_str(&format!(
                "
{}

{}

",
                try!(self.html.get_toc_name()),
                &toc));
        }

        if titles.len() > 1 {
            content.push_str(&format!("

    {} »

",
                        filenamer(0),
                        titles[0]));
        }
        // Render index.html and write it too
        let mut mapbuilder = try!(self.html.book.get_metadata(|s| self.render_vec(&try!(Parser::new().parse_inline(s)))))
            .insert_str("content", content)
            .insert_str("header", try!(HtmlRenderer::get_header(self)))
            .insert_str("footer", try!(HtmlRenderer::get_footer(self)))
            .insert_str("toc", toc.clone())
            .insert_str("script", self.html.book.get_template("html.js").unwrap())
            .insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
        if self.html.book.options.get_bool("html.highlight_code").unwrap() == true {
            mapbuilder = mapbuilder.insert_bool("highlight_code", true);
        }
        let data = mapbuilder.build();
        let template = try!(compile_str(try!(self.html.book.get_template("html_dir.index.html")).as_ref(),
                                        &self.html.book.source,
                                        "could not compile template 'html_dir.index.html"));
        let mut res = vec!();
        template.render_data(&mut res, &data);
        try!(self.write_file("index.html", &res));

        Ok(())
    }

    // Render the CSS file and write it
    fn write_css(&self) -> Result<()> {
        // Render the CSS
        let template_css = try!(compile_str(try!(self.html.book.get_template("html.css")).as_ref(),
                                            &self.html.book.source,
                                            "could not compile template 'html.css"));
        let data = try!(self.html.book.get_metadata(|s| Ok(s.to_owned())))
            .build();
        let mut res:Vec<u8> = vec!();
        template_css.render_data(&mut res, &data);
        let css = String::from_utf8_lossy(&res);

        // Write it
        self.write_file("stylesheet.css", css.as_bytes())
    }

    // Write content to a file
    fn write_file(&self, file: &str, content: &[u8]) -> Result<()> {
        let dest_path = PathBuf::from(&self.html.book.options.get_path("output.html_dir").unwrap());
        if dest_path.starts_with("..") {
            panic!("html dir is asked to create a file outside of its directory, no way!");
        }
        let dest_file = dest_path.join(file);
        let dest_dir = dest_file.parent().unwrap();
        if !fs::metadata(dest_dir).is_ok() { // dir does not exist, create it
            try!(fs::DirBuilder::new()
                 .recursive(true)
                 .create(&dest_dir)
                 .map_err(|e| Error::render(&self.html.book.source,
                                            format!("could not create directory in {}:{}", dest_dir.display(), e))));
        }
        let mut f = try!(File::create(&dest_file)
                         .map_err(|e| Error::render(&self.html.book.source,
                                                   format!("could not create file {}:{}", dest_file.display(), e))));
        f.write_all(content)
            .map_err(|e| Error::render(&self.html.book.source,
                                       format!("could not write to file {}:{}", dest_file.display(), e)))
    }
}

/// Generate a file name given an int
fn filenamer(i: usize) -> String {
    format!("chapter_{:03}.html", i)
}

derive_html!{HtmlDirRenderer<'a>, HtmlRenderer::static_render_token}
<|endoftext|>


<commit_before>pub trait PaginatableIterator : Sized {
    fn paginate(self, page: usize) -> Paginate<Self>;
}

impl<T, U : Iterator<Item = T>> PaginatableIterator for U {
    fn paginate(self, page: usize) -> Paginate<U> {
        Paginate {
            iter: self,
            page: page,
        }
    }
}

struct Paginate<I> {
    iter: I,
    page: usize,
}

impl<E, I : Iterator<Item = E>> Iterator for Paginate<I> {
    type Item = Vec<E>;

    fn next(&mut self) -> Option<Vec<E>> {
        let mut r = Vec::new();
        for _ in 0..self.page {
            match self.iter.next() {
                Some(next) => r.push(next),
                None if r.is_empty() => return None,
                None                 => return Some(r),
            }
        }
        Some(r)
    }
}

<commit_msg>git rid of private type warning<commit_after>pub trait PaginatableIterator : Sized {
    fn paginate(self, page: usize) -> Paginate<Self>;
}

impl<T, U : Iterator<Item = T>> PaginatableIterator for U {
    fn paginate(self, page: usize) -> Paginate<U> {
        Paginate {
            iter: self,
            page: page,
        }
    }
}

pub struct Paginate<I> {
    iter: I,
    page: usize,
}

impl<E, I : Iterator<Item = E>> Iterator for Paginate<I> {
    type Item = Vec<E>;

    fn next(&mut self) -> Option<Vec<E>> {
        let mut r = Vec::new();
        for _ in 0..self.page {
            match self.iter.next() {
                Some(next) => r.push(next),
                None if r.is_empty() => return None,
                None                 => return Some(r),
            }
        }
        Some(r)
    }
}

<|endoftext|>