<commit_before>#![allow(dead_code)]

#[cfg(target_os = "linux")]
extern crate i2cdev;
extern crate termion;

#[cfg(target_os = "linux")]
use self::i2cdev::core::I2CDevice;
#[cfg(target_os = "linux")]
use self::i2cdev::linux::LinuxI2CDevice;

use std;
use shared::*;

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

/// Represents a device capable of displaying a rectangular bitmap buffer.
pub trait Display {
    fn show(&mut self, buffer: &[Column]);
}

#[cfg(target_os = "linux")]
/// A Scroll pHAT HD device connected over I2C bus (e.g. on a Raspberry Pi).
pub struct I2CDisplay {
    device: LinuxI2CDevice,
    frame: u8,
}

#[cfg(target_os = "linux")]
impl I2CDisplay {
    /// Creates a new I2CDisplay device using the I2C device identified by the provided
    /// `device_id` (normally 1 or 2).
    pub fn new(device_id: u8) -> I2CDisplay {
        let device_path = format!("/dev/i2c-{}", device_id);
        let d = LinuxI2CDevice::new(device_path, ADDRESS).unwrap();
        let mut display = I2CDisplay {
            device: d,
            frame: 0,
        };
        display.register(CONFIG_BANK, MODE_REGISTER, PICTURE_MODE);
        display
    }

    fn bank(&mut self, bank: u8) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.device.smbus_write_byte_data(BANK_ADDRESS, bank)
    }

    fn register(
        &mut self,
        bank: u8,
        register: u8,
        value: u8,
    ) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.bank(bank);
        self.device.smbus_write_block_data(register, &[value])
    }

    fn frame(&mut self, frame: u8) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame)
    }

    fn reset(&mut self) {
        self.sleep(true);
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false);
    }

    fn sleep(&mut self, value: bool) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 })
    }
}

#[cfg(target_os = "linux")]
impl Display for I2CDisplay {
    fn show(&mut self, buffer: &[Column]) {
        // TODO(tzn): Double buffering.
        // let new_frame = (self.frame + 1) % 2;
        let new_frame = 1;
        self.bank(new_frame);
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.device
                    .smbus_write_byte_data(COLOR_OFFSET + offset as u8, value);
            }
        }
        self.frame(new_frame);
        self.frame = new_frame;
    }
}

/// A virtual display that outputs its buffer to the terminal from which the binary is attached.
///
/// Useful for debugging or prototyping, as it does not require a physical display to be connected.
pub struct TermDisplay {}

impl TermDisplay {
    pub fn new() -> TermDisplay {
        TermDisplay {}
    }
}

impl Display for TermDisplay {
    fn show(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);
        for x in 0..buffer.len() {
            let col = &buffer[x];
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { ' ' } else { '#' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 1, y as u16 + 1), v);
            }
        }
    }
}

/// A virtual display that outputs its buffer to the terminal from which the binary is attached.
///
/// Useful for debugging or prototyping, as it does not require a physical display to be connected.
pub struct UnicodeDisplay {}

impl UnicodeDisplay {
    pub fn new() -> UnicodeDisplay {
        UnicodeDisplay {}
    }
}

impl Display for UnicodeDisplay {
    fn show(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);

        let col = &buffer[0];

        println!("{}╔", termion::cursor::Goto(1, 1));
        for y in 0..col.len() {
            println!("{}║", termion::cursor::Goto(1, y as u16 + 2));
        }
        println!("{}╚", termion::cursor::Goto(1, col.len() as u16 + 2));

        for x in 0..buffer.len() {
            let col = &buffer[x];
            println!("{}═", termion::cursor::Goto(x as u16 + 2, 1));
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { '░' } else { '▓' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 2, y as u16 + 2), v);
            }
            println!(
                "{}═",
                termion::cursor::Goto(x as u16 + 2, col.len() as u16 + 2)
            );
        }

        println!("{}╗", termion::cursor::Goto(buffer.len() as u16 + 1, 1));
        for y in 0..col.len() {
            println!(
                "{}║",
                termion::cursor::Goto(buffer.len() as u16 + 1, y as u16 + 2)
            );
        }
        println!(
            "{}╝",
            termion::cursor::Goto(buffer.len() as u16 + 1, col.len() as u16 + 2)
        );
    }
}
<commit_msg>Fully initialize Scroll pHAT HD when creating I2CDisplay<commit_after>#![allow(dead_code)]

#[cfg(target_os = "linux")]
extern crate i2cdev;
extern crate termion;

#[cfg(target_os = "linux")]
use self::i2cdev::core::I2CDevice;
#[cfg(target_os = "linux")]
use self::i2cdev::linux::LinuxI2CDevice;

use std;
use shared::*;

const MODE_REGISTER: u8 = 0x00;
const FRAME_REGISTER: u8 = 0x01;
const AUTOPLAY1_REGISTER: u8 = 0x02;
const AUTOPLAY2_REGISTER: u8 = 0x03;
const BLINK_REGISTER: u8 = 0x05;
const AUDIOSYNC_REGISTER: u8 = 0x06;
const BREATH1_REGISTER: u8 = 0x08;
const BREATH2_REGISTER: u8 = 0x09;
const SHUTDOWN_REGISTER: u8 = 0x0A;
const GAIN_REGISTER: u8 = 0x0B;
const ADC_REGISTER: u8 = 0x0C;

const CONFIG_BANK: u8 = 0x0B;
const BANK_ADDRESS: u8 = 0xFD;

const PICTURE_MODE: u8 = 0x00;
const AUTOPLAY_MODE: u8 = 0x08;
const AUDIOPLAY_MODE: u8 = 0x18;

const ENABLE_OFFSET: u8 = 0x00;
const BLINK_OFFSET: u8 = 0x12;
const COLOR_OFFSET: u8 = 0x24;

const ADDRESS: u16 = 0x74;

/// Represents a device capable of displaying a rectangular bitmap buffer.
pub trait Display {
    fn show(&mut self, buffer: &[Column]);
}

#[cfg(target_os = "linux")]
/// A Scroll pHAT HD device connected over I2C bus (e.g. on a Raspberry Pi).
pub struct I2CDisplay {
    device: LinuxI2CDevice,
    frame: u8,
}

#[cfg(target_os = "linux")]
impl I2CDisplay {
    /// Creates a new I2CDisplay device using the I2C device identified by the provided
    /// `device_id` (normally 1 or 2).
    pub fn new(device_id: u8) -> I2CDisplay {
        let device_path = format!("/dev/i2c-{}", device_id);
        let d = LinuxI2CDevice::new(device_path, ADDRESS).unwrap();
        let mut display = I2CDisplay {
            device: d,
            frame: 0,
        };

        // Display initialization
        display.reset();

        display.device.smbus_write_byte_data(BANK_ADDRESS, CONFIG_BANK);
        // Switch to Picture Mode
        display.device.smbus_write_byte_data(MODE_REGISTER, PICTURE_MODE);
        // Disable audio sync
        display.device.smbus_write_byte_data(AUDIOSYNC_REGISTER, 0);

        // Initialize frame 1
        display.device.smbus_write_byte_data(BANK_ADDRESS, 1);
        // Turn off blinking for all LEDs
        for i in 0..17 {
            display.device.smbus_write_byte_data(BLINK_OFFSET + i, 0);
        }
        // Set the PWM duty cycle for all LEDs to 0%
        for i in 0..17 {
            for j in 0..7 {
                display.device.smbus_write_byte_data(COLOR_OFFSET + (i * 8) + j, 0);
            }
        }
        // Turn all LEDs "on"
        for i in 0..17 {
            display.device.smbus_write_byte_data(ENABLE_OFFSET + i, 127);
        }

        // Initialize frame 0
        display.device.smbus_write_byte_data(BANK_ADDRESS, 0);
        // Turn off blinking for all LEDs
        for i in 0..17 {
            display.device.smbus_write_byte_data(BLINK_OFFSET + i, 0);
        }
        // Set the PWM duty cycle for all LEDs to 0%
        for i in 0..17 {
            for j in 0..7 {
                display.device.smbus_write_byte_data(COLOR_OFFSET + (i * 8) + j, 0);
            }
        }
        // Turn all LEDs "on"
        for i in 0..17 {
            display.device.smbus_write_byte_data(ENABLE_OFFSET + i, 127);
        }

        display
    }

    fn bank(&mut self, bank: u8) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.device.smbus_write_byte_data(BANK_ADDRESS, bank)
    }

    fn register(
        &mut self,
        bank: u8,
        register: u8,
        value: u8,
    ) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.bank(bank);
        self.device.smbus_write_byte_data(register, value)
    }

    fn frame(&mut self, frame: u8) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.register(CONFIG_BANK, FRAME_REGISTER, frame)
    }

    fn reset(&mut self) {
        self.sleep(true);
        std::thread::sleep(std::time::Duration::from_millis(10));
        self.sleep(false);
    }

    fn sleep(&mut self, value: bool) -> Result<(), i2cdev::linux::LinuxI2CError> {
        self.register(CONFIG_BANK, SHUTDOWN_REGISTER, if value { 0 } else { 1 })
    }
}

#[cfg(target_os = "linux")]
impl Display for I2CDisplay {
    fn show(&mut self, buffer: &[Column]) {
        // TODO(tzn): Double buffering.
        // let new_frame = (self.frame + 1) % 2;
        let new_frame = 1;
        self.bank(new_frame);
        for y in 0..DISPLAY_HEIGHT {
            for x in 0..DISPLAY_WIDTH {
                let offset = if x >= 8 {
                    (x - 8) * 16 + y
                } else {
                    (8 - x) * 16 - (y + 2)
                };
                let value = match buffer.get(x as usize) {
                    Some(column) => column[y as usize],
                    None => 0,
                };
                self.device
                    .smbus_write_byte_data(COLOR_OFFSET + offset as u8, value);
            }
        }
        self.frame(new_frame);
        self.frame = new_frame;
    }
}

/// A virtual display that outputs its buffer to the terminal from which the binary is attached.
///
/// Useful for debugging or prototyping, as it does not require a physical display to be connected.
pub struct TermDisplay {}

impl TermDisplay {
    pub fn new() -> TermDisplay {
        TermDisplay {}
    }
}

impl Display for TermDisplay {
    fn show(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);
        for x in 0..buffer.len() {
            let col = &buffer[x];
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { ' ' } else { '#' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 1, y as u16 + 1), v);
            }
        }
    }
}

/// A virtual display that outputs its buffer to the terminal from which the binary is attached.
///
/// Useful for debugging or prototyping, as it does not require a physical display to be connected.
pub struct UnicodeDisplay {}

impl UnicodeDisplay {
    pub fn new() -> UnicodeDisplay {
        UnicodeDisplay {}
    }
}

impl Display for UnicodeDisplay {
    fn show(&mut self, buffer: &[Column]) {
        print!("{}", termion::clear::All);

        let col = &buffer[0];

        println!("{}╔", termion::cursor::Goto(1, 1));
        for y in 0..col.len() {
            println!("{}║", termion::cursor::Goto(1, y as u16 + 2));
        }
        println!("{}╚", termion::cursor::Goto(1, col.len() as u16 + 2));

        for x in 0..buffer.len() {
            let col = &buffer[x];
            println!("{}═", termion::cursor::Goto(x as u16 + 2, 1));
            for y in 0..col.len() {
                let c = col[y];
                let v = if c == 0 { '░' } else { '▓' };
                println!("{}{}", termion::cursor::Goto(x as u16 + 2, y as u16 + 2), v);
            }
            println!(
                "{}═",
                termion::cursor::Goto(x as u16 + 2, col.len() as u16 + 2)
            );
        }

        println!("{}╗", termion::cursor::Goto(buffer.len() as u16 + 1, 1));
        for y in 0..col.len() {
            println!(
                "{}║",
                termion::cursor::Goto(buffer.len() as u16 + 1, y as u16 + 2)
            );
        }
        println!(
            "{}╝",
            termion::cursor::Goto(buffer.len() as u16 + 1, col.len() as u16 + 2)
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use std::error;
use std::iter::repeat;
use rustc_serialize;

use super::{Data, StrVal, Bool, VecVal, Map, OptVal};
pub use self::Error::*;

#[derive(Default)]
pub struct Encoder {
    pub data: Vec<Data>,
}

impl Encoder {
    pub fn new() -> Encoder {
        Encoder::default()
    }
}

#[derive(Debug)]
pub enum Error {
    NestedOptions,
    UnsupportedType,
    MissingElements,
    KeyIsNotString,
    NoDataToEncode,
    MultipleRootsFound,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        self.description().fmt(f)
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            NestedOptions => "nested Option types are not supported",
            UnsupportedType => "unsupported type",
            MissingElements => "no elements in value",
            KeyIsNotString => "key is not a string",
            NoDataToEncode => "the encodable type created no data",
            MultipleRootsFound => {
                "the encodable type emitted data that was not tree-like in structure"
            }
        }
    }
}

pub type EncoderResult = Result<(), Error>;

impl rustc_serialize::Encoder for Encoder {
    type Error = Error;

    fn emit_nil(&mut self) -> EncoderResult {
        Err(UnsupportedType)
    }

    fn emit_isize(&mut self, v: isize) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_usize(&mut self, v: usize) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_u64(&mut self, v: u64) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_u32(&mut self, v: u32) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_u16(&mut self, v: u16) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_u8(&mut self, v: u8) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }

    fn emit_i64(&mut self, v: i64) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_i32(&mut self, v: i32) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_i16(&mut self, v: i16) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_i8(&mut self, v: i8) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }

    fn emit_bool(&mut self, v: bool) -> EncoderResult {
        self.data.push(Bool(v));
        Ok(())
    }

    fn emit_f64(&mut self, v: f64) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }
    fn emit_f32(&mut self, v: f32) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }

    fn emit_char(&mut self, v: char) -> EncoderResult {
        self.data.push(StrVal(repeat(v).take(1).collect::<String>()));
        Ok(())
    }
    fn emit_str(&mut self, v: &str) -> EncoderResult {
        self.data.push(StrVal(v.to_string()));
        Ok(())
    }

    fn emit_enum<F>(&mut self, _name: &str, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_variant<F>(&mut self, _name: &str, _id: usize, _len: usize, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_struct_variant<F>(&mut self,
                                   _v_name: &str,
                                   _v_id: usize,
                                   _len: usize,
                                   _f: F)
                                   -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_struct_variant_field<F>(&mut self,
                                         _f_name: &str,
                                         _f_idx: usize,
                                         _f: F)
                                         -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.data.push(Map(HashMap::new()));
        f(self)
    }

    fn emit_struct_field<F>(&mut self, name: &str, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let mut m = match self.data.pop() {
            Some(Map(m)) => m,
            _ => {
                return Err(UnsupportedType);
            }
        };
        try!(f(self));
        let data = match self.data.pop() {
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        m.insert(name.to_string(), data);
        self.data.push(Map(m));
        Ok(())
    }

    fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq(len, f)
    }

    fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_tuple_struct<F>(&mut self, _name: &str, len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq(len, f)
    }

    fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq_elt(idx, f)
    }

    // Specialized types:
    fn emit_option<F>(&mut self, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        f(self)
    }

    fn emit_option_none(&mut self) -> EncoderResult {
        self.data.push(OptVal(None));
        Ok(())
    }

    fn emit_option_some<F>(&mut self, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        try!(f(self));
        let val = match self.data.pop() {
            Some(OptVal(_)) => return Err(NestedOptions),
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        self.data.push(OptVal(Some(Box::new(val))));
        Ok(())
    }

    fn emit_seq<F>(&mut self, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.data.push(VecVal(Vec::new()));
        f(self)
    }

    fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let mut v = match self.data.pop() {
            Some(VecVal(v)) => v,
            _ => {
                return Err(UnsupportedType);
            }
        };
        try!(f(self));
        let data = match self.data.pop() {
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        v.push(data);
        self.data.push(VecVal(v));
        Ok(())
    }

    fn emit_map<F>(&mut self, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.data.push(Map(HashMap::new()));
        f(self)
    }

    fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        try!(f(self));
        let last = match self.data.last() {
            Some(d) => d,
            None => {
                return Err(MissingElements);
            }
        };
        match *last {
            StrVal(_) => Ok(()),
            _ => Err(KeyIsNotString),
        }
    }

    fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let k = match self.data.pop() {
            Some(StrVal(s)) => s,
            _ => {
                return Err(KeyIsNotString);
            }
        };
        let mut m = match self.data.pop() {
            Some(Map(m)) => m,
            _ => bug!("Expected a map"),
        };
        try!(f(self));
        let popped = match self.data.pop() {
            Some(p) => p,
            None => bug!("Nothing to pop!"),
        };
        m.insert(k, popped);
        self.data.push(Map(m));
        Ok(())
    }
}

pub fn encode<T: rustc_serialize::Encodable>(data: &T) -> Result<Data, Error> {
    let mut encoder = Encoder::new();
    try!(data.encode(&mut encoder));
    match encoder.data.len() {
        1 => Ok(encoder.data.pop().unwrap()),
        // These two errors are really down to either the type implementing Encodable wrong
        // or using some data which is incompatible with the way mustache *currently* works.
        // It does feel like `bug!` more than Error but maybe it covers some usecases so
        // lets not panic here.
        0 => Err(NoDataToEncode),
        _ => Err(MultipleRootsFound),
    }
}

#[cfg(test)]
mod tests {
    use super::{encode, Error};
    use rustc_serialize::{Encodable, Encoder};

    struct NoData;

    impl Encodable for NoData {
        fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
            Ok(())
        }
    }

    struct Multiroot;

    impl Encodable for Multiroot {
        fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
            try!(s.emit_u32(4));
            s.emit_u32(1)
        }
    }

    #[test]
    fn encodable_with_no_data() {
        assert_let!(Err(Error::NoDataToEncode) = encode(&NoData));
    }

    #[test]
    fn encodable_with_multiple_roots() {
        assert_let!(Err(Error::MultipleRootsFound) = encode(&Multiroot));
    }
}
<commit_msg>refactor(encoder): DRY up the Encoder implementation<commit_after>use std::collections::HashMap;
use std::fmt;
use std::error;
use rustc_serialize;

use super::{Data, StrVal, Bool, VecVal, Map, OptVal};
pub use self::Error::*;

#[derive(Default)]
pub struct Encoder {
    pub data: Vec<Data>,
}

impl Encoder {
    pub fn new() -> Encoder {
        Encoder::default()
    }
}

#[derive(Debug)]
pub enum Error {
    NestedOptions,
    UnsupportedType,
    MissingElements,
    KeyIsNotString,
    NoDataToEncode,
    MultipleRootsFound,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use std::error::Error;
        self.description().fmt(f)
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            NestedOptions => "nested Option types are not supported",
            UnsupportedType => "unsupported type",
            MissingElements => "no elements in value",
            KeyIsNotString => "key is not a string",
            NoDataToEncode => "the encodable type created no data",
            MultipleRootsFound => {
                "the encodable type emitted data that was not tree-like in structure"
            }
        }
    }
}

pub type EncoderResult = Result<(), Error>;

impl Encoder {
    fn push(&mut self, data: Data) {
        self.data.push(data);
    }

    fn push_ok(&mut self, data: Data) -> EncoderResult {
        self.push(data);
        Ok(())
    }

    fn push_str_ok<T: ToString>(&mut self, data: T) -> EncoderResult {
        self.push(StrVal(data.to_string()));
        Ok(())
    }
}

impl rustc_serialize::Encoder for Encoder {
    type Error = Error;

    fn emit_nil(&mut self) -> EncoderResult {
        Err(UnsupportedType)
    }

    fn emit_isize(&mut self, v: isize) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_usize(&mut self, v: usize) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_u64(&mut self, v: u64) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_u32(&mut self, v: u32) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_u16(&mut self, v: u16) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_u8(&mut self, v: u8) -> EncoderResult {
        self.push_str_ok(v)
    }

    fn emit_i64(&mut self, v: i64) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_i32(&mut self, v: i32) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_i16(&mut self, v: i16) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_i8(&mut self, v: i8) -> EncoderResult {
        self.push_str_ok(v)
    }

    fn emit_bool(&mut self, v: bool) -> EncoderResult {
        self.push_ok(Bool(v))
    }

    fn emit_f64(&mut self, v: f64) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_f32(&mut self, v: f32) -> EncoderResult {
        self.push_str_ok(v)
    }

    fn emit_char(&mut self, v: char) -> EncoderResult {
        self.push_str_ok(v)
    }
    fn emit_str(&mut self, v: &str) -> EncoderResult {
        self.push_str_ok(v)
    }

    fn emit_enum<F>(&mut self, _name: &str, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_variant<F>(&mut self, _name: &str, _id: usize, _len: usize, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, _f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_struct_variant<F>(&mut self,
                                   _v_name: &str,
                                   _v_id: usize,
                                   _len: usize,
                                   _f: F)
                                   -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_enum_struct_variant_field<F>(&mut self,
                                         _f_name: &str,
                                         _f_idx: usize,
                                         _f: F)
                                         -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        Err(UnsupportedType)
    }

    fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.push(Map(HashMap::new()));
        f(self)
    }

    fn emit_struct_field<F>(&mut self, name: &str, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let mut m = match self.data.pop() {
            Some(Map(m)) => m,
            _ => {
                return Err(UnsupportedType);
            }
        };
        try!(f(self));
        let data = match self.data.pop() {
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        m.insert(name.to_string(), data);
        self.push_ok(Map(m))
    }

    fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq(len, f)
    }

    fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_tuple_struct<F>(&mut self, _name: &str, len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq(len, f)
    }

    fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.emit_seq_elt(idx, f)
    }

    // Specialized types:
    fn emit_option<F>(&mut self, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        f(self)
    }

    fn emit_option_none(&mut self) -> EncoderResult {
        self.push_ok(OptVal(None))
    }

    fn emit_option_some<F>(&mut self, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        try!(f(self));
        let val = match self.data.pop() {
            Some(OptVal(_)) => return Err(NestedOptions),
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        self.push_ok(OptVal(Some(Box::new(val))))
    }

    fn emit_seq<F>(&mut self, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.push(VecVal(Vec::new()));
        f(self)
    }

    fn emit_seq_elt<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let mut v = match self.data.pop() {
            Some(VecVal(v)) => v,
            _ => {
                return Err(UnsupportedType);
            }
        };
        try!(f(self));
        let data = match self.data.pop() {
            Some(d) => d,
            _ => {
                return Err(UnsupportedType);
            }
        };
        v.push(data);
        self.push_ok(VecVal(v))
    }

    fn emit_map<F>(&mut self, _len: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        self.push(Map(HashMap::new()));
        f(self)
    }

    fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        try!(f(self));
        let last = match self.data.last() {
            Some(d) => d,
            None => {
                return Err(MissingElements);
            }
        };
        match *last {
            StrVal(_) => Ok(()),
            _ => Err(KeyIsNotString),
        }
    }

    fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncoderResult
    where F: FnOnce(&mut Encoder) -> EncoderResult
    {
        let k = match self.data.pop() {
            Some(StrVal(s)) => s,
            _ => {
                return Err(KeyIsNotString);
            }
        };
        let mut m = match self.data.pop() {
            Some(Map(m)) => m,
            _ => bug!("Expected a map"),
        };
        try!(f(self));
        let popped = match self.data.pop() {
            Some(p) => p,
            None => bug!("Nothing to pop!"),
        };
        m.insert(k, popped);
        self.push(Map(m));
        Ok(())
    }
}

pub fn encode<T: rustc_serialize::Encodable>(data: &T) -> Result<Data, Error> {
    let mut encoder = Encoder::new();
    try!(data.encode(&mut encoder));
    match encoder.data.len() {
        1 => Ok(encoder.data.pop().unwrap()),
        // These two errors are really down to either the type implementing Encodable wrong
        // or using some data which is incompatible with the way mustache *currently* works.
        // It does feel like `bug!` more than Error but maybe it covers some usecases so
        // lets not panic here.
        0 => Err(NoDataToEncode),
        _ => Err(MultipleRootsFound),
    }
}

#[cfg(test)]
mod tests {
    use super::{encode, Error};
    use rustc_serialize::{Encodable, Encoder};

    struct NoData;

    impl Encodable for NoData {
        fn encode<S: Encoder>(&self, _: &mut S) -> Result<(), S::Error> {
            Ok(())
        }
    }

    struct Multiroot;

    impl Encodable for Multiroot {
        fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
            try!(s.emit_u32(4));
            s.emit_u32(1)
        }
    }

    #[test]
    fn encodable_with_no_data() {
        assert_let!(Err(Error::NoDataToEncode) = encode(&NoData));
    }

    #[test]
    fn encodable_with_multiple_roots() {
        assert_let!(Err(Error::MultipleRootsFound) = encode(&Multiroot));
    }
}
<|endoftext|>


<commit_before>// See erts-6.1/doc/html/erl_ext_dist.html

#![feature(struct_variant)]     // this is for enum Eterm
#![allow(non_camel_case_types)] // this is for enum ErlTermTag
#![feature(macro_rules)]        // for try_io!

extern crate num;
extern crate collections;
extern crate debug;

use std::string::String;
use std::vec::Vec;
use std::num::FromPrimitive;
use std::io;
use num::bigint::BigInt;
use collections::bitv::Bitv;

#[deriving(FromPrimitive, Show)]
enum ErlTermTag {
    // ATOM_CACHE_REF = 82,
    SMALL_INTEGER_EXT = 97,
    INTEGER_EXT = 98,
    FLOAT_EXT = 99,
    ATOM_EXT = 100,
    REFERENCE_EXT = 101,
    PORT_EXT = 102,
    PID_EXT = 103,
    SMALL_TUPLE_EXT = 104,
    LARGE_TUPLE_EXT = 105,
    MAP_EXT = 116,
    NIL_EXT = 106,
    STRING_EXT = 107,
    LIST_EXT = 108,
    BINARY_EXT = 109,
    SMALL_BIG_EXT = 110,
    LARGE_BIG_EXT = 111,
    NEW_REFERENCE_EXT = 114,
    SMALL_ATOM_EXT = 115,
    FUN_EXT = 117,
    NEW_FUN_EXT = 112,
    EXPORT_EXT = 113,
    BIT_BINARY_EXT = 77,
    NEW_FLOAT_EXT = 70,
    ATOM_UTF8_EXT = 118,
    SMALL_ATOM_UTF8_EXT = 119,
}

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub enum Eterm {
    SmallInteger(u8),           // small_integer
    Integer(int),               // integer
    // Float(f64),                 // float, new_float
    Atom(Atom),                 // atom, small_atom, atom_utf8, small_atom_utf8
    Reference {                 // reference, new_reference
        node: Atom,
        id: Vec<u8>,
        creation: u8},
    Port {                      // poort
        node: Atom,
        id: u8,
        creation: u8},
    Pid(Pid),                   // pid
    Tuple(Tuple),               // small_tuple, large_tuple
    Map(EMap),                  // map
    Nil,                        // nil
    String(String),             // string
    List(List),                 // list
    Binary(Vec<u8>),            // binary
    BigNum(BigInt),             // small_big, large_big
    Fun {                       // fun
        pid: Pid,
        module: Atom,
        index: u32,
        uniq: u32,
        free_vars: Vec<Eterm>},
    NewFun {                    // new_fun
        arity: u8,
        uniq: u16,
        index: u32,
        module: Atom,
        old_index: u32,
        old_uniq: u16,
        pid: Pid,
        free_vars: Vec<Eterm>},
    Export {                    // export
        module: Atom,
        function: Atom,
        arity: u8,
    },
    BitBinary(Bitv),            // bit_binary; XXX: maybe choose some other representation?
}
pub type Atom = String;
pub type Tuple = Vec<Eterm>;
pub type EMap = Vec<(Eterm, Eterm)>; // k-v pairs //TreeMap<Eterm, Eterm>;
pub type List = Vec<Eterm>;

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub struct Pid {                // moved out from enum because it used in Eterm::{Fun,NewFun}
    node: Atom,
    id: u8,
    serial: u32,
    creation: u8,
}

// enum BuildError {
//     IoError(io::IoError),
//     BErr,
// }
type BuildResult = io::IoResult<Eterm>;//, BuildError>;

struct Builder<T> {
    rdr: T,
}

// macro_rules! try_io(
//     ($e:expr) => (
//         match $e {
//             Ok(e) => e,
//             Err(e) => return IoError(e)
//         }
//         )
// )

impl<T: io::Reader> Builder<T> {
    fn new(rdr: T) -> Builder<T> {
        Builder{rdr: rdr}
    }
    fn read_prelude(&mut self) -> io::IoResult<bool> {
        Ok(131 == try!(self.rdr.read_u8()))
    }
    fn build(&mut self) -> BuildResult {
        let int_tag = try!(self.rdr.read_u8());
        let tag: Option<ErlTermTag> = FromPrimitive::from_u8(int_tag);
        match tag {
            Some(SMALL_INTEGER_EXT) =>
                Ok(SmallInteger(try!(self.rdr.read_u8()))),
            Some(INTEGER_EXT) =>
                Ok(Integer(try!(self.rdr.read_be_int()))),
            Some(ATOM_EXT) | Some(ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(SMALL_TUPLE_EXT) => {
                let mut tuple: Tuple = Vec::new();
                let arity = try!(self.rdr.read_u8());
                for _ in range(0, arity) {
                    let term = try!(self.build());
                    tuple.push(term)
                }
                Ok(Tuple(tuple))
            },
            Some(MAP_EXT) => {
                let mut map: EMap = Vec::new();
                let arity = try!(self.rdr.read_be_u32());
                for _ in range(0, arity) {
                    let key = try!(self.build());
                    let val = try!(self.build());
                    map.push((key, val))
                }
                Ok(Map(map))
            },
            Some(BINARY_EXT) => {
                let len = try!(self.rdr.read_be_u32());
                Ok(Binary(try!(self.rdr.read_exact(len as uint))))
            }
            Some(t) =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Tag not implemented",
                    detail: Some(format!("Tag: #{} - {}", int_tag, t))
                }),
            None =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Invalid data type",
                    detail: Some(format!("Tag: #{}", int_tag))
                })
        }
    }
}

fn main() {
    use std::io::{File,BufferedReader};

    for i in range(70, 120) {
        let tag: Option<ErlTermTag> = FromPrimitive::from_int(i);
        println!("{} => {:?}", i, tag);
    }
    println!("==============================");
    let map = vec!( (Atom("my_map_key".to_string()), Nil) );

    let term: Eterm = NewFun {
        arity: 3,
        uniq: 1234,
        index: 10,
        module: "my_mod".to_string(),
        old_index: 1212,
        old_uniq: 1234,
        pid: Pid {node: "wasd".to_string(), id: 1, serial: 123, creation: 2},
        free_vars: vec!(//Float(3.14),
                        Nil,
                        Binary(vec!(1, 2, 3, 4)),
                        Export {
                            module: "my_mod".to_string(),
                            function: "my_func".to_string(),
                            arity: 4},
                        List(vec!(SmallInteger(1), Integer(1000000), Nil)),
                        Tuple(vec!(Atom("record".to_string()), Map(map))),
                        ),
    };
    println!("{:?}", term);
    println!("==============================");
    match term {
        NewFun {free_vars: vars, ..} =>
            println!("{:?}", vars.as_slice()),
        _ => ()
    }
    println!("==============================");
    let f = BufferedReader::new(File::open(&Path::new("test/test_terms.bin")));
    let mut builder = Builder::new(f);
    match builder.read_prelude() {
        Ok(true) =>
            println!("Valid eterm"),
        Ok(false) =>
            println!("Invalid eterm!"),
        Err(io::IoError{desc: d, ..}) => {
            println!("IoError: {}", d);
            return
        }
    }
    println!("{}", builder.build());
}
<commit_msg>References, Nil and SMALL_ATOM_* parsers.<commit_after>// See erts-6.1/doc/html/erl_ext_dist.html

#![feature(struct_variant)]     // this is for enum Eterm
#![allow(non_camel_case_types)] // this is for enum ErlTermTag
#![feature(macro_rules)]        // for try_io!

extern crate num;
extern crate collections;
extern crate debug;

use std::string::String;
use std::vec::Vec;
use std::num::FromPrimitive;
use std::io;
use num::bigint::BigInt;
use collections::bitv::Bitv;

#[deriving(FromPrimitive, Show)]
enum ErlTermTag {
    // ATOM_CACHE_REF = 82,
    SMALL_INTEGER_EXT = 97,
    INTEGER_EXT = 98,
    FLOAT_EXT = 99,
    ATOM_EXT = 100,
    REFERENCE_EXT = 101,
    PORT_EXT = 102,
    PID_EXT = 103,
    SMALL_TUPLE_EXT = 104,
    LARGE_TUPLE_EXT = 105,
    MAP_EXT = 116,
    NIL_EXT = 106,
    STRING_EXT = 107,
    LIST_EXT = 108,
    BINARY_EXT = 109,
    SMALL_BIG_EXT = 110,
    LARGE_BIG_EXT = 111,
    NEW_REFERENCE_EXT = 114,
    SMALL_ATOM_EXT = 115,
    FUN_EXT = 117,
    NEW_FUN_EXT = 112,
    EXPORT_EXT = 113,
    BIT_BINARY_EXT = 77,
    NEW_FLOAT_EXT = 70,
    ATOM_UTF8_EXT = 118,
    SMALL_ATOM_UTF8_EXT = 119,
}

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub enum Eterm {
    SmallInteger(u8),           // small_integer
    Integer(int),               // integer
    Float(f64),                 // float, new_float
    Atom(Atom),                 // atom, small_atom, atom_utf8, small_atom_utf8
    Reference {                 // reference, new_reference
        node: Atom,
        id: Vec<u8>,
        creation: u8},
    Port {                      // poort
        node: Atom,
        id: u8,
        creation: u8},
    Pid(Pid),                   // pid
    Tuple(Tuple),               // small_tuple, large_tuple
    Map(EMap),                  // map
    Nil,                        // nil
    String(String),             // string
    List(List),                 // list
    Binary(Vec<u8>),            // binary
    BigNum(BigInt),             // small_big, large_big
    Fun {                       // fun
        pid: Pid,
        module: Atom,
        index: u32,
        uniq: u32,
        free_vars: Vec<Eterm>},
    NewFun {                    // new_fun
        arity: u8,
        uniq: u16,
        index: u32,
        module: Atom,
        old_index: u32,
        old_uniq: u16,
        pid: Pid,
        free_vars: Vec<Eterm>},
    Export {                    // export
        module: Atom,
        function: Atom,
        arity: u8,
    },
    BitBinary(Bitv),            // bit_binary; XXX: maybe choose some other representation?
}
pub type Atom = String;
pub type Tuple = Vec<Eterm>;
pub type EMap = Vec<(Eterm, Eterm)>; // k-v pairs //TreeMap<Eterm, Eterm>;
pub type List = Vec<Eterm>;

//#[deriving(PartialOrd, PartialEq, Ord, Eq)]
#[deriving(Show)]
pub struct Pid {                // moved out from enum because it used in Eterm::{Fun,NewFun}
    node: Atom,
    id: u8,
    serial: u32,
    creation: u8,
}

// enum BuildError {
//     IoError(io::IoError),
//     BErr,
// }
type BuildResult = io::IoResult<Eterm>;//, BuildError>;

struct Builder<T> {
    rdr: T,
}

// macro_rules! try_io(
//     ($e:expr) => (
//         match $e {
//             Ok(e) => e,
//             Err(e) => return IoError(e)
//         }
//         )
// )

impl<T: io::Reader> Builder<T> {
    fn new(rdr: T) -> Builder<T> {
        Builder{rdr: rdr}
    }
    fn read_prelude(&mut self) -> io::IoResult<bool> {
        Ok(131 == try!(self.rdr.read_u8()))
    }
    fn build(&mut self) -> BuildResult {
        let int_tag = try!(self.rdr.read_u8());
        let tag: Option<ErlTermTag> = FromPrimitive::from_u8(int_tag);
        match tag {
            Some(SMALL_INTEGER_EXT) =>
                Ok(SmallInteger(try!(self.rdr.read_u8()))),
            Some(INTEGER_EXT) =>
                Ok(Integer(try!(self.rdr.read_be_int()))),
            Some(ATOM_EXT) | Some(ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(REFERENCE_EXT) => {
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let id = try!(self.rdr.read_exact(4));
                let creation = try!(self.rdr.read_u8());
                Ok(Reference {
                    node: node,
                    id: id,
                    creation: creation
                })
            },
            Some(SMALL_TUPLE_EXT) => {
                let mut tuple: Tuple = Vec::new();
                let arity = try!(self.rdr.read_u8());
                for _ in range(0, arity) {
                    let term = try!(self.build());
                    tuple.push(term)
                }
                Ok(Tuple(tuple))
            },
            Some(MAP_EXT) => {
                let mut map: EMap = Vec::new();
                let arity = try!(self.rdr.read_be_u32());
                for _ in range(0, arity) {
                    let key = try!(self.build());
                    let val = try!(self.build());
                    map.push((key, val))
                }
                Ok(Map(map))
            },
            Some(NIL_EXT) =>
                Ok(Nil),
            Some(BINARY_EXT) => {
                let len = try!(self.rdr.read_be_u32());
                Ok(Binary(try!(self.rdr.read_exact(len as uint))))
            },
            Some(NEW_REFERENCE_EXT) => {
                let len = try!(self.rdr.read_be_u16());
                let node = match try!(self.build()) {
                    Atom(a) =>
                        a,
                    _ =>
                        return Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Assertion failed: Atom expected",
                            detail: None
                        })
                };
                let creation = try!(self.rdr.read_u8());
                let id = try!(self.rdr.read_exact(4 * len as uint));
                Ok(Reference{
                    node: node,
                    id: id, // here id should be Vec<u32>, but since it's not interpreted, leave it as is
                    creation: creation
                })
            },
            Some(SMALL_ATOM_EXT) | Some(SMALL_ATOM_UTF8_EXT) => {
                let len = try!(self.rdr.read_u8());
                let data = try!(self.rdr.read_exact(len as uint));
                // XXX: data is in latin1 in case of ATOM_EXT
                match String::from_utf8(data) {
                    Ok(atom) =>
                        Ok(Atom(atom)),
                    Err(_) =>
                        Err(io::IoError{
                            kind: io::OtherIoError,
                            desc: "Bad utf-8",
                            detail: None // format!("{}", data)
                            })
                }
            },
            Some(t) =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Tag not implemented",
                    detail: Some(format!("Tag: #{} - {}", int_tag, t))
                }),
            None =>
                Err(io::IoError{
                    kind: io::OtherIoError,
                    desc: "Invalid data type",
                    detail: Some(format!("Tag: #{}", int_tag))
                })
        }
    }
}

fn main() {
    use std::io::{File,BufferedReader};

    for i in range(70, 120) {
        let tag: Option<ErlTermTag> = FromPrimitive::from_int(i);
        println!("{} => {:?}", i, tag);
    }
    println!("==============================");
    let map = vec!( (Atom("my_map_key".to_string()), Nil) );

    let term: Eterm = NewFun {
        arity: 3,
        uniq: 1234,
        index: 10,
        module: "my_mod".to_string(),
        old_index: 1212,
        old_uniq: 1234,
        pid: Pid {node: "wasd".to_string(), id: 1, serial: 123, creation: 2},
        free_vars: vec!(//Float(3.14),
                        Nil,
                        Binary(vec!(1, 2, 3, 4)),
                        Export {
                            module: "my_mod".to_string(),
                            function: "my_func".to_string(),
                            arity: 4},
                        List(vec!(SmallInteger(1), Integer(1000000), Nil)),
                        Tuple(vec!(Atom("record".to_string()), Map(map))),
                        ),
    };
    println!("{:?}", term);
    println!("==============================");
    match term {
        NewFun {free_vars: vars, ..} =>
            println!("{:?}", vars.as_slice()),
        _ => ()
    }
    println!("==============================");
    let f = BufferedReader::new(File::open(&Path::new("test/test_terms.bin")));
    let mut builder = Builder::new(f);
    match builder.read_prelude() {
        Ok(true) =>
            println!("Valid eterm"),
        Ok(false) =>
            println!("Invalid eterm!"),
        Err(io::IoError{desc: d, ..}) => {
            println!("IoError: {}", d);
            return
        }
    }
    println!("{}", builder.build());
}
<|endoftext|>


<commit_before>use std;
use std::io;

use ::as_u16;
use ::as_u32;

use ::errors::Result;
use ::errors::ErrorKind::*;

#[derive(Debug)]
struct Extent {
    block: u32,
    start: u64,
    len: u16,
}

pub struct TreeReader<R> {
    inner: R,
    pos: u64,
    len: u64,
    block_size: u32,
    extents: Vec<Extent>,
}

impl<R> TreeReader<R>
where R: io::Read + io::Seek {
    pub fn new(mut inner: R, block_size: u32, size: u64, block: [u8; 4 * 15]) -> Result<TreeReader<R>> {
        let extents = load_extent_tree(&mut inner, block, block_size)?;
        TreeReader::create(inner, block_size, size, extents)
    }

    fn create(inner: R, block_size: u32, size: u64, extents: Vec<Extent>) -> Result<TreeReader<R>> {
        Ok(TreeReader {
            pos: 0,
            len: size,
            inner,
            extents,
            block_size,
        })
    }
}

enum FoundBlock<'a> {
    Actual(&'a Extent),
    Sparse(u32),
}

fn find_block(block: u32, extents: &[Extent]) -> FoundBlock {
    for extent in extents {
        if block < extent.block {
            // we've gone past it
            return FoundBlock::Sparse(extent.block - block);
        }

        if block >= extent.block && block < extent.block + extent.len as u32 {
            // we're inside it
            return FoundBlock::Actual(extent);
        }
    }

    return FoundBlock::Sparse(std::u32::MAX);
}

impl<R> io::Read for TreeReader<R>
    where R: io::Read + io::Seek {

    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }

        let block_size = self.block_size as u64;

        let wanted_block = (self.pos / block_size) as u32;
        let read_of_this_block = self.pos % block_size;

        match find_block(wanted_block, &self.extents) {
            FoundBlock::Actual(extent) => {
                let bytes_through_extent = (block_size * (wanted_block - extent.block) as u64) + read_of_this_block;
                let remaining_bytes_in_extent = (extent.len as u64 * block_size) - bytes_through_extent;
                let to_read = std::cmp::min(remaining_bytes_in_extent, buf.len() as u64) as usize;
                let to_read = std::cmp::min(to_read as u64, self.len - self.pos) as usize;
                self.inner.seek(io::SeekFrom::Start(extent.start as u64 * block_size + bytes_through_extent))?;
                let read = self.inner.read(&mut buf[0..to_read])?;
                self.pos += read as u64;
                return Ok(read);
            }
            FoundBlock::Sparse(max) => {
                let max_bytes = max as u64 * block_size;
                let read = std::cmp::min(max_bytes, buf.len() as u64) as usize;
                let read = std::cmp::min(read as u64, self.len - self.pos) as usize;
                zero(&mut buf[0..read]);
                self.pos += read as u64;
                return Ok(read);
            }
        }
    }
}

fn add_found_extents<R>(
    block_size: u32,
    mut inner: &mut R,
    block: &[u8],
    expected_depth: u16,
    extents: &mut Vec<Extent>) -> Result<()>
where R: io::Read + io::Seek {

    ensure!(0x0a == block[0] && 0xf3 == block[1],
        AssumptionFailed("invalid extent magic".to_string()));

    let extent_entries = as_u16(&block[2..]);
    // 4..: max; doesn't seem to be useful during read
    let depth = as_u16(&block[6..]);
    // 8..: generation, not used in standard ext4

    ensure!(expected_depth == depth,
        AssumptionFailed(format!("depth incorrect: {} != {}", expected_depth, depth)));

    if 0 == depth {
        for en in 0..extent_entries {
            let raw_extent = &block[12 + en as usize * 12..];
            let ee_block = as_u32(raw_extent);
            let ee_len = as_u16(&raw_extent[4..]);
            let ee_start_hi = as_u16(&raw_extent[6..]);
            let ee_start_lo = as_u32(&raw_extent[8..]);
            let ee_start = ee_start_lo as u64 + 0x1000 * ee_start_hi as u64;

            extents.push(Extent {
                block: ee_block,
                start: ee_start,
                len: ee_len,
            });
        }

        return Ok(());
    }

    for en in 0..extent_entries {
        let extent_idx = &block[12 + en as usize * 12..];
        //            let ei_block = as_u32(extent_idx);
        let ei_leaf_lo = as_u32(&extent_idx[4..]);
        let ei_leaf_hi = as_u16(&extent_idx[8..]);
        let ee_leaf: u64 = ei_leaf_lo as u64 + ((ei_leaf_hi as u64) << 32);
        inner.seek(io::SeekFrom::Start(block_size as u64 * ee_leaf))?;
        let mut block = vec![0u8; block_size as usize];
        inner.read_exact(&mut block)?;
        add_found_extents(block_size, inner, &block, depth - 1, extents)?;
    }

    Ok(())
}

fn load_extent_tree<R>(mut inner: R, start: [u8; 4 * 15], block_size: u32) -> Result<Vec<Extent>>
    where R: io::Read + io::Seek {
    ensure!(0x0a == start[0] && 0xf3 == start[1],
        AssumptionFailed("invalid extent magic".to_string()));

    let extent_entries = as_u16(&start[2..]);
    // 4..: max; doesn't seem to be useful during read
    let depth = as_u16(&start[6..]);

    ensure!(depth <= 5,
        AssumptionFailed(format!("initial depth too high: {}", depth)));

    let mut extents = Vec::with_capacity(extent_entries as usize + depth as usize * 200);

    add_found_extents(block_size, &mut inner, &start, depth, &mut extents)?;

    extents.sort_by_key(|e| e.block);

    Ok(extents)
}

fn zero(buf: &mut [u8]) {
    for i in buf {
        *i = 0;
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use ::extents::TreeReader;
    use ::extents::Extent;

    #[test]
    fn simple_tree() {
        let data = (0..255u8).collect::<Vec<u8>>();
        let size = 4 + 4 * 2;
        let all_bytes = io::Cursor::new(data);
        let mut reader = TreeReader::create(all_bytes, 4, size as u64,
            vec![
                Extent {
                    block: 0,
                    start: 10,
                    len: 1,
                },
                Extent {
                    block: 1,
                    start: 20,
                    len: 2,
                }
            ]
        ).unwrap();

        let mut res = Vec::new();
        assert_eq!(size, reader.read_to_end(&mut res).unwrap());

        assert_eq!(vec![
            40, 41, 42, 43,
            80, 81, 82, 83,
            84, 85, 86, 87,
        ], res);
    }

    #[test]
    fn zero_buf() {
        let mut buf = [7u8; 5];
        assert_eq!(7, buf[0]);
        ::extents::zero(&mut buf);
        for i in &buf {
            assert_eq!(0, *i);
        }
    }
}
<commit_msg>untested impl Seek for TreeReader<commit_after>use std;
use std::io;

use ::as_u16;
use ::as_u32;

use ::errors::Result;
use ::errors::ErrorKind::*;

#[derive(Debug)]
struct Extent {
    block: u32,
    start: u64,
    len: u16,
}

pub struct TreeReader<R> {
    inner: R,
    pos: u64,
    len: u64,
    block_size: u32,
    extents: Vec<Extent>,
}

impl<R> TreeReader<R>
where R: io::Read + io::Seek {
    pub fn new(mut inner: R, block_size: u32, size: u64, block: [u8; 4 * 15]) -> Result<TreeReader<R>> {
        let extents = load_extent_tree(&mut inner, block, block_size)?;
        TreeReader::create(inner, block_size, size, extents)
    }

    fn create(inner: R, block_size: u32, size: u64, extents: Vec<Extent>) -> Result<TreeReader<R>> {
        Ok(TreeReader {
            pos: 0,
            len: size,
            inner,
            extents,
            block_size,
        })
    }

    pub fn into_inner(self) -> R {
        self.inner
    }
}

enum FoundBlock<'a> {
    Actual(&'a Extent),
    Sparse(u32),
}

fn find_block(block: u32, extents: &[Extent]) -> FoundBlock {
    for extent in extents {
        if block < extent.block {
            // we've gone past it
            return FoundBlock::Sparse(extent.block - block);
        }

        if block >= extent.block && block < extent.block + extent.len as u32 {
            // we're inside it
            return FoundBlock::Actual(extent);
        }
    }

    return FoundBlock::Sparse(std::u32::MAX);
}

impl<R> io::Read for TreeReader<R>
    where R: io::Read + io::Seek {

    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }

        let block_size = self.block_size as u64;

        let wanted_block = (self.pos / block_size) as u32;
        let read_of_this_block = self.pos % block_size;

        match find_block(wanted_block, &self.extents) {
            FoundBlock::Actual(extent) => {
                let bytes_through_extent = (block_size * (wanted_block - extent.block) as u64) + read_of_this_block;
                let remaining_bytes_in_extent = (extent.len as u64 * block_size) - bytes_through_extent;
                let to_read = std::cmp::min(remaining_bytes_in_extent, buf.len() as u64) as usize;
                let to_read = std::cmp::min(to_read as u64, self.len - self.pos) as usize;
                self.inner.seek(io::SeekFrom::Start(extent.start as u64 * block_size + bytes_through_extent))?;
                let read = self.inner.read(&mut buf[0..to_read])?;
                self.pos += read as u64;
                return Ok(read);
            }
            FoundBlock::Sparse(max) => {
                let max_bytes = max as u64 * block_size;
                let read = std::cmp::min(max_bytes, buf.len() as u64) as usize;
                let read = std::cmp::min(read as u64, self.len - self.pos) as usize;
                zero(&mut buf[0..read]);
                self.pos += read as u64;
                return Ok(read);
            }
        }
    }
}

impl<R> io::Seek for TreeReader<R>
where R: io::Read + io::Seek {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        match pos {
            io::SeekFrom::Start(set) => { self.pos = set }
            io::SeekFrom::Current(diff) => { self.pos = (self.pos as i64 + diff) as u64 }
            io::SeekFrom::End(set) => {
                assert!(set >= 0);
                self.pos = self.len - set as u64
            }
        }

        assert!(self.pos <= self.len);

        Ok(self.pos)
    }
}

fn add_found_extents<R>(
    block_size: u32,
    mut inner: &mut R,
    block: &[u8],
    expected_depth: u16,
    extents: &mut Vec<Extent>) -> Result<()>
where R: io::Read + io::Seek {

    ensure!(0x0a == block[0] && 0xf3 == block[1],
        AssumptionFailed("invalid extent magic".to_string()));

    let extent_entries = as_u16(&block[2..]);
    // 4..: max; doesn't seem to be useful during read
    let depth = as_u16(&block[6..]);
    // 8..: generation, not used in standard ext4

    ensure!(expected_depth == depth,
        AssumptionFailed(format!("depth incorrect: {} != {}", expected_depth, depth)));

    if 0 == depth {
        for en in 0..extent_entries {
            let raw_extent = &block[12 + en as usize * 12..];
            let ee_block = as_u32(raw_extent);
            let ee_len = as_u16(&raw_extent[4..]);
            let ee_start_hi = as_u16(&raw_extent[6..]);
            let ee_start_lo = as_u32(&raw_extent[8..]);
            let ee_start = ee_start_lo as u64 + 0x1000 * ee_start_hi as u64;

            extents.push(Extent {
                block: ee_block,
                start: ee_start,
                len: ee_len,
            });
        }

        return Ok(());
    }

    for en in 0..extent_entries {
        let extent_idx = &block[12 + en as usize * 12..];
        //            let ei_block = as_u32(extent_idx);
        let ei_leaf_lo = as_u32(&extent_idx[4..]);
        let ei_leaf_hi = as_u16(&extent_idx[8..]);
        let ee_leaf: u64 = ei_leaf_lo as u64 + ((ei_leaf_hi as u64) << 32);
        inner.seek(io::SeekFrom::Start(block_size as u64 * ee_leaf))?;
        let mut block = vec![0u8; block_size as usize];
        inner.read_exact(&mut block)?;
        add_found_extents(block_size, inner, &block, depth - 1, extents)?;
    }

    Ok(())
}

fn load_extent_tree<R>(mut inner: R, start: [u8; 4 * 15], block_size: u32) -> Result<Vec<Extent>>
    where R: io::Read + io::Seek {
    ensure!(0x0a == start[0] && 0xf3 == start[1],
        AssumptionFailed("invalid extent magic".to_string()));

    let extent_entries = as_u16(&start[2..]);
    // 4..: max; doesn't seem to be useful during read
    let depth = as_u16(&start[6..]);

    ensure!(depth <= 5,
        AssumptionFailed(format!("initial depth too high: {}", depth)));

    let mut extents = Vec::with_capacity(extent_entries as usize + depth as usize * 200);

    add_found_extents(block_size, &mut inner, &start, depth, &mut extents)?;

    extents.sort_by_key(|e| e.block);

    Ok(extents)
}

fn zero(buf: &mut [u8]) {
    for i in buf {
        *i = 0;
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use ::extents::TreeReader;
    use ::extents::Extent;

    #[test]
    fn simple_tree() {
        let data = (0..255u8).collect::<Vec<u8>>();
        let size = 4 + 4 * 2;
        let all_bytes = io::Cursor::new(data);
        let mut reader = TreeReader::create(all_bytes, 4, size as u64,
            vec![
                Extent {
                    block: 0,
                    start: 10,
                    len: 1,
                },
                Extent {
                    block: 1,
                    start: 20,
                    len: 2,
                }
            ]
        ).unwrap();

        let mut res = Vec::new();
        assert_eq!(size, reader.read_to_end(&mut res).unwrap());

        assert_eq!(vec![
            40, 41, 42, 43,
            80, 81, 82, 83,
            84, 85, 86, 87,
        ], res);
    }

    #[test]
    fn zero_buf() {
        let mut buf = [7u8; 5];
        assert_eq!(7, buf[0]);
        ::extents::zero(&mut buf);
        for i in &buf {
            assert_eq!(0, *i);
        }
    }
}
<|endoftext|>


<commit_before>use std::c_str::CString;
use std::error;
use std::fmt;
use std::str;

use curl_ffi as ffi;

pub use curl_ffi::CURLcode::CURLE_OK as OK;
pub use curl_ffi::CURLcode::CURLE_UNSUPPORTED_PROTOCOL as UNSUPPORTED_PROTOCOL;
pub use curl_ffi::CURLcode::CURLE_FAILED_INIT as FAILED_INIT;
pub use curl_ffi::CURLcode::CURLE_URL_MALFORMAT as URL_MALFORMAT;
pub use curl_ffi::CURLcode::CURLE_NOT_BUILT_IN as NOT_BUILT_IN;
pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_PROXY as COULDNT_RESOLVE_PROXY;
pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_HOST as COULDNT_RESOLVE_HOST;
pub use curl_ffi::CURLcode::CURLE_COULDNT_CONNECT as COULDNT_CONNECT;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_SERVER_REPLY as FTP_WEIRD_SERVER_REPLY;
pub use curl_ffi::CURLcode::CURLE_REMOTE_ACCESS_DENIED as REMOTE_ACCESS_DENIED;
pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_FAILED as FTP_ACCEPT_FAILED;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASS_REPLY as FTP_WEIRD_PASS_REPLY;
pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_TIMEOUT as FTP_ACCEPT_TIMEOUT;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASV_REPLY as FTP_WEIRD_PASV_REPLY;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_227_FORMAT as FTP_WEIRD_227_FORMAT;
pub use curl_ffi::CURLcode::CURLE_FTP_CANT_GET_HOST as FTP_CANT_GET_HOST;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE16 as OBSOLETE16;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_SET_TYPE as FTP_COULDNT_SET_TYPE;
pub use curl_ffi::CURLcode::CURLE_PARTIAL_FILE as PARTIAL_FILE;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_RETR_FILE as FTP_COULDNT_RETR_FILE;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE20 as OBSOLETE20;
pub use curl_ffi::CURLcode::CURLE_QUOTE_ERROR as QUOTE_ERROR;
pub use curl_ffi::CURLcode::CURLE_HTTP_RETURNED_ERROR as HTTP_RETURNED_ERROR;
pub use curl_ffi::CURLcode::CURLE_WRITE_ERROR as WRITE_ERROR;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE24 as OBSOLETE24;
pub use curl_ffi::CURLcode::CURLE_UPLOAD_FAILED as UPLOAD_FAILED;
pub use curl_ffi::CURLcode::CURLE_READ_ERROR as READ_ERROR;
pub use curl_ffi::CURLcode::CURLE_OUT_OF_MEMORY as OUT_OF_MEMORY;
pub use curl_ffi::CURLcode::CURLE_OPERATION_TIMEDOUT as OPERATION_TIMEDOUT;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE29 as OBSOLETE29;
pub use curl_ffi::CURLcode::CURLE_FTP_PORT_FAILED as FTP_PORT_FAILED;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_USE_REST as FTP_COULDNT_USE_REST;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE32 as OBSOLETE32;
pub use curl_ffi::CURLcode::CURLE_RANGE_ERROR as RANGE_ERROR;
pub use curl_ffi::CURLcode::CURLE_HTTP_POST_ERROR as HTTP_POST_ERROR;
pub use curl_ffi::CURLcode::CURLE_SSL_CONNECT_ERROR as SSL_CONNECT_ERROR;
pub use curl_ffi::CURLcode::CURLE_BAD_DOWNLOAD_RESUME as BAD_DOWNLOAD_RESUME;
pub use curl_ffi::CURLcode::CURLE_FILE_COULDNT_READ_FILE as FILE_COULDNT_READ_FILE;
pub use curl_ffi::CURLcode::CURLE_LDAP_CANNOT_BIND as LDAP_CANNOT_BIND;
pub use curl_ffi::CURLcode::CURLE_LDAP_SEARCH_FAILED as LDAP_SEARCH_FAILED;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE40 as OBSOLETE40;
pub use curl_ffi::CURLcode::CURLE_FUNCTION_NOT_FOUND as FUNCTION_NOT_FOUND;
pub use curl_ffi::CURLcode::CURLE_ABORTED_BY_CALLBACK as ABORTED_BY_CALLBACK;
pub use curl_ffi::CURLcode::CURLE_BAD_FUNCTION_ARGUMENT as BAD_FUNCTION_ARGUMENT;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE44 as OBSOLETE44;
pub use curl_ffi::CURLcode::CURLE_INTERFACE_FAILED as INTERFACE_FAILED;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE46 as OBSOLETE46;
pub use curl_ffi::CURLcode::CURLE_TOO_MANY_REDIRECTS  as TOO_MANY_REDIRECTS ;
pub use curl_ffi::CURLcode::CURLE_UNKNOWN_OPTION as UNKNOWN_OPTION;
pub use curl_ffi::CURLcode::CURLE_TELNET_OPTION_SYNTAX  as TELNET_OPTION_SYNTAX ;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE50 as OBSOLETE50;
pub use curl_ffi::CURLcode::CURLE_PEER_FAILED_VERIFICATION as PEER_FAILED_VERIFICATION;
pub use curl_ffi::CURLcode::CURLE_GOT_NOTHING as GOT_NOTHING;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_NOTFOUND as SSL_ENGINE_NOTFOUND;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_SETFAILED as SSL_ENGINE_SETFAILED;
pub use curl_ffi::CURLcode::CURLE_SEND_ERROR as SEND_ERROR;
pub use curl_ffi::CURLcode::CURLE_RECV_ERROR as RECV_ERROR;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE57 as OBSOLETE57;
pub use curl_ffi::CURLcode::CURLE_SSL_CERTPROBLEM as SSL_CERTPROBLEM;
pub use curl_ffi::CURLcode::CURLE_SSL_CIPHER as SSL_CIPHER;
pub use curl_ffi::CURLcode::CURLE_SSL_CACERT as SSL_CACERT;
pub use curl_ffi::CURLcode::CURLE_BAD_CONTENT_ENCODING as BAD_CONTENT_ENCODING;
pub use curl_ffi::CURLcode::CURLE_LDAP_INVALID_URL as LDAP_INVALID_URL;
pub use curl_ffi::CURLcode::CURLE_FILESIZE_EXCEEDED as FILESIZE_EXCEEDED;
pub use curl_ffi::CURLcode::CURLE_USE_SSL_FAILED as USE_SSL_FAILED;
pub use curl_ffi::CURLcode::CURLE_SEND_FAIL_REWIND as SEND_FAIL_REWIND;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_INITFAILED as SSL_ENGINE_INITFAILED;
pub use curl_ffi::CURLcode::CURLE_LOGIN_DENIED as LOGIN_DENIED;
pub use curl_ffi::CURLcode::CURLE_TFTP_NOTFOUND as TFTP_NOTFOUND;
pub use curl_ffi::CURLcode::CURLE_TFTP_PERM as TFTP_PERM;
pub use curl_ffi::CURLcode::CURLE_REMOTE_DISK_FULL as REMOTE_DISK_FULL;
pub use curl_ffi::CURLcode::CURLE_TFTP_ILLEGAL as TFTP_ILLEGAL;
pub use curl_ffi::CURLcode::CURLE_TFTP_UNKNOWNID as TFTP_UNKNOWNID;
pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_EXISTS as REMOTE_FILE_EXISTS;
pub use curl_ffi::CURLcode::CURLE_TFTP_NOSUCHUSER as TFTP_NOSUCHUSER;
pub use curl_ffi::CURLcode::CURLE_CONV_FAILED as CONV_FAILED;
pub use curl_ffi::CURLcode::CURLE_CONV_REQD as CONV_REQD;
pub use curl_ffi::CURLcode::CURLE_SSL_CACERT_BADFILE as SSL_CACERT_BADFILE;
pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_NOT_FOUND as REMOTE_FILE_NOT_FOUND;
pub use curl_ffi::CURLcode::CURLE_SSH as SSH;
pub use curl_ffi::CURLcode::CURLE_SSL_SHUTDOWN_FAILED as SSL_SHUTDOWN_FAILED;
pub use curl_ffi::CURLcode::CURLE_AGAIN as AGAIN;
pub use curl_ffi::CURLcode::CURLE_SSL_CRL_BADFILE as SSL_CRL_BADFILE;
pub use curl_ffi::CURLcode::CURLE_SSL_ISSUER_ERROR as SSL_ISSUER_ERROR;
pub use curl_ffi::CURLcode::CURLE_FTP_PRET_FAILED as FTP_PRET_FAILED;
pub use curl_ffi::CURLcode::CURLE_RTSP_CSEQ_ERROR as RTSP_CSEQ_ERROR;
pub use curl_ffi::CURLcode::CURLE_RTSP_SESSION_ERROR as RTSP_SESSION_ERROR;
pub use curl_ffi::CURLcode::CURLE_FTP_BAD_FILE_LIST as FTP_BAD_FILE_LIST;
pub use curl_ffi::CURLcode::CURLE_CHUNK_FAILED as CHUNK_FAILED;
pub use curl_ffi::CURLcode::CURLE_NO_CONNECTION_AVAILABLE as NO_CONNECTION_AVAILABLE;
pub use curl_ffi::CURLcode::CURLE_LAST as LAST;

pub struct ErrCode(pub ffi::CURLcode);

impl ErrCode {
    pub fn is_success(self) -> bool {
       self.code() as int == OK as int
    }

    pub fn code(self) -> ffi::CURLcode { let ErrCode(c) = self; c }
}

impl fmt::Show for ErrCode {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let c_str = unsafe {
            CString::new(ffi::curl_easy_strerror(self.code()), false)
        };

        match c_str.as_str() {
            Some(s) => write!(fmt, "{}", s),
            None => write!(fmt, "<unknown err>")
        }
    }
}

impl error::Error for ErrCode {
    fn description(&self) -> &str {
        let code = self.code();
        unsafe { str::raw::c_str_to_static_slice(ffi::curl_easy_strerror(code)) }
    }
}
<commit_msg>Renamed function call as per a deprecation warning.<commit_after>use std::c_str::CString;
use std::error;
use std::fmt;
use std::str;

use curl_ffi as ffi;

pub use curl_ffi::CURLcode::CURLE_OK as OK;
pub use curl_ffi::CURLcode::CURLE_UNSUPPORTED_PROTOCOL as UNSUPPORTED_PROTOCOL;
pub use curl_ffi::CURLcode::CURLE_FAILED_INIT as FAILED_INIT;
pub use curl_ffi::CURLcode::CURLE_URL_MALFORMAT as URL_MALFORMAT;
pub use curl_ffi::CURLcode::CURLE_NOT_BUILT_IN as NOT_BUILT_IN;
pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_PROXY as COULDNT_RESOLVE_PROXY;
pub use curl_ffi::CURLcode::CURLE_COULDNT_RESOLVE_HOST as COULDNT_RESOLVE_HOST;
pub use curl_ffi::CURLcode::CURLE_COULDNT_CONNECT as COULDNT_CONNECT;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_SERVER_REPLY as FTP_WEIRD_SERVER_REPLY;
pub use curl_ffi::CURLcode::CURLE_REMOTE_ACCESS_DENIED as REMOTE_ACCESS_DENIED;
pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_FAILED as FTP_ACCEPT_FAILED;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASS_REPLY as FTP_WEIRD_PASS_REPLY;
pub use curl_ffi::CURLcode::CURLE_FTP_ACCEPT_TIMEOUT as FTP_ACCEPT_TIMEOUT;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_PASV_REPLY as FTP_WEIRD_PASV_REPLY;
pub use curl_ffi::CURLcode::CURLE_FTP_WEIRD_227_FORMAT as FTP_WEIRD_227_FORMAT;
pub use curl_ffi::CURLcode::CURLE_FTP_CANT_GET_HOST as FTP_CANT_GET_HOST;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE16 as OBSOLETE16;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_SET_TYPE as FTP_COULDNT_SET_TYPE;
pub use curl_ffi::CURLcode::CURLE_PARTIAL_FILE as PARTIAL_FILE;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_RETR_FILE as FTP_COULDNT_RETR_FILE;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE20 as OBSOLETE20;
pub use curl_ffi::CURLcode::CURLE_QUOTE_ERROR as QUOTE_ERROR;
pub use curl_ffi::CURLcode::CURLE_HTTP_RETURNED_ERROR as HTTP_RETURNED_ERROR;
pub use curl_ffi::CURLcode::CURLE_WRITE_ERROR as WRITE_ERROR;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE24 as OBSOLETE24;
pub use curl_ffi::CURLcode::CURLE_UPLOAD_FAILED as UPLOAD_FAILED;
pub use curl_ffi::CURLcode::CURLE_READ_ERROR as READ_ERROR;
pub use curl_ffi::CURLcode::CURLE_OUT_OF_MEMORY as OUT_OF_MEMORY;
pub use curl_ffi::CURLcode::CURLE_OPERATION_TIMEDOUT as OPERATION_TIMEDOUT;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE29 as OBSOLETE29;
pub use curl_ffi::CURLcode::CURLE_FTP_PORT_FAILED as FTP_PORT_FAILED;
pub use curl_ffi::CURLcode::CURLE_FTP_COULDNT_USE_REST as FTP_COULDNT_USE_REST;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE32 as OBSOLETE32;
pub use curl_ffi::CURLcode::CURLE_RANGE_ERROR as RANGE_ERROR;
pub use curl_ffi::CURLcode::CURLE_HTTP_POST_ERROR as HTTP_POST_ERROR;
pub use curl_ffi::CURLcode::CURLE_SSL_CONNECT_ERROR as SSL_CONNECT_ERROR;
pub use curl_ffi::CURLcode::CURLE_BAD_DOWNLOAD_RESUME as BAD_DOWNLOAD_RESUME;
pub use curl_ffi::CURLcode::CURLE_FILE_COULDNT_READ_FILE as FILE_COULDNT_READ_FILE;
pub use curl_ffi::CURLcode::CURLE_LDAP_CANNOT_BIND as LDAP_CANNOT_BIND;
pub use curl_ffi::CURLcode::CURLE_LDAP_SEARCH_FAILED as LDAP_SEARCH_FAILED;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE40 as OBSOLETE40;
pub use curl_ffi::CURLcode::CURLE_FUNCTION_NOT_FOUND as FUNCTION_NOT_FOUND;
pub use curl_ffi::CURLcode::CURLE_ABORTED_BY_CALLBACK as ABORTED_BY_CALLBACK;
pub use curl_ffi::CURLcode::CURLE_BAD_FUNCTION_ARGUMENT as BAD_FUNCTION_ARGUMENT;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE44 as OBSOLETE44;
pub use curl_ffi::CURLcode::CURLE_INTERFACE_FAILED as INTERFACE_FAILED;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE46 as OBSOLETE46;
pub use curl_ffi::CURLcode::CURLE_TOO_MANY_REDIRECTS  as TOO_MANY_REDIRECTS ;
pub use curl_ffi::CURLcode::CURLE_UNKNOWN_OPTION as UNKNOWN_OPTION;
pub use curl_ffi::CURLcode::CURLE_TELNET_OPTION_SYNTAX  as TELNET_OPTION_SYNTAX ;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE50 as OBSOLETE50;
pub use curl_ffi::CURLcode::CURLE_PEER_FAILED_VERIFICATION as PEER_FAILED_VERIFICATION;
pub use curl_ffi::CURLcode::CURLE_GOT_NOTHING as GOT_NOTHING;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_NOTFOUND as SSL_ENGINE_NOTFOUND;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_SETFAILED as SSL_ENGINE_SETFAILED;
pub use curl_ffi::CURLcode::CURLE_SEND_ERROR as SEND_ERROR;
pub use curl_ffi::CURLcode::CURLE_RECV_ERROR as RECV_ERROR;
pub use curl_ffi::CURLcode::CURLE_OBSOLETE57 as OBSOLETE57;
pub use curl_ffi::CURLcode::CURLE_SSL_CERTPROBLEM as SSL_CERTPROBLEM;
pub use curl_ffi::CURLcode::CURLE_SSL_CIPHER as SSL_CIPHER;
pub use curl_ffi::CURLcode::CURLE_SSL_CACERT as SSL_CACERT;
pub use curl_ffi::CURLcode::CURLE_BAD_CONTENT_ENCODING as BAD_CONTENT_ENCODING;
pub use curl_ffi::CURLcode::CURLE_LDAP_INVALID_URL as LDAP_INVALID_URL;
pub use curl_ffi::CURLcode::CURLE_FILESIZE_EXCEEDED as FILESIZE_EXCEEDED;
pub use curl_ffi::CURLcode::CURLE_USE_SSL_FAILED as USE_SSL_FAILED;
pub use curl_ffi::CURLcode::CURLE_SEND_FAIL_REWIND as SEND_FAIL_REWIND;
pub use curl_ffi::CURLcode::CURLE_SSL_ENGINE_INITFAILED as SSL_ENGINE_INITFAILED;
pub use curl_ffi::CURLcode::CURLE_LOGIN_DENIED as LOGIN_DENIED;
pub use curl_ffi::CURLcode::CURLE_TFTP_NOTFOUND as TFTP_NOTFOUND;
pub use curl_ffi::CURLcode::CURLE_TFTP_PERM as TFTP_PERM;
pub use curl_ffi::CURLcode::CURLE_REMOTE_DISK_FULL as REMOTE_DISK_FULL;
pub use curl_ffi::CURLcode::CURLE_TFTP_ILLEGAL as TFTP_ILLEGAL;
pub use curl_ffi::CURLcode::CURLE_TFTP_UNKNOWNID as TFTP_UNKNOWNID;
pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_EXISTS as REMOTE_FILE_EXISTS;
pub use curl_ffi::CURLcode::CURLE_TFTP_NOSUCHUSER as TFTP_NOSUCHUSER;
pub use curl_ffi::CURLcode::CURLE_CONV_FAILED as CONV_FAILED;
pub use curl_ffi::CURLcode::CURLE_CONV_REQD as CONV_REQD;
pub use curl_ffi::CURLcode::CURLE_SSL_CACERT_BADFILE as SSL_CACERT_BADFILE;
pub use curl_ffi::CURLcode::CURLE_REMOTE_FILE_NOT_FOUND as REMOTE_FILE_NOT_FOUND;
pub use curl_ffi::CURLcode::CURLE_SSH as SSH;
pub use curl_ffi::CURLcode::CURLE_SSL_SHUTDOWN_FAILED as SSL_SHUTDOWN_FAILED;
pub use curl_ffi::CURLcode::CURLE_AGAIN as AGAIN;
pub use curl_ffi::CURLcode::CURLE_SSL_CRL_BADFILE as SSL_CRL_BADFILE;
pub use curl_ffi::CURLcode::CURLE_SSL_ISSUER_ERROR as SSL_ISSUER_ERROR;
pub use curl_ffi::CURLcode::CURLE_FTP_PRET_FAILED as FTP_PRET_FAILED;
pub use curl_ffi::CURLcode::CURLE_RTSP_CSEQ_ERROR as RTSP_CSEQ_ERROR;
pub use curl_ffi::CURLcode::CURLE_RTSP_SESSION_ERROR as RTSP_SESSION_ERROR;
pub use curl_ffi::CURLcode::CURLE_FTP_BAD_FILE_LIST as FTP_BAD_FILE_LIST;
pub use curl_ffi::CURLcode::CURLE_CHUNK_FAILED as CHUNK_FAILED;
pub use curl_ffi::CURLcode::CURLE_NO_CONNECTION_AVAILABLE as NO_CONNECTION_AVAILABLE;
pub use curl_ffi::CURLcode::CURLE_LAST as LAST;

pub struct ErrCode(pub ffi::CURLcode);

impl ErrCode {
    pub fn is_success(self) -> bool {
       self.code() as int == OK as int
    }

    pub fn code(self) -> ffi::CURLcode { let ErrCode(c) = self; c }
}

impl fmt::Show for ErrCode {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let c_str = unsafe {
            CString::new(ffi::curl_easy_strerror(self.code()), false)
        };

        match c_str.as_str() {
            Some(s) => write!(fmt, "{}", s),
            None => write!(fmt, "<unknown err>")
        }
    }
}

impl error::Error for ErrCode {
    fn description(&self) -> &str {
        let code = self.code();
        unsafe { str::from_c_str(ffi::curl_easy_strerror(code)) }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

// TODO: add tests

use strings::string_buffer::StringBuffer;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Write, Read, stdout, BufWriter};

use config::{NewlineStyle, Config, WriteMode};
use rustfmt_diff::{make_diff, print_diff, Mismatch, DiffLine};

// A map of the files of a crate, with their new content
pub type FileMap = HashMap<String, StringBuffer>;

// Append a newline to the end of each file.
pub fn append_newlines(file_map: &mut FileMap) {
    for (_, s) in file_map.iter_mut() {
        s.push_str("\n");
    }
}

pub fn write_all_files(file_map: &FileMap,
                       mode: WriteMode,
                       config: &Config)
                       -> Result<(), io::Error> {
    output_heading(mode).ok();
    for filename in file_map.keys() {
        try!(write_file(&file_map[filename], filename, mode, config));
    }
    output_trailing(mode).ok();

    Ok(())
}

pub fn output_heading(mode: WriteMode) -> Result<(), io::Error> {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    match mode {
        WriteMode::Checkstyle => {
            let mut xml_heading = String::new();
            xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            xml_heading.push_str("\n");
            xml_heading.push_str("<checkstyle version=\"4.3\">");
            try!(write!(stdout, "{}", xml_heading));
            Ok(())
        }
        _ => Ok(()),
    }
}

pub fn output_trailing(mode: WriteMode) -> Result<(), io::Error> {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    match mode {
        WriteMode::Checkstyle => {
            let mut xml_tail = String::new();
            xml_tail.push_str("</checkstyle>");
            try!(write!(stdout, "{}", xml_tail));
            Ok(())
        }
        _ => Ok(()),
    }
}

pub fn output_checkstyle_file<T>(mut writer: T,
                                 filename: &str,
                                 diff: Vec<Mismatch>)
                                 -> Result<(), io::Error>
    where T: Write
{
    try!(write!(writer, "<file name=\"{}\">", filename));
    for mismatch in diff {
        for line in mismatch.lines {
            match line {
                DiffLine::Expected(ref str) => {
                    let message = xml_escape_str(&str);
                    // TODO XML encode str here.
                    try!(write!(writer,
                                "<error line=\"{}\" severity=\"error\" message=\"Should be \
                                 `{}`\" />",
                                mismatch.line_number,
                                message));
                }
                _ => {
                    // Do nothing with context and expected.
                }
            }
        }
    }
    try!(write!(writer, "</file>"));
    Ok(())
}

// Convert special characters into XML entities.
// This is needed for checkstyle output.
fn xml_escape_str(string: &str) -> String {
    let mut out = String::new();
    for c in string.chars() {
        match c {
            '<' => out.push_str("<"),
            '>' => out.push_str(">"),
            '"' => out.push_str("""),
            '\'' => out.push_str("'"),
            '&' => out.push_str("&"),
            _ => out.push(c),
        }
    }
    out
}

// Prints all newlines either as `\n` or as `\r\n`.
pub fn write_system_newlines<T>(writer: T,
                                text: &StringBuffer,
                                config: &Config)
                                -> Result<(), io::Error>
    where T: Write
{
    // Buffer output, since we're writing a since char at a time.
    let mut writer = BufWriter::new(writer);

    let style = if config.newline_style == NewlineStyle::Native {
        if cfg!(windows) {
            NewlineStyle::Windows
        } else {
            NewlineStyle::Unix
        }
    } else {
        config.newline_style
    };

    match style {
        NewlineStyle::Unix => write!(writer, "{}", text),
        NewlineStyle::Windows => {
            for (c, _) in text.chars() {
                match c {
                    '\n' => try!(write!(writer, "\r\n")),
                    '\r' => continue,
                    c => try!(write!(writer, "{}", c)),
                }
            }
            Ok(())
        }
        NewlineStyle::Native => unreachable!(),
    }
}

pub fn write_file(text: &StringBuffer,
                  filename: &str,
                  mode: WriteMode,
                  config: &Config)
                  -> Result<Option<String>, io::Error> {

    fn source_and_formatted_text(text: &StringBuffer,
                                 filename: &str,
                                 config: &Config)
                                 -> Result<(String, String), io::Error> {
        let mut f = try!(File::open(filename));
        let mut ori_text = String::new();
        try!(f.read_to_string(&mut ori_text));
        let mut v = Vec::new();
        try!(write_system_newlines(&mut v, text, config));
        let fmt_text = String::from_utf8(v).unwrap();
        Ok((ori_text, fmt_text))
    }

    match mode {
        WriteMode::Replace => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                if fmt != ori {
                    // Do a little dance to make writing safer - write to a temp file
                    // rename the original to a .bk, then rename the temp file to the
                    // original.
                    let tmp_name = filename.to_owned() + ".tmp";
                    let bk_name = filename.to_owned() + ".bk";
                    {
                        // Write text to temp file
                        let tmp_file = try!(File::create(&tmp_name));
                        try!(write_system_newlines(tmp_file, text, config));
                    }

                    try!(fs::rename(filename, bk_name));
                    try!(fs::rename(tmp_name, filename));
                }
            }
        }
        WriteMode::Overwrite => {
            // Write text directly over original file.
            let file = try!(File::create(filename));
            try!(write_system_newlines(file, text, config));
        }
        WriteMode::Plain => {
            let stdout = stdout();
            let stdout = stdout.lock();
            try!(write_system_newlines(stdout, text, config));
        }
        WriteMode::Display | WriteMode::Coverage => {
            println!("{}:\n", filename);
            let stdout = stdout();
            let stdout = stdout.lock();
            try!(write_system_newlines(stdout, text, config));
        }
        WriteMode::Diff => {
            println!("Diff of {}:\n", filename);
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                print_diff(make_diff(&ori, &fmt, 3),
                           |line_num| format!("\nDiff at line {}:", line_num));
            }
        }
        WriteMode::Default => {
            unreachable!("The WriteMode should NEVER Be default at this point!");
        }
        WriteMode::Checkstyle => {
            let stdout = stdout();
            let stdout = stdout.lock();
            // Generate the diff for the current file.
            let mut f = try!(File::open(filename));
            let mut ori_text = String::new();
            try!(f.read_to_string(&mut ori_text));
            let mut v = Vec::new();
            try!(write_system_newlines(&mut v, text, config));
            let fmt_text = String::from_utf8(v).unwrap();
            let diff = make_diff(&ori_text, &fmt_text, 3);
            // Output the XML tags for the lines that are different.
            output_checkstyle_file(stdout, filename, diff).unwrap();
        }
        WriteMode::Return => {
            // io::Write is not implemented for String, working around with
            // Vec<u8>
            let mut v = Vec::new();
            try!(write_system_newlines(&mut v, text, config));
            // won't panic, we are writing correct utf8
            return Ok(Some(String::from_utf8(v).unwrap()));
        }
    }

    Ok(None)
}
<commit_msg>Update based on pull request feedback.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

// TODO: add tests

use strings::string_buffer::StringBuffer;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Write, Read, stdout, BufWriter};

use config::{NewlineStyle, Config, WriteMode};
use rustfmt_diff::{make_diff, print_diff, Mismatch, DiffLine};

// A map of the files of a crate, with their new content
pub type FileMap = HashMap<String, StringBuffer>;

// Append a newline to the end of each file.
pub fn append_newlines(file_map: &mut FileMap) {
    for (_, s) in file_map.iter_mut() {
        s.push_str("\n");
    }
}

pub fn write_all_files(file_map: &FileMap,
                       mode: WriteMode,
                       config: &Config)
                       -> Result<(), io::Error> {
    output_heading(mode).ok();
    for filename in file_map.keys() {
        try!(write_file(&file_map[filename], filename, mode, config));
    }
    output_trailing(mode).ok();

    Ok(())
}

pub fn output_heading(mode: WriteMode) -> Result<(), io::Error> {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    match mode {
        WriteMode::Checkstyle => {
            let mut xml_heading = String::new();
            xml_heading.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            xml_heading.push_str("\n");
            xml_heading.push_str("<checkstyle version=\"4.3\">");
            try!(write!(stdout, "{}", xml_heading));
            Ok(())
        }
        _ => Ok(()),
    }
}

pub fn output_trailing(mode: WriteMode) -> Result<(), io::Error> {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    match mode {
        WriteMode::Checkstyle => {
            let mut xml_tail = String::new();
            xml_tail.push_str("</checkstyle>");
            try!(write!(stdout, "{}", xml_tail));
            Ok(())
        }
        _ => Ok(()),
    }
}

pub fn output_checkstyle_file<T>(mut writer: T,
                                 filename: &str,
                                 diff: Vec<Mismatch>)
                                 -> Result<(), io::Error>
    where T: Write
{
    try!(write!(writer, "<file name=\"{}\">", filename));
    for mismatch in diff {
        for line in mismatch.lines {
            match line {
                DiffLine::Expected(ref str) => {
                    let message = xml_escape_str(&str);
                    // TODO XML encode str here.
                    try!(write!(writer,
                                "<error line=\"{}\" severity=\"warning\" message=\"Should be \
                                 `{}`\" />",
                                mismatch.line_number,
                                message));
                }
                _ => {
                    // Do nothing with context and expected.
                }
            }
        }
    }
    try!(write!(writer, "</file>"));
    Ok(())
}

// Convert special characters into XML entities.
// This is needed for checkstyle output.
fn xml_escape_str(string: &str) -> String {
    let mut out = String::new();
    for c in string.chars() {
        match c {
            '<' => out.push_str("<"),
            '>' => out.push_str(">"),
            '"' => out.push_str("""),
            '\'' => out.push_str("'"),
            '&' => out.push_str("&"),
            _ => out.push(c),
        }
    }
    out
}

// Prints all newlines either as `\n` or as `\r\n`.
pub fn write_system_newlines<T>(writer: T,
                                text: &StringBuffer,
                                config: &Config)
                                -> Result<(), io::Error>
    where T: Write
{
    // Buffer output, since we're writing a since char at a time.
    let mut writer = BufWriter::new(writer);

    let style = if config.newline_style == NewlineStyle::Native {
        if cfg!(windows) {
            NewlineStyle::Windows
        } else {
            NewlineStyle::Unix
        }
    } else {
        config.newline_style
    };

    match style {
        NewlineStyle::Unix => write!(writer, "{}", text),
        NewlineStyle::Windows => {
            for (c, _) in text.chars() {
                match c {
                    '\n' => try!(write!(writer, "\r\n")),
                    '\r' => continue,
                    c => try!(write!(writer, "{}", c)),
                }
            }
            Ok(())
        }
        NewlineStyle::Native => unreachable!(),
    }
}

pub fn write_file(text: &StringBuffer,
                  filename: &str,
                  mode: WriteMode,
                  config: &Config)
                  -> Result<Option<String>, io::Error> {

    fn source_and_formatted_text(text: &StringBuffer,
                                 filename: &str,
                                 config: &Config)
                                 -> Result<(String, String), io::Error> {
        let mut f = try!(File::open(filename));
        let mut ori_text = String::new();
        try!(f.read_to_string(&mut ori_text));
        let mut v = Vec::new();
        try!(write_system_newlines(&mut v, text, config));
        let fmt_text = String::from_utf8(v).unwrap();
        Ok((ori_text, fmt_text))
    }

    fn create_diff(filename: &str,
                   text: &StringBuffer,
                   config: &Config)
                   -> Result<Vec<Mismatch>, io::Error> {
        let ori_text, fmt_text = try!(source_and_formatted_text(text, filename, config));
        Ok(make_diff(&ori_text, &fmt_text, 3))
    }

    match mode {
        WriteMode::Replace => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                if fmt != ori {
                    // Do a little dance to make writing safer - write to a temp file
                    // rename the original to a .bk, then rename the temp file to the
                    // original.
                    let tmp_name = filename.to_owned() + ".tmp";
                    let bk_name = filename.to_owned() + ".bk";
                    {
                        // Write text to temp file
                        let tmp_file = try!(File::create(&tmp_name));
                        try!(write_system_newlines(tmp_file, text, config));
                    }

                    try!(fs::rename(filename, bk_name));
                    try!(fs::rename(tmp_name, filename));
                }
            }
        }
        WriteMode::Overwrite => {
            // Write text directly over original file.
            let file = try!(File::create(filename));
            try!(write_system_newlines(file, text, config));
        }
        WriteMode::Plain => {
            let stdout = stdout();
            let stdout = stdout.lock();
            try!(write_system_newlines(stdout, text, config));
        }
        WriteMode::Display | WriteMode::Coverage => {
            println!("{}:\n", filename);
            let stdout = stdout();
            let stdout = stdout.lock();
            try!(write_system_newlines(stdout, text, config));
        }
        WriteMode::Diff => {
            println!("Diff of {}:\n", filename);
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                print_diff(make_diff(&ori, &fmt, 3),
                           |line_num| format!("\nDiff at line {}:", line_num));
            }
        }
        WriteMode::Default => {
            unreachable!("The WriteMode should NEVER Be default at this point!");
        }
        WriteMode::Checkstyle => {
            let stdout = stdout();
            let stdout = stdout.lock();
            let diff = try!(create_diff(filename, text, config));
            // Output the XML tags for the lines that are different.
            try!(output_checkstyle_file(stdout, filename, diff));
        }
        WriteMode::Return => {
            // io::Write is not implemented for String, working around with
            // Vec<u8>
            let mut v = Vec::new();
            try!(write_system_newlines(&mut v, text, config));
            // won't panic, we are writing correct utf8
            return Ok(Some(String::from_utf8(v).unwrap()));
        }
    }

    Ok(None)
}
<|endoftext|>


<commit_before>use core::str::Utf8Error;
use std::path::PathBuf;

use iron::prelude::*;
use iron::status;
use mount::Mount;
use url::percent_encoding::percent_decode;

use collection::browse as collection_browse;
use collection::flatten as collection_flatten;

pub fn get_api_handler() -> Mount {
    let mut mount = Mount::new();
    mount.mount("/browse/", self::browse)
        .mount("/flatten/", self::flatten);
    mount
}

fn path_from_request(request: &Request) -> Result<PathBuf, Utf8Error> {
    let path_string = request.url.path().join("/");
    let decoded_path = percent_decode(path_string.as_bytes()).decode_utf8();
    decoded_path.map(|s| PathBuf::from(s.into_owned()))
}

fn browse(request: &mut Request) -> IronResult<Response> {
    let path = path_from_request(request);
    if path.is_err() {
        return Ok(Response::with((status::BadRequest)));
    }
    collection_browse(&path.unwrap());
    Ok(Response::with((status::Ok, "TODO browse data here")))
}

fn flatten(request: &mut Request) -> IronResult<Response> {
    let path = path_from_request(request);
    if path.is_err() {
        return Ok(Response::with((status::BadRequest)));
    }
    collection_flatten(&path.unwrap());
    Ok(Response::with((status::Ok, "TODO Flatten data here")))
}
<commit_msg>Minor cleanup<commit_after>use core::str::Utf8Error;
use std::path::PathBuf;
use std::ops::Deref;

use iron::prelude::*;
use iron::status;
use mount::Mount;
use url::percent_encoding::percent_decode;

use collection;

pub fn get_api_handler() -> Mount {
    let mut mount = Mount::new();
    mount.mount("/browse/", self::browse)
        .mount("/flatten/", self::flatten);
    mount
}

fn path_from_request(request: &Request) -> Result<PathBuf, Utf8Error> {
    let path_string = request.url.path().join("/");
    let decoded_path = percent_decode(path_string.as_bytes()).decode_utf8();
    decoded_path.map(|s| PathBuf::from(s.deref()))
}

fn browse(request: &mut Request) -> IronResult<Response> {
    let path = path_from_request(request);
    if path.is_err() {
        return Ok(Response::with((status::BadRequest)));
    }
    collection::browse(&path.unwrap());
    Ok(Response::with((status::Ok, "TODO browse data here")))
}

fn flatten(request: &mut Request) -> IronResult<Response> {
    let path = path_from_request(request);
    if path.is_err() {
        return Ok(Response::with((status::BadRequest)));
    }
    collection::flatten(&path.unwrap());
    Ok(Response::with((status::Ok, "TODO Flatten data here")))
}
<|endoftext|>