<commit_before>extern crate rustbox;

const MINOS: usize = 4;

#[derive(Copy, Clone, Debug)]
pub struct Tetromino {
    pub minos: [Point; MINOS],
    pub pos: Point,
    pub tetro_type: TetrominoType,
    pub rot: Rotation,
}

#[derive(Copy, Clone, Debug)]
pub struct Point {
    pub x: isize,
    pub y: isize,
}

#[derive(Copy, Clone, Debug)]
pub enum TetrominoType {
    I,
    J,
    L,
    O,
    S,
    T,
    Z,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Rotation {
    Spawn,
    Right,
    Rot2,
    Left,
}

#[derive(Copy, Clone, Debug)]
pub enum Direction {
    Clockwise,
    CounterClockwise,
}

impl Tetromino {
    /// Initializes a new Tetromino struct of a specified type and rotation
    pub fn new(pos: Point, tetro_type: TetrominoType, rot: Rotation) -> Self {
        let mut tetromino = TETROMINOS[tetro_type as usize][rot as usize].clone();
        tetromino.pos = pos;
        tetromino
    }
}

impl Point {
    /// Initializes a new Point struct
    pub fn new(x: isize, y: isize) -> Self {
        Point {
            x: x,
            y: y,
        }
    }
}

pub const TYPES: usize = 7;
pub const ROTS: usize = 4;
const TETROMINOS: [[Tetromino; ROTS]; TYPES] = [I, J, L, O, S, T, Z];

const I: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 3, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
            Point { x: 2, y: 3 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 2 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
            Point { x: 3, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 1, y: 3 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Left,
    },
];

const J: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 0 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 0, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Left,
    },
];

const L: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 0, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Left,
    },
];

const O: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Left,
    },
];

const S: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 0, y: 2 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Left,
    },
];

const T: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Left,
    },
];

const Z: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 0, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Left,
    },
];<commit_msg>Fix points for left rotated J<commit_after>extern crate rustbox;

const MINOS: usize = 4;

#[derive(Copy, Clone, Debug)]
pub struct Tetromino {
    pub minos: [Point; MINOS],
    pub pos: Point,
    pub tetro_type: TetrominoType,
    pub rot: Rotation,
}

#[derive(Copy, Clone, Debug)]
pub struct Point {
    pub x: isize,
    pub y: isize,
}

#[derive(Copy, Clone, Debug)]
pub enum TetrominoType {
    I,
    J,
    L,
    O,
    S,
    T,
    Z,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Rotation {
    Spawn,
    Right,
    Rot2,
    Left,
}

#[derive(Copy, Clone, Debug)]
pub enum Direction {
    Clockwise,
    CounterClockwise,
}

impl Tetromino {
    /// Initializes a new Tetromino struct of a specified type and rotation
    pub fn new(pos: Point, tetro_type: TetrominoType, rot: Rotation) -> Self {
        let mut tetromino = TETROMINOS[tetro_type as usize][rot as usize].clone();
        tetromino.pos = pos;
        tetromino
    }
}

impl Point {
    /// Initializes a new Point struct
    pub fn new(x: isize, y: isize) -> Self {
        Point {
            x: x,
            y: y,
        }
    }
}

pub const TYPES: usize = 7;
pub const ROTS: usize = 4;
const TETROMINOS: [[Tetromino; ROTS]; TYPES] = [I, J, L, O, S, T, Z];

const I: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 3, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
            Point { x: 2, y: 3 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 2 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
            Point { x: 3, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 1, y: 3 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::I,
        rot: Rotation::Left,
    },
];

const J: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 0 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 0, y: 2 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::J,
        rot: Rotation::Left,
    },
];

const L: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 0, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::L,
        rot: Rotation::Left,
    },
];

const O: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::O,
        rot: Rotation::Left,
    },
];

const S: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 2, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 0, y: 2 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::S,
        rot: Rotation::Left,
    },
];

const T: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::T,
        rot: Rotation::Left,
    },
];

const Z: [Tetromino; ROTS] = [
    Tetromino {
        minos: [
            Point { x: 0, y: 0 },
            Point { x: 1, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Spawn,
    },

    Tetromino {
        minos: [
            Point { x: 2, y: 0 },
            Point { x: 1, y: 1 },
            Point { x: 2, y: 1 },
            Point { x: 1, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Right,
    },

    Tetromino {
        minos: [
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 1, y: 2 },
            Point { x: 2, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Rot2,
    },

    Tetromino {
        minos: [
            Point { x: 1, y: 0 },
            Point { x: 0, y: 1 },
            Point { x: 1, y: 1 },
            Point { x: 0, y: 2 },
        ],

        pos: Point { x: 0, y: 0 },
        tetro_type: TetrominoType::Z,
        rot: Rotation::Left,
    },
];<|endoftext|>


<commit_before>use errors::OscError;
use types::{OscBundle, OscMessage, OscPacket, OscType, Result};

use byteorder::{BigEndian, ByteOrder};

/// Takes a reference to an OSC packet and returns
/// a byte vector on success. If the packet was invalid
/// an `OscError` is returned.
///
/// # Example
///
/// ```
/// use rosc::{OscPacket,OscMessage,OscType};
/// use rosc::encoder;
///
/// let packet = OscPacket::Message(OscMessage{
///         addr: "/greet/me".to_string(),
///         args: Some(vec![OscType::String("hi!".to_string())])
///     }
/// );
/// assert!(encoder::encode(&packet).is_ok())
/// ```
pub fn encode(packet: &OscPacket) -> Result<Vec<u8>> {
    match *packet {
        OscPacket::Message(ref msg) => encode_message(msg),
        OscPacket::Bundle(ref bundle) => encode_bundle(bundle),
    }
}

fn encode_message(msg: &OscMessage) -> Result<Vec<u8>> {
    let mut msg_bytes: Vec<u8> = Vec::new();

    msg_bytes.extend(encode_string(msg.addr.clone()));
    let mut type_tags: Vec<char> = vec![','];
    let mut arg_bytes: Vec<u8> = Vec::new();

    if let Some(ref args) = msg.args {
        for arg in args {
            let (bytes, tags): (Option<Vec<u8>>, String) = encode_arg(arg)?;

            type_tags.extend(tags.chars());
            if bytes.is_some() {
                arg_bytes.extend(bytes.unwrap());
            }
        }
    }

    msg_bytes.extend(encode_string(type_tags.into_iter().collect::<String>()));
    if !arg_bytes.is_empty() {
        msg_bytes.extend(arg_bytes);
    }
    Ok(msg_bytes)
}

fn encode_bundle(bundle: &OscBundle) -> Result<Vec<u8>> {
    let mut bundle_bytes: Vec<u8> = Vec::new();
    bundle_bytes.extend(encode_string("#bundle".to_string()).into_iter());

    match encode_arg(&bundle.timetag)? {
        (Some(x), _) => {
            bundle_bytes.extend(x.into_iter());
        }
        (None, _) => {
            return Err(OscError::BadBundle("Missing time tag!".to_string()));
        }
    }

    if bundle.content.is_empty() {
        // TODO: A bundle of length zero, should this really be supported?
        bundle_bytes.extend([0u8; 4].into_iter());
        return Ok(bundle_bytes);
    }

    for packet in &bundle.content {
        match *packet {
            OscPacket::Message(ref m) => {
                let msg = encode_message(m)?;
                let mut msg_size = vec![0u8; 4];
                BigEndian::write_u32(&mut msg_size, msg.len() as u32);
                bundle_bytes.extend(msg_size.into_iter().chain(msg.into_iter()));
            }
            OscPacket::Bundle(ref b) => {
                let bdl = encode_bundle(b)?;
                let mut bdl_size = vec![0u8; 4];
                BigEndian::write_u32(&mut bdl_size, bdl.len() as u32);
                bundle_bytes.extend(bdl_size.into_iter().chain(bdl.into_iter()));
            }
        }
    }

    Ok(bundle_bytes)
}

fn encode_arg(arg: &OscType) -> Result<(Option<Vec<u8>>, String)> {
    match *arg {
        OscType::Int(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_i32(&mut bytes, *x);
            Ok((Some(bytes), "i".into()))
        }
        OscType::Long(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_i64(&mut bytes, *x);
            Ok((Some(bytes), "h".into()))
        }
        OscType::Float(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_f32(&mut bytes, *x);
            Ok((Some(bytes), "f".into()))
        }
        OscType::Double(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_f64(&mut bytes, *x);
            Ok((Some(bytes), "d".into()))
        }
        OscType::Char(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_u32(&mut bytes, *x as u32);
            Ok((Some(bytes), "c".into()))
        }
        OscType::String(ref x) => Ok((Some(encode_string(x.clone())), "s".into())),
        OscType::Blob(ref x) => {
            let padded_blob_length: usize = pad(x.len() as u64) as usize;
            let mut bytes = vec![0u8; 4 + padded_blob_length];
            // write length
            BigEndian::write_i32(&mut bytes[..4], x.len() as i32);
            for (i, v) in x.iter().enumerate() {
                bytes[i + 4] = *v;
            }
            Ok((Some(bytes), "b".into()))
        }
        OscType::Time(ref x, ref y) => Ok((Some(encode_time_tag(*x, *y)), "t".into())),
        OscType::Midi(ref x) => Ok((Some(vec![x.port, x.status, x.data1, x.data2]), "m'".into())),
        OscType::Color(ref x) => Ok((Some(vec![x.red, x.green, x.blue, x.alpha]), "r".into())),
        OscType::Bool(ref x) => {
            if *x {
                Ok((None, "T".into()))
            } else {
                Ok((None, "F".into()))
            }
        }
        OscType::Nil => Ok((None, "N".into())),
        OscType::Inf => Ok((None, "I".into())),
        OscType::Array(ref x) => {
            let mut bytes = vec![0u8; 0];
            let mut type_tags = String::from("[");
            for v in x.iter() {
                match encode_arg(v) {
                    Ok((Some(other_bytes), other_type_tags)) => {
                        bytes.extend(other_bytes);
                        type_tags.push_str(&other_type_tags);
                    }
                    Ok((None, other_type_tags)) => {
                        type_tags.push_str(&other_type_tags);
                    }
                    Err(err) => return Err(err),
                }
            }
            type_tags.push_str("]");
            Ok((Some(bytes), type_tags))
        }
    }
}

/// Null terminates the byte representation of string `s` and
/// adds null bytes until the length of the result is a
/// multiple of 4.
pub fn encode_string<S: Into<String>>(s: S) -> Vec<u8> {
    let mut bytes: Vec<u8> = s.into().as_bytes().into();
    bytes.push(0u8);
    pad_bytes(&mut bytes);
    bytes
}

fn pad_bytes(bytes: &mut Vec<u8>) {
    let padded_lengh = pad(bytes.len() as u64);
    while bytes.len() < padded_lengh as usize {
        bytes.push(0u8)
    }
}

/// Returns the position padded to 4 bytes.
///
/// # Example
///
/// ```
/// use rosc::encoder;
///
/// let pos: u64 = 10;
/// assert_eq!(12u64, encoder::pad(pos))
/// ```
pub fn pad(pos: u64) -> u64 {
    match pos % 4 {
        0 => pos,
        d => pos + (4 - d),
    }
}

fn encode_time_tag(sec: u32, frac: u32) -> Vec<u8> {
    let mut bytes = vec![0u8; 8];
    BigEndian::write_u32(&mut bytes[..4], sec);
    BigEndian::write_u32(&mut bytes[4..], frac);
    bytes
}

#[test]
fn test_pad() {
    assert_eq!(4, pad(4));
    assert_eq!(8, pad(5));
    assert_eq!(8, pad(6));
    assert_eq!(8, pad(7));
}
<commit_msg>Fix extra char in MIDI type tag<commit_after>use errors::OscError;
use types::{OscBundle, OscMessage, OscPacket, OscType, Result};

use byteorder::{BigEndian, ByteOrder};

/// Takes a reference to an OSC packet and returns
/// a byte vector on success. If the packet was invalid
/// an `OscError` is returned.
///
/// # Example
///
/// ```
/// use rosc::{OscPacket,OscMessage,OscType};
/// use rosc::encoder;
///
/// let packet = OscPacket::Message(OscMessage{
///         addr: "/greet/me".to_string(),
///         args: Some(vec![OscType::String("hi!".to_string())])
///     }
/// );
/// assert!(encoder::encode(&packet).is_ok())
/// ```
pub fn encode(packet: &OscPacket) -> Result<Vec<u8>> {
    match *packet {
        OscPacket::Message(ref msg) => encode_message(msg),
        OscPacket::Bundle(ref bundle) => encode_bundle(bundle),
    }
}

fn encode_message(msg: &OscMessage) -> Result<Vec<u8>> {
    let mut msg_bytes: Vec<u8> = Vec::new();

    msg_bytes.extend(encode_string(msg.addr.clone()));
    let mut type_tags: Vec<char> = vec![','];
    let mut arg_bytes: Vec<u8> = Vec::new();

    if let Some(ref args) = msg.args {
        for arg in args {
            let (bytes, tags): (Option<Vec<u8>>, String) = encode_arg(arg)?;

            type_tags.extend(tags.chars());
            if bytes.is_some() {
                arg_bytes.extend(bytes.unwrap());
            }
        }
    }

    msg_bytes.extend(encode_string(type_tags.into_iter().collect::<String>()));
    if !arg_bytes.is_empty() {
        msg_bytes.extend(arg_bytes);
    }
    Ok(msg_bytes)
}

fn encode_bundle(bundle: &OscBundle) -> Result<Vec<u8>> {
    let mut bundle_bytes: Vec<u8> = Vec::new();
    bundle_bytes.extend(encode_string("#bundle".to_string()).into_iter());

    match encode_arg(&bundle.timetag)? {
        (Some(x), _) => {
            bundle_bytes.extend(x.into_iter());
        }
        (None, _) => {
            return Err(OscError::BadBundle("Missing time tag!".to_string()));
        }
    }

    if bundle.content.is_empty() {
        // TODO: A bundle of length zero, should this really be supported?
        bundle_bytes.extend([0u8; 4].into_iter());
        return Ok(bundle_bytes);
    }

    for packet in &bundle.content {
        match *packet {
            OscPacket::Message(ref m) => {
                let msg = encode_message(m)?;
                let mut msg_size = vec![0u8; 4];
                BigEndian::write_u32(&mut msg_size, msg.len() as u32);
                bundle_bytes.extend(msg_size.into_iter().chain(msg.into_iter()));
            }
            OscPacket::Bundle(ref b) => {
                let bdl = encode_bundle(b)?;
                let mut bdl_size = vec![0u8; 4];
                BigEndian::write_u32(&mut bdl_size, bdl.len() as u32);
                bundle_bytes.extend(bdl_size.into_iter().chain(bdl.into_iter()));
            }
        }
    }

    Ok(bundle_bytes)
}

fn encode_arg(arg: &OscType) -> Result<(Option<Vec<u8>>, String)> {
    match *arg {
        OscType::Int(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_i32(&mut bytes, *x);
            Ok((Some(bytes), "i".into()))
        }
        OscType::Long(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_i64(&mut bytes, *x);
            Ok((Some(bytes), "h".into()))
        }
        OscType::Float(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_f32(&mut bytes, *x);
            Ok((Some(bytes), "f".into()))
        }
        OscType::Double(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_f64(&mut bytes, *x);
            Ok((Some(bytes), "d".into()))
        }
        OscType::Char(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_u32(&mut bytes, *x as u32);
            Ok((Some(bytes), "c".into()))
        }
        OscType::String(ref x) => Ok((Some(encode_string(x.clone())), "s".into())),
        OscType::Blob(ref x) => {
            let padded_blob_length: usize = pad(x.len() as u64) as usize;
            let mut bytes = vec![0u8; 4 + padded_blob_length];
            // write length
            BigEndian::write_i32(&mut bytes[..4], x.len() as i32);
            for (i, v) in x.iter().enumerate() {
                bytes[i + 4] = *v;
            }
            Ok((Some(bytes), "b".into()))
        }
        OscType::Time(ref x, ref y) => Ok((Some(encode_time_tag(*x, *y)), "t".into())),
        OscType::Midi(ref x) => Ok((Some(vec![x.port, x.status, x.data1, x.data2]), "m".into())),
        OscType::Color(ref x) => Ok((Some(vec![x.red, x.green, x.blue, x.alpha]), "r".into())),
        OscType::Bool(ref x) => {
            if *x {
                Ok((None, "T".into()))
            } else {
                Ok((None, "F".into()))
            }
        }
        OscType::Nil => Ok((None, "N".into())),
        OscType::Inf => Ok((None, "I".into())),
        OscType::Array(ref x) => {
            let mut bytes = vec![0u8; 0];
            let mut type_tags = String::from("[");
            for v in x.iter() {
                match encode_arg(v) {
                    Ok((Some(other_bytes), other_type_tags)) => {
                        bytes.extend(other_bytes);
                        type_tags.push_str(&other_type_tags);
                    }
                    Ok((None, other_type_tags)) => {
                        type_tags.push_str(&other_type_tags);
                    }
                    Err(err) => return Err(err),
                }
            }
            type_tags.push_str("]");
            Ok((Some(bytes), type_tags))
        }
    }
}

/// Null terminates the byte representation of string `s` and
/// adds null bytes until the length of the result is a
/// multiple of 4.
pub fn encode_string<S: Into<String>>(s: S) -> Vec<u8> {
    let mut bytes: Vec<u8> = s.into().as_bytes().into();
    bytes.push(0u8);
    pad_bytes(&mut bytes);
    bytes
}

fn pad_bytes(bytes: &mut Vec<u8>) {
    let padded_lengh = pad(bytes.len() as u64);
    while bytes.len() < padded_lengh as usize {
        bytes.push(0u8)
    }
}

/// Returns the position padded to 4 bytes.
///
/// # Example
///
/// ```
/// use rosc::encoder;
///
/// let pos: u64 = 10;
/// assert_eq!(12u64, encoder::pad(pos))
/// ```
pub fn pad(pos: u64) -> u64 {
    match pos % 4 {
        0 => pos,
        d => pos + (4 - d),
    }
}

fn encode_time_tag(sec: u32, frac: u32) -> Vec<u8> {
    let mut bytes = vec![0u8; 8];
    BigEndian::write_u32(&mut bytes[..4], sec);
    BigEndian::write_u32(&mut bytes[4..], frac);
    bytes
}

#[test]
fn test_pad() {
    assert_eq!(4, pad(4));
    assert_eq!(8, pad(5));
    assert_eq!(8, pad(6));
    assert_eq!(8, pad(7));
}
<|endoftext|>


<commit_before>use std::cmp;
use std::io::BufReader;
use std::fs::File;
use std::path::Path;

use cgmath::{self, Transform as Transform_};
use genmesh::{Triangulate, Vertex as GenVertex};
use genmesh::generators::{self, IndexedPolygon, SharedVertex};
use gfx;
use gfx::format::I8Norm;
use gfx::handle as h;
use gfx::traits::{Factory as Factory_, FactoryExt};
use image;

use render::{BackendFactory, BackendResources, ConstantBuffer, GpuData, Vertex, ShadowFormat};
use scene::{Color, Background, Group, Mesh, Sprite, Material,
            AmbientLight, DirectionalLight, HemisphereLight, PointLight};
use {Hub, HubPtr, SubLight, Node, SubNode, Normal, Position, Transform,
     VisualData, LightData, Object, VisualObject, LightObject, Scene,
     Camera, OrthographicCamera, PerspectiveCamera};

const NORMAL_Z: [I8Norm; 4] = [I8Norm(0), I8Norm(0), I8Norm(1), I8Norm(0)];

const QUAD: [Vertex; 4] = [
    Vertex {
        pos: [-1.0, -1.0, 0.0, 1.0],
        uv: [0.0, 0.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [1.0, -1.0, 0.0, 1.0],
        uv: [1.0, 0.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [-1.0, 1.0, 0.0, 1.0],
        uv: [0.0, 1.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [1.0, 1.0, 0.0, 1.0],
        uv: [1.0, 1.0],
        normal: NORMAL_Z,
    },
];

impl From<SubNode> for Node {
    fn from(sub: SubNode) -> Self {
        Node {
            visible: true,
            world_visible: false,
            transform: Transform::one(),
            world_transform: Transform::one(),
            parent: None,
            scene_id: None,
            sub_node: sub,
        }
    }
}

impl Hub {
    fn spawn(&mut self) -> Object {
        Object {
            visible: true,
            transform: Transform::one(),
            node: self.nodes.create(SubNode::Empty.into()),
            tx: self.message_tx.clone(),
        }
    }

    fn spawn_visual(&mut self, data: VisualData<ConstantBuffer>)
                    -> VisualObject
    {
        VisualObject {
            data: data.drop_payload(),
            inner: Object {
                visible: true,
                transform: Transform::one(),
                node: self.nodes.create(SubNode::Visual(data).into()),
                tx: self.message_tx.clone(),
            },
        }
    }

    fn spawn_light(&mut self, data: LightData) -> LightObject {
        LightObject {
            inner: Object {
                visible: true,
                transform: Transform::one(),
                node: self.nodes.create(SubNode::Light(data.clone()).into()),
                tx: self.message_tx.clone(),
            },
            data,
        }
    }
}

#[derive(Clone)]
pub struct ShadowMap {
    resource: gfx::handle::ShaderResourceView<BackendResources, f32>,
    target: gfx::handle::DepthStencilView<BackendResources, ShadowFormat>,
}

impl ShadowMap {
    #[doc(hidden)]
    pub fn to_target(&self) -> gfx::handle::DepthStencilView<BackendResources, ShadowFormat> {
        self.target.clone()
    }

    #[doc(hidden)]
    pub fn to_resource(&self) -> gfx::handle::ShaderResourceView<BackendResources, f32> {
        self.resource.clone()
    }
}

pub type SceneId = usize;

pub struct Factory {
    backend: BackendFactory,
    scene_id: SceneId,
    hub: HubPtr,
    quad: GpuData,
}

impl Factory {
    #[doc(hidden)]
    pub fn new(mut backend: BackendFactory) -> Self {
        let (vbuf, slice) = backend.create_vertex_buffer_with_slice(&QUAD, ());
        Factory {
            backend: backend,
            scene_id: 0,
            hub: Hub::new(),
            quad: GpuData {
                slice: slice,
                vertices: vbuf,
            },
        }
    }

    pub fn scene(&mut self) -> Scene {
        self.scene_id += 1;
        let mut hub = self.hub.lock().unwrap();
        let node = hub.nodes.create(Node {
            scene_id: Some(self.scene_id),
            .. SubNode::Empty.into()
        });
        Scene {
            unique_id: self.scene_id,
            node: node,
            tx: hub.message_tx.clone(),
            hub: self.hub.clone(),
            background: Background::Color(0),
        }
    }

    pub fn orthographic_camera(&mut self, left: f32, right: f32, top: f32, bottom: f32,
                               near: f32, far: f32) -> OrthographicCamera {
        Camera {
            object: self.hub.lock().unwrap().spawn(),
            projection: cgmath::Ortho{ left, right, bottom, top, near, far },
        }
    }

    pub fn perspective_camera(&mut self, fov: f32, aspect: f32,
                              near: f32, far: f32) -> PerspectiveCamera {
        Camera {
            object: self.hub.lock().unwrap().spawn(),
            projection: cgmath::PerspectiveFov {
                fovy: cgmath::Deg(fov).into(),
                aspect: aspect,
                near: near,
                far: far,
            },
        }
    }

    pub fn group(&mut self) -> Group {
        Group::new(self.hub.lock().unwrap().spawn())
    }

    pub fn mesh(&mut self, geom: Geometry, mat: Material) -> Mesh {
        let vertices: Vec<_> = if geom.normals.is_empty() {
            geom.vertices.iter().map(|v| Vertex {
                pos: [v.x, v.y, v.z, 1.0],
                uv: [0.0, 0.0], //TODO
                normal: NORMAL_Z,
            }).collect()
        } else {
            let f2i = |x: f32| I8Norm(cmp::min(cmp::max((x * 127.) as isize, -128), 127) as i8);
            geom.vertices.iter().zip(geom.normals.iter()).map(|(v, n)| Vertex {
                pos: [v.x, v.y, v.z, 1.0],
                uv: [0.0, 0.0], //TODO
                normal: [f2i(n.x), f2i(n.y), f2i(n.z), I8Norm(0)],
            }).collect()
        };
        //TODO: dynamic geometry
        let cbuf = self.backend.create_constant_buffer(1);
        let (vbuf, slice) = if geom.faces.is_empty() {
            self.backend.create_vertex_buffer_with_slice(&vertices, ())
        } else {
            let faces: &[u16] = gfx::memory::cast_slice(&geom.faces);
            self.backend.create_vertex_buffer_with_slice(&vertices, faces)
        };
        Mesh::new(self.hub.lock().unwrap().spawn_visual(VisualData {
            material: mat,
            payload: cbuf,
            gpu_data: GpuData {
                slice: slice,
                vertices: vbuf,
            },
        }))
    }

    pub fn sprite(&mut self, mat: Material) -> Sprite {
        let cbuf = self.backend.create_constant_buffer(1);
        Sprite::new(self.hub.lock().unwrap().spawn_visual(VisualData {
            material: mat,
            payload: cbuf,
            gpu_data: self.quad.clone(),
        }))
    }

    pub fn ambient_light(&mut self, color: Color, intensity: f32) -> AmbientLight {
        AmbientLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Ambient,
            shadow: None,
        }))
    }

    pub fn directional_light(&mut self, color: Color, intensity: f32) -> DirectionalLight {
        DirectionalLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Directional,
            shadow: None,
        }))
    }

    pub fn hemisphere_light(&mut self, sky_color: Color, ground_color: Color,
                            intensity: f32) -> HemisphereLight {
        HemisphereLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color: sky_color,
            intensity,
            sub_light: SubLight::Hemisphere{ ground: ground_color },
            shadow: None,
        }))
    }

    pub fn point_light(&mut self, color: Color, intensity: f32) -> PointLight {
        PointLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Point,
            shadow: None,
        }))
    }

    pub fn shadow_map(&mut self, width: u16, height: u16) -> ShadowMap {
        let (_, resource, target) = self.backend.create_depth_stencil::<ShadowFormat>(
            width, height).unwrap();
        ShadowMap {
            resource,
            target,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Geometry {
    pub vertices: Vec<Position>,
    pub normals: Vec<Normal>,
    pub faces: Vec<[u16; 3]>,
    pub is_dynamic: bool,
}

impl Geometry {
    pub fn empty() -> Geometry {
        Geometry {
            vertices: Vec::new(),
            normals: Vec::new(),
            faces: Vec::new(),
            is_dynamic: false,
        }
    }

    pub fn from_vertices(verts: Vec<Position>) -> Geometry {
        Geometry {
            vertices: verts,
            .. Geometry::empty()
        }
    }

    pub fn new_box(sx: f32, sy: f32, sz: f32) -> Self {
        let gen = generators::Cube::new();
        let function = |GenVertex{ pos, ..}| {
            Position::new(pos[0] * 0.5 * sx, pos[1] * 0.5 * sy, pos[2] * 0.5 * sz)
        };
        Geometry {
            vertices: gen.shared_vertex_iter()
                         .map(function)
                         .collect(),
            normals: gen.shared_vertex_iter()
                        .map(|v| Normal::from(v.normal))
                        .collect(),
            faces: gen.indexed_polygon_iter()
                       .triangulate()
                       .map(|t| [t.x as u16, t.y as u16, t.z as u16])
                       .collect(),
            is_dynamic: false,
        }
    }

    pub fn new_cylinder(radius_top: f32, radius_bottom: f32, height: f32,
                        radius_segments: usize) -> Self
    {
        let gen = generators::Cylinder::new(radius_segments);
        //Three.js has height along the Y axis for some reason
        let f_pos = |GenVertex{ pos, ..}| {
            let scale = (pos[2] + 1.0) * 0.5 * radius_top +
                        (1.0 - pos[2]) * 0.5 * radius_bottom;
            Position::new(pos[1] * scale, pos[2] * 0.5 * height, pos[0] * scale)
        };
        let f_normal = |GenVertex{ normal, ..}| {
            Normal::from([normal[1], normal[2], normal[0]])
        };
        Geometry {
            vertices: gen.shared_vertex_iter()
                         .map(f_pos)
                         .collect(),
            normals: gen.shared_vertex_iter()
                        .map(f_normal)
                        .collect(),
            faces: gen.indexed_polygon_iter()
                       .triangulate()
                       .map(|t| [t.x as u16, t.y as u16, t.z as u16])
                       .collect(),
            is_dynamic: false,
        }
    }

    pub fn new_sphere(radius: f32, width_segments: usize,
                      height_segments: usize) -> Self
    {
        let gen = generators::SphereUV::new(width_segments, height_segments);
        let function = |GenVertex{ pos, ..}| {
            Position::new(pos[0] * radius, pos[1] * radius, pos[2] * radius)
        };
        Geometry {
            vertices: gen.shared_vertex_iter()
                         .map(function)
                         .collect(),
            normals: gen.shared_vertex_iter()
                        .map(|v| Normal::from(v.normal))
                        .collect(),
            faces: gen.indexed_polygon_iter()
                       .triangulate()
                       .map(|t| [t.x as u16, t.y as u16, t.z as u16])
                       .collect(),
            is_dynamic: false,
        }
    }
}

#[derive(Clone)]
pub struct Texture<T> {
    view: h::ShaderResourceView<BackendResources, T>,
    sampler: h::Sampler<BackendResources>,
}

impl<T> Texture<T> {
    #[doc(hidden)]
    pub fn new(view: h::ShaderResourceView<BackendResources, T>,
               sampler: h::Sampler<BackendResources>) -> Self {
        Texture {
            view,
            sampler,
        }
    }

    #[doc(hidden)]
    pub fn to_param(&self) -> (h::ShaderResourceView<BackendResources, T>, h::Sampler<BackendResources>) {
        (self.view.clone(), self.sampler.clone())
    }
}

impl Factory {
    pub fn load_texture(&mut self, path_str: &str) -> Texture<[f32; 4]> {
        use gfx::texture as t;
        use image::ImageFormat as F;

        let path = Path::new(path_str);
        let extension = path.extension()
                            .expect("no extension for an image?")
                            .to_string_lossy()
                            .to_lowercase();
        let format = match extension.as_str() {
            "png" => F::PNG,
            "jpg" | "jpeg" => F::JPEG,
            "gif" => F::GIF,
            "webp" => F::WEBP,
            "ppm" => F::PPM,
            "tiff" => F::TIFF,
            "tga" => F::TGA,
            "bmp" => F::BMP,
            "ico" => F::ICO,
            "hdr" => F::HDR,
            _ => panic!("Unrecognized image extension: {}", extension),
        };

        let file = File::open(&path)
                        .unwrap_or_else(|e| panic!("Unable to open {}: {:?}", path_str, e));
        let img = image::load(BufReader::new(file), format)
                        .unwrap_or_else(|e| panic!("Unable to decode {}: {:?}", path_str, e))
                        .flipv().to_rgba();
        let (width, height) = img.dimensions();
        let kind = t::Kind::D2(width as t::Size, height as t::Size, t::AaMode::Single);
        let (_, view) = self.backend.create_texture_immutable_u8::<gfx::format::Srgba8>(kind, &[&img])
                                    .unwrap_or_else(|e| panic!("Unable to create GPU texture for {}: {:?}", path_str, e));

        Texture {
            view,
            sampler: self.backend.create_sampler_linear(),
        }
    }
}
<commit_msg>Generators refactor<commit_after>use std::cmp;
use std::io::BufReader;
use std::fs::File;
use std::path::Path;

use cgmath::{self, Transform as Transform_};
use genmesh::{EmitTriangles, Triangulate, Vertex as GenVertex};
use genmesh::generators::{self, IndexedPolygon, SharedVertex};
use gfx;
use gfx::format::I8Norm;
use gfx::handle as h;
use gfx::traits::{Factory as Factory_, FactoryExt};
use image;

use render::{BackendFactory, BackendResources, ConstantBuffer, GpuData, Vertex, ShadowFormat};
use scene::{Color, Background, Group, Mesh, Sprite, Material,
            AmbientLight, DirectionalLight, HemisphereLight, PointLight};
use {Hub, HubPtr, SubLight, Node, SubNode, Normal, Position, Transform,
     VisualData, LightData, Object, VisualObject, LightObject, Scene,
     Camera, OrthographicCamera, PerspectiveCamera};

const NORMAL_Z: [I8Norm; 4] = [I8Norm(0), I8Norm(0), I8Norm(1), I8Norm(0)];

const QUAD: [Vertex; 4] = [
    Vertex {
        pos: [-1.0, -1.0, 0.0, 1.0],
        uv: [0.0, 0.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [1.0, -1.0, 0.0, 1.0],
        uv: [1.0, 0.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [-1.0, 1.0, 0.0, 1.0],
        uv: [0.0, 1.0],
        normal: NORMAL_Z,
    },
    Vertex {
        pos: [1.0, 1.0, 0.0, 1.0],
        uv: [1.0, 1.0],
        normal: NORMAL_Z,
    },
];

impl From<SubNode> for Node {
    fn from(sub: SubNode) -> Self {
        Node {
            visible: true,
            world_visible: false,
            transform: Transform::one(),
            world_transform: Transform::one(),
            parent: None,
            scene_id: None,
            sub_node: sub,
        }
    }
}

impl Hub {
    fn spawn(&mut self) -> Object {
        Object {
            visible: true,
            transform: Transform::one(),
            node: self.nodes.create(SubNode::Empty.into()),
            tx: self.message_tx.clone(),
        }
    }

    fn spawn_visual(&mut self, data: VisualData<ConstantBuffer>)
                    -> VisualObject
    {
        VisualObject {
            data: data.drop_payload(),
            inner: Object {
                visible: true,
                transform: Transform::one(),
                node: self.nodes.create(SubNode::Visual(data).into()),
                tx: self.message_tx.clone(),
            },
        }
    }

    fn spawn_light(&mut self, data: LightData) -> LightObject {
        LightObject {
            inner: Object {
                visible: true,
                transform: Transform::one(),
                node: self.nodes.create(SubNode::Light(data.clone()).into()),
                tx: self.message_tx.clone(),
            },
            data,
        }
    }
}

#[derive(Clone)]
pub struct ShadowMap {
    resource: gfx::handle::ShaderResourceView<BackendResources, f32>,
    target: gfx::handle::DepthStencilView<BackendResources, ShadowFormat>,
}

impl ShadowMap {
    #[doc(hidden)]
    pub fn to_target(&self) -> gfx::handle::DepthStencilView<BackendResources, ShadowFormat> {
        self.target.clone()
    }

    #[doc(hidden)]
    pub fn to_resource(&self) -> gfx::handle::ShaderResourceView<BackendResources, f32> {
        self.resource.clone()
    }
}

pub type SceneId = usize;

pub struct Factory {
    backend: BackendFactory,
    scene_id: SceneId,
    hub: HubPtr,
    quad: GpuData,
}

impl Factory {
    #[doc(hidden)]
    pub fn new(mut backend: BackendFactory) -> Self {
        let (vbuf, slice) = backend.create_vertex_buffer_with_slice(&QUAD, ());
        Factory {
            backend: backend,
            scene_id: 0,
            hub: Hub::new(),
            quad: GpuData {
                slice: slice,
                vertices: vbuf,
            },
        }
    }

    pub fn scene(&mut self) -> Scene {
        self.scene_id += 1;
        let mut hub = self.hub.lock().unwrap();
        let node = hub.nodes.create(Node {
            scene_id: Some(self.scene_id),
            .. SubNode::Empty.into()
        });
        Scene {
            unique_id: self.scene_id,
            node: node,
            tx: hub.message_tx.clone(),
            hub: self.hub.clone(),
            background: Background::Color(0),
        }
    }

    pub fn orthographic_camera(&mut self, left: f32, right: f32, top: f32, bottom: f32,
                               near: f32, far: f32) -> OrthographicCamera {
        Camera {
            object: self.hub.lock().unwrap().spawn(),
            projection: cgmath::Ortho{ left, right, bottom, top, near, far },
        }
    }

    pub fn perspective_camera(&mut self, fov: f32, aspect: f32,
                              near: f32, far: f32) -> PerspectiveCamera {
        Camera {
            object: self.hub.lock().unwrap().spawn(),
            projection: cgmath::PerspectiveFov {
                fovy: cgmath::Deg(fov).into(),
                aspect: aspect,
                near: near,
                far: far,
            },
        }
    }

    pub fn group(&mut self) -> Group {
        Group::new(self.hub.lock().unwrap().spawn())
    }

    pub fn mesh(&mut self, geom: Geometry, mat: Material) -> Mesh {
        let vertices: Vec<_> = if geom.normals.is_empty() {
            geom.vertices.iter().map(|v| Vertex {
                pos: [v.x, v.y, v.z, 1.0],
                uv: [0.0, 0.0], //TODO
                normal: NORMAL_Z,
            }).collect()
        } else {
            let f2i = |x: f32| I8Norm(cmp::min(cmp::max((x * 127.) as isize, -128), 127) as i8);
            geom.vertices.iter().zip(geom.normals.iter()).map(|(v, n)| Vertex {
                pos: [v.x, v.y, v.z, 1.0],
                uv: [0.0, 0.0], //TODO
                normal: [f2i(n.x), f2i(n.y), f2i(n.z), I8Norm(0)],
            }).collect()
        };
        //TODO: dynamic geometry
        let cbuf = self.backend.create_constant_buffer(1);
        let (vbuf, slice) = if geom.faces.is_empty() {
            self.backend.create_vertex_buffer_with_slice(&vertices, ())
        } else {
            let faces: &[u16] = gfx::memory::cast_slice(&geom.faces);
            self.backend.create_vertex_buffer_with_slice(&vertices, faces)
        };
        Mesh::new(self.hub.lock().unwrap().spawn_visual(VisualData {
            material: mat,
            payload: cbuf,
            gpu_data: GpuData {
                slice: slice,
                vertices: vbuf,
            },
        }))
    }

    pub fn sprite(&mut self, mat: Material) -> Sprite {
        let cbuf = self.backend.create_constant_buffer(1);
        Sprite::new(self.hub.lock().unwrap().spawn_visual(VisualData {
            material: mat,
            payload: cbuf,
            gpu_data: self.quad.clone(),
        }))
    }

    pub fn ambient_light(&mut self, color: Color, intensity: f32) -> AmbientLight {
        AmbientLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Ambient,
            shadow: None,
        }))
    }

    pub fn directional_light(&mut self, color: Color, intensity: f32) -> DirectionalLight {
        DirectionalLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Directional,
            shadow: None,
        }))
    }

    pub fn hemisphere_light(&mut self, sky_color: Color, ground_color: Color,
                            intensity: f32) -> HemisphereLight {
        HemisphereLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color: sky_color,
            intensity,
            sub_light: SubLight::Hemisphere{ ground: ground_color },
            shadow: None,
        }))
    }

    pub fn point_light(&mut self, color: Color, intensity: f32) -> PointLight {
        PointLight::new(self.hub.lock().unwrap().spawn_light(LightData {
            color,
            intensity,
            sub_light: SubLight::Point,
            shadow: None,
        }))
    }

    pub fn shadow_map(&mut self, width: u16, height: u16) -> ShadowMap {
        let (_, resource, target) = self.backend.create_depth_stencil::<ShadowFormat>(
            width, height).unwrap();
        ShadowMap {
            resource,
            target,
        }
    }
}

#[derive(Clone, Debug)]
pub struct Geometry {
    pub vertices: Vec<Position>,
    pub normals: Vec<Normal>,
    pub faces: Vec<[u16; 3]>,
    pub is_dynamic: bool,
}

impl Geometry {
    pub fn empty() -> Geometry {
        Geometry {
            vertices: Vec::new(),
            normals: Vec::new(),
            faces: Vec::new(),
            is_dynamic: false,
        }
    }

    pub fn from_vertices(verts: Vec<Position>) -> Geometry {
        Geometry {
            vertices: verts,
            .. Geometry::empty()
        }
    }

    fn generate<P, G, Fpos, Fnor>(gen: G, fpos: Fpos, fnor: Fnor) -> Self where
        P: EmitTriangles<Vertex=usize>,
        G: IndexedPolygon<P> + SharedVertex<GenVertex>,
        Fpos: Fn(GenVertex) -> Position,
        Fnor: Fn(GenVertex) -> Normal,
    {
        Geometry {
            vertices: gen.shared_vertex_iter()
                         .map(fpos)
                         .collect(),
            normals: gen.shared_vertex_iter()
                        .map(fnor)
                        .collect(),
            faces: gen.indexed_polygon_iter()
                       .triangulate()
                       .map(|t| [t.x as u16, t.y as u16, t.z as u16])
                       .collect(),
            is_dynamic: false,
        }
    }

    pub fn new_plane(sx: f32, sy: f32) -> Self {
        Self::generate(generators::Plane::new(),
            |GenVertex{ pos, ..}| {
                Position::new(pos[0] * 0.5 * sx, pos[1] * 0.5 * sy, 0.0)
            },
            |v| Normal::from(v.normal)
        )
    }

    pub fn new_box(sx: f32, sy: f32, sz: f32) -> Self {
        Self::generate(generators::Cube::new(),
            |GenVertex{ pos, ..}| {
                Position::new(pos[0] * 0.5 * sx, pos[1] * 0.5 * sy, pos[2] * 0.5 * sz)
            },
            |v| Normal::from(v.normal)
        )
    }

    pub fn new_cylinder(radius_top: f32, radius_bottom: f32, height: f32,
                        radius_segments: usize) -> Self
    {
        Self::generate(generators::Cylinder::new(radius_segments),
            //Three.js has height along the Y axis for some reason
            |GenVertex{ pos, ..}| {
                let scale = (pos[2] + 1.0) * 0.5 * radius_top +
                            (1.0 - pos[2]) * 0.5 * radius_bottom;
                Position::new(pos[1] * scale, pos[2] * 0.5 * height, pos[0] * scale)
            },
            |GenVertex{ normal, ..}| {
                Normal::from([normal[1], normal[2], normal[0]])
            },
        )
    }

    pub fn new_sphere(radius: f32, width_segments: usize,
                      height_segments: usize) -> Self
    {
        Self::generate(generators::SphereUV::new(width_segments, height_segments),
            |GenVertex{ pos, ..}| {
                Position::new(pos[0] * radius, pos[1] * radius, pos[2] * radius)
            },
            |v| Normal::from(v.normal)
        )
    }
}

#[derive(Clone)]
pub struct Texture<T> {
    view: h::ShaderResourceView<BackendResources, T>,
    sampler: h::Sampler<BackendResources>,
}

impl<T> Texture<T> {
    #[doc(hidden)]
    pub fn new(view: h::ShaderResourceView<BackendResources, T>,
               sampler: h::Sampler<BackendResources>) -> Self {
        Texture {
            view,
            sampler,
        }
    }

    #[doc(hidden)]
    pub fn to_param(&self) -> (h::ShaderResourceView<BackendResources, T>, h::Sampler<BackendResources>) {
        (self.view.clone(), self.sampler.clone())
    }
}

impl Factory {
    pub fn load_texture(&mut self, path_str: &str) -> Texture<[f32; 4]> {
        use gfx::texture as t;
        use image::ImageFormat as F;

        let path = Path::new(path_str);
        let extension = path.extension()
                            .expect("no extension for an image?")
                            .to_string_lossy()
                            .to_lowercase();
        let format = match extension.as_str() {
            "png" => F::PNG,
            "jpg" | "jpeg" => F::JPEG,
            "gif" => F::GIF,
            "webp" => F::WEBP,
            "ppm" => F::PPM,
            "tiff" => F::TIFF,
            "tga" => F::TGA,
            "bmp" => F::BMP,
            "ico" => F::ICO,
            "hdr" => F::HDR,
            _ => panic!("Unrecognized image extension: {}", extension),
        };

        let file = File::open(&path)
                        .unwrap_or_else(|e| panic!("Unable to open {}: {:?}", path_str, e));
        let img = image::load(BufReader::new(file), format)
                        .unwrap_or_else(|e| panic!("Unable to decode {}: {:?}", path_str, e))
                        .flipv().to_rgba();
        let (width, height) = img.dimensions();
        let kind = t::Kind::D2(width as t::Size, height as t::Size, t::AaMode::Single);
        let (_, view) = self.backend.create_texture_immutable_u8::<gfx::format::Srgba8>(kind, &[&img])
                                    .unwrap_or_else(|e| panic!("Unable to create GPU texture for {}: {:?}", path_str, e));

        Texture {
            view,
            sampler: self.backend.create_sampler_linear(),
        }
    }
}
<|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(&file_map, mode).ok();
    for filename in file_map.keys() {
        try!(write_file(&file_map[filename], filename, mode, config));
    }
    output_trailing(&file_map, mode).ok();

    Ok(())
}

pub fn output_heading(file_map: &FileMap, 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(file_map: &FileMap, 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) => {
                    try!(write!(writer,
                                "<error line=\"{}\" severity=\"error\" message=\"Should be \
                                 `{}`\" />",
                                mismatch.line_number,
                                str));
                }
                _ => {
                    // Do nothing with context and expected.
                }
            }
        }
    }
    try!(write!(writer, "</file>"));
    Ok(())
}

// 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>Remove unused argument.<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) => {
                    try!(write!(writer,
                                "<error line=\"{}\" severity=\"error\" message=\"Should be \
                                 `{}`\" />",
                                mismatch.line_number,
                                str));
                }
                _ => {
                    // Do nothing with context and expected.
                }
            }
        }
    }
    try!(write!(writer, "</file>"));
    Ok(())
}

// 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)
}
<|endoftext|>


<commit_before>// Copyright (C) 2015, Alberto Corona <alberto@0x1a.us>
// All rights reserved. This file is part of rpf, distributed under the
// BSD 3-Clause license. For full terms please see the LICENSE file.

pub use utils::prog::{Prog};
pub use utils::as_path::*;
pub use utils::status::{Status};
pub use utils::styled::*;
pub use utils::pathmod::{PathMod};

pub mod as_path;
pub mod prog;
pub mod pathmod;
pub mod status;
pub mod styled;
pub mod test;
<commit_msg>mod: reflect changes done to `Status`<commit_after>// Copyright (C) 2015, Alberto Corona <alberto@0x1a.us>
// All rights reserved. This file is part of rpf, distributed under the
// BSD 3-Clause license. For full terms please see the LICENSE file.

pub use utils::prog::{Prog};
pub use utils::as_path::*;
pub use utils::status::{Exit,ExitStatus};
pub use utils::styled::*;
pub use utils::pathmod::{PathMod};

pub mod as_path;
pub mod prog;
pub mod pathmod;
pub mod status;
pub mod styled;
pub mod test;
<|endoftext|>


<commit_before>use crate::Version;
use clap::{Arg, ArgMatches, Command};
use console::{Emoji, Style};
use convert_case::{Case, Casing};
use ditto_config::{self as config, PackageName};
use miette::{bail, miette, IntoDiagnostic, Result, WrapErr};
use std::{
    env::current_exe,
    fs,
    path::{Path, PathBuf},
    process,
};

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name)
        .about("Bootstrap a new project")
        .arg(
            Arg::new("name")
                .long("name")
                .takes_value(true)
                .validator_regex(config::PACKAGE_NAME_REGEX.clone(), "Bad package name")
                .help("Optional package name (defaults to DIR)"),
        )
        .arg(
            Arg::new("javascript")
                .long("js")
                .help("JavaScript project?"),
        )
        .arg(
            Arg::new("directory")
                .id("DIR")
                .takes_value(true)
                .required(true)
                .help("Directory for the project"),
        )
}

pub fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    let project_dir = matches.value_of("DIR").unwrap();
    let package_name = PackageName::new_unchecked(
        matches
            .value_of("name")
            .map_or_else(
                || {
                    if !config::PACKAGE_NAME_REGEX.is_match(project_dir) {
                        bail!("If `--name` isn't specified, DIR must be a valid package name")
                    }
                    Ok(project_dir)
                },
                Ok,
            )?
            .to_owned(),
    );

    let project_dir = PathBuf::from(project_dir);

    if project_dir.exists() {
        return Err(miette!(
            "path {:?} already exists",
            project_dir.to_string_lossy()
        ));
    }

    println!("Writing files...");
    fs::create_dir_all(&project_dir)
        .into_diagnostic()
        .wrap_err(format!(
            "error creating new project directory {:?}",
            project_dir.to_string_lossy()
        ))?;

    let flavour = get_flavour(matches)?;

    write_files(package_name, &project_dir, ditto_version, &flavour)?;

    // Run an initial `ditto make` in the new directory to kick things off
    if let Ok(ditto) = current_exe() {
        println!("\nRunning `ditto make`...");
        process::Command::new(ditto)
            .arg("make")
            .current_dir(&project_dir)
            .status()
            .into_diagnostic()
            .wrap_err("error running `make` in new project directory")?;
    }

    Ok(())
}

enum Flavour {
    Bland,
    JavaScript, // `--javascript`
}

fn get_flavour(matches: &ArgMatches) -> Result<Flavour> {
    if matches.is_present("javascript") {
        return Ok(Flavour::JavaScript);
    }
    Ok(Flavour::Bland)
}

fn write_files(
    package_name: PackageName,
    project_dir: &Path,
    ditto_version: &Version,
    flavour: &Flavour,
) -> Result<()> {
    let config = write_new_config(package_name, project_dir, ditto_version, flavour)?;
    write_empty_ditto_module(&config, project_dir)?;
    write_new_gitignore(&config, project_dir, flavour)?;
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            write_js_files(&config, project_dir)?;
        }
    }
    Ok(())
}

fn write_js_files(config: &config::Config, project_dir: &Path) -> Result<()> {
    write_package_json(config, project_dir)
}

fn write_package_json(config: &config::Config, project_dir: &Path) -> Result<()> {
    let mut path = project_dir.to_path_buf();
    path.push("package");
    path.set_extension("json");
    let file = fs::File::create(&path).into_diagnostic().wrap_err(format!(
        "error creating package.json file at {:?}",
        path.to_string_lossy()
    ))?;
    let workspaces = vec![format!(
        "{}/*",
        config.codegen_js_config.packages_dir.to_string_lossy()
    )];
    let value = serde_json::json!({
        "private": true,
        "type": "module",
        "workspaces": workspaces,
    });
    serde_json::to_writer_pretty(file, &value)
        .into_diagnostic()
        .wrap_err(format!(
            "error writing package.json file to {:?}",
            path.to_string_lossy()
        ))?;
    log_path_written(path);
    Ok(())
}

fn write_new_config(
    package_name: PackageName,
    project_dir: &Path,
    ditto_version: &Version,
    flavour: &Flavour,
) -> Result<config::Config> {
    let mut config = config::Config::new(package_name);
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            config.targets =
                std::collections::HashSet::from([config::Target::Web, config::Target::Nodejs]);
        }
    }
    let mut config_path = project_dir.to_path_buf();
    config_path.push(config::CONFIG_FILE_NAME);
    let config_string = toml::to_string(&config)
        .into_diagnostic()
        .wrap_err("error serializing new config file")?;

    let preamble = format!(
        "# Welcome to your new ditto project!
#
# Options for this file can be found at:
# https://github.com/ditto-lang/ditto/tree/{rev}/crates/ditto-config#readme",
        rev = ditto_version.git_rev
    );

    fs::write(&config_path, format!("{}\n{}", preamble, config_string))
        .into_diagnostic()
        .wrap_err(format!(
            "error writing new config file to {:?}",
            config_path.to_string_lossy()
        ))?;

    log_path_written(&config_path);
    Ok(config)
}

fn write_new_gitignore(
    config: &config::Config,
    project_dir: &Path,
    flavour: &Flavour,
) -> Result<()> {
    let mut path = project_dir.to_path_buf();
    path.push(".gitignore");

    let mut ignore_lines = vec![
        // .ditto
        config.ditto_dir.to_string_lossy().into_owned(),
    ];
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            // dist
            ignore_lines.push(
                config
                    .codegen_js_config
                    .dist_dir
                    .to_string_lossy()
                    .into_owned(),
            );
            // node_modules
            ignore_lines.push(String::from("node_modules"));
        }
    }

    fs::write(&path, ignore_lines.join("\n"))
        .into_diagnostic()
        .wrap_err(format!(
            "error writing .gitignore to {:?}",
            path.to_string_lossy()
        ))?;

    log_path_written(&path);
    Ok(())
}

fn write_empty_ditto_module(config: &config::Config, project_dir: &Path) -> Result<()> {
    let mut module_path = project_dir.to_path_buf();
    module_path.push(&config.src_dir);
    fs::create_dir_all(&module_path)
        .into_diagnostic()
        .wrap_err(format!(
            "error creating ditto source directory {:?}",
            module_path.to_string_lossy()
        ))?;
    let module_name = config.name.to_case(Case::Pascal);
    module_path.push(&module_name);
    module_path.set_extension("ditto");

    let module_contents = format!("module {} exports (..);", module_name);
    write_ditto_module(module_path, module_contents)
}

fn write_ditto_module<P: AsRef<Path>>(path: P, contents: String) -> Result<()> {
    let module = ditto_cst::Module::parse(&contents).map_err(|_| {
        miette!(
            "Internal error: couldn't parse generated module: {:?}",
            contents
        )
    })?;
    let formatted = ditto_fmt::format_module(module);
    fs::write(&path, formatted)
        .into_diagnostic()
        .wrap_err(format!(
            "error writing ditto module to {}",
            path.as_ref().to_string_lossy()
        ))?;
    log_path_written(path);
    Ok(())
}

fn log_path_written<P: AsRef<Path>>(path: P) {
    let message = format!(
        "  {} {}",
        Emoji("✨", "Wrote"),
        path.as_ref().to_string_lossy()
    );
    println!("{}", Style::new().cyan().apply_to(message));
}
<commit_msg>Respect DITTO_PLAIN in `ditto bootstrap`<commit_after>use crate::Version;
use clap::{Arg, ArgMatches, Command};
use console::{Emoji, Style};
use convert_case::{Case, Casing};
use ditto_config::{self as config, PackageName};
use miette::{bail, miette, IntoDiagnostic, Result, WrapErr};
use std::{
    env::current_exe,
    fs,
    path::{Path, PathBuf},
    process,
};

pub fn command<'a>(name: &str) -> Command<'a> {
    Command::new(name)
        .about("Bootstrap a new project")
        .arg(
            Arg::new("name")
                .long("name")
                .takes_value(true)
                .validator_regex(config::PACKAGE_NAME_REGEX.clone(), "Bad package name")
                .help("Optional package name (defaults to DIR)"),
        )
        .arg(
            Arg::new("javascript")
                .long("js")
                .help("JavaScript project?"),
        )
        .arg(
            Arg::new("directory")
                .id("DIR")
                .takes_value(true)
                .required(true)
                .help("Directory for the project"),
        )
}

pub fn run(matches: &ArgMatches, ditto_version: &Version) -> Result<()> {
    let project_dir = matches.value_of("DIR").unwrap();
    let package_name = PackageName::new_unchecked(
        matches
            .value_of("name")
            .map_or_else(
                || {
                    if !config::PACKAGE_NAME_REGEX.is_match(project_dir) {
                        bail!("If `--name` isn't specified, DIR must be a valid package name")
                    }
                    Ok(project_dir)
                },
                Ok,
            )?
            .to_owned(),
    );

    let project_dir = PathBuf::from(project_dir);

    if project_dir.exists() {
        return Err(miette!(
            "path {:?} already exists",
            project_dir.to_string_lossy()
        ));
    }

    println!("Writing files...");
    fs::create_dir_all(&project_dir)
        .into_diagnostic()
        .wrap_err(format!(
            "error creating new project directory {:?}",
            project_dir.to_string_lossy()
        ))?;

    let flavour = get_flavour(matches)?;

    write_files(package_name, &project_dir, ditto_version, &flavour)?;

    // Run an initial `ditto make` in the new directory to kick things off
    if let Ok(ditto) = current_exe() {
        println!("\nRunning `ditto make`...");
        process::Command::new(ditto)
            .arg("make")
            .current_dir(&project_dir)
            .status()
            .into_diagnostic()
            .wrap_err("error running `make` in new project directory")?;
    }

    Ok(())
}

enum Flavour {
    Bland,
    JavaScript, // `--javascript`
}

fn get_flavour(matches: &ArgMatches) -> Result<Flavour> {
    if matches.is_present("javascript") {
        return Ok(Flavour::JavaScript);
    }
    Ok(Flavour::Bland)
}

fn write_files(
    package_name: PackageName,
    project_dir: &Path,
    ditto_version: &Version,
    flavour: &Flavour,
) -> Result<()> {
    let config = write_new_config(package_name, project_dir, ditto_version, flavour)?;
    write_empty_ditto_module(&config, project_dir)?;
    write_new_gitignore(&config, project_dir, flavour)?;
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            write_js_files(&config, project_dir)?;
        }
    }
    Ok(())
}

fn write_js_files(config: &config::Config, project_dir: &Path) -> Result<()> {
    write_package_json(config, project_dir)
}

fn write_package_json(config: &config::Config, project_dir: &Path) -> Result<()> {
    let mut path = project_dir.to_path_buf();
    path.push("package");
    path.set_extension("json");
    let file = fs::File::create(&path).into_diagnostic().wrap_err(format!(
        "error creating package.json file at {:?}",
        path.to_string_lossy()
    ))?;
    let workspaces = vec![format!(
        "{}/*",
        config.codegen_js_config.packages_dir.to_string_lossy()
    )];
    let value = serde_json::json!({
        "private": true,
        "type": "module",
        "workspaces": workspaces,
    });
    serde_json::to_writer_pretty(file, &value)
        .into_diagnostic()
        .wrap_err(format!(
            "error writing package.json file to {:?}",
            path.to_string_lossy()
        ))?;
    log_path_written(path);
    Ok(())
}

fn write_new_config(
    package_name: PackageName,
    project_dir: &Path,
    ditto_version: &Version,
    flavour: &Flavour,
) -> Result<config::Config> {
    let mut config = config::Config::new(package_name);
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            config.targets =
                std::collections::HashSet::from([config::Target::Web, config::Target::Nodejs]);
        }
    }
    let mut config_path = project_dir.to_path_buf();
    config_path.push(config::CONFIG_FILE_NAME);
    let config_string = toml::to_string(&config)
        .into_diagnostic()
        .wrap_err("error serializing new config file")?;

    let preamble = format!(
        "# Welcome to your new ditto project!
#
# Options for this file can be found at:
# https://github.com/ditto-lang/ditto/tree/{rev}/crates/ditto-config#readme",
        rev = ditto_version.git_rev
    );

    fs::write(&config_path, format!("{}\n{}", preamble, config_string))
        .into_diagnostic()
        .wrap_err(format!(
            "error writing new config file to {:?}",
            config_path.to_string_lossy()
        ))?;

    log_path_written(&config_path);
    Ok(config)
}

fn write_new_gitignore(
    config: &config::Config,
    project_dir: &Path,
    flavour: &Flavour,
) -> Result<()> {
    let mut path = project_dir.to_path_buf();
    path.push(".gitignore");

    let mut ignore_lines = vec![
        // .ditto
        config.ditto_dir.to_string_lossy().into_owned(),
    ];
    match flavour {
        Flavour::Bland => {}
        Flavour::JavaScript => {
            // dist
            ignore_lines.push(
                config
                    .codegen_js_config
                    .dist_dir
                    .to_string_lossy()
                    .into_owned(),
            );
            // node_modules
            ignore_lines.push(String::from("node_modules"));
        }
    }

    fs::write(&path, ignore_lines.join("\n"))
        .into_diagnostic()
        .wrap_err(format!(
            "error writing .gitignore to {:?}",
            path.to_string_lossy()
        ))?;

    log_path_written(&path);
    Ok(())
}

fn write_empty_ditto_module(config: &config::Config, project_dir: &Path) -> Result<()> {
    let mut module_path = project_dir.to_path_buf();
    module_path.push(&config.src_dir);
    fs::create_dir_all(&module_path)
        .into_diagnostic()
        .wrap_err(format!(
            "error creating ditto source directory {:?}",
            module_path.to_string_lossy()
        ))?;
    let module_name = config.name.to_case(Case::Pascal);
    module_path.push(&module_name);
    module_path.set_extension("ditto");

    let module_contents = format!("module {} exports (..);", module_name);
    write_ditto_module(module_path, module_contents)
}

fn write_ditto_module<P: AsRef<Path>>(path: P, contents: String) -> Result<()> {
    let module = ditto_cst::Module::parse(&contents).map_err(|_| {
        miette!(
            "Internal error: couldn't parse generated module: {:?}",
            contents
        )
    })?;
    let formatted = ditto_fmt::format_module(module);
    fs::write(&path, formatted)
        .into_diagnostic()
        .wrap_err(format!(
            "error writing ditto module to {}",
            path.as_ref().to_string_lossy()
        ))?;
    log_path_written(path);
    Ok(())
}

fn log_path_written<P: AsRef<Path>>(path: P) {
    if crate::common::is_plain() {
        println!("Wrote {}", path.as_ref().to_string_lossy());
    } else {
        let message = format!(
            "  {} {}",
            Emoji("✨", "Wrote"),
            path.as_ref().to_string_lossy()
        );
        println!("{}", Style::new().cyan().apply_to(message));
    }
}
<|endoftext|>


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

//! null record type, generally not used except as an internal tool for representing null data

use crate::error::*;
use crate::serialize::binary::*;

/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
///
/// ```text
/// 3.3.10. NULL RDATA format (EXPERIMENTAL)
///
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///     /                  <anything>                   /
///     /                                               /
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// Anything at all may be in the RDATA field so long as it is 65535 octets
/// or less.
///
/// NULL records cause no additional section processing.  NULL RRs are not
/// allowed in master files.  NULLs are used as placeholders in some
/// experimental extensions of the DNS.
/// ```
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone)]
pub struct NULL {
    anything: Option<Vec<u8>>,
}

impl NULL {
    /// Construct a new NULL RData
    pub fn new() -> NULL {
        Default::default()
    }

    /// Constructs a new NULL RData with the associated data
    pub fn with(anything: Vec<u8>) -> NULL {
        NULL {
            anything: Some(anything),
        }
    }

    /// Returns the buffer stored in the NULL
    pub fn anything(&self) -> Option<&Vec<u8>> {
        self.anything.as_ref()
    }
}

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<NULL> {
    let rdata_length = rdata_length.map(|u| u as usize).unverified(/*any u16 is valid*/);
    if rdata_length > 0 {
        let anything = decoder.read_vec(rdata_length)?.unverified(/*any byte array is good*/);
        Ok(NULL::with(anything))
    } else {
        Ok(NULL::new())
    }
}

/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder, nil: &NULL) -> ProtoResult<()> {
    if let Some(anything) = nil.anything() {
        for b in anything.iter() {
            encoder.emit(*b)?;
        }
    }

    Ok(())
}

#[test]
pub fn test() {
    let rdata = NULL::with(vec![0, 1, 2, 3, 4, 5, 6, 7]);

    let mut bytes = Vec::new();
    let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
    assert!(emit(&mut encoder, &rdata).is_ok());
    let bytes = encoder.into_bytes();

    println!("bytes: {:?}", bytes);

    let mut decoder: BinDecoder = BinDecoder::new(bytes);
    let restrict = Restrict::new(bytes.len() as u16);
    let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
    assert_eq!(rdata, read_rdata);
}
<commit_msg>proto: Return slice instead of `Vec` in `NULL` accessor<commit_after>/*
 * Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! null record type, generally not used except as an internal tool for representing null data

use crate::error::*;
use crate::serialize::binary::*;

/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
///
/// ```text
/// 3.3.10. NULL RDATA format (EXPERIMENTAL)
///
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///     /                  <anything>                   /
///     /                                               /
///     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// Anything at all may be in the RDATA field so long as it is 65535 octets
/// or less.
///
/// NULL records cause no additional section processing.  NULL RRs are not
/// allowed in master files.  NULLs are used as placeholders in some
/// experimental extensions of the DNS.
/// ```
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone)]
pub struct NULL {
    anything: Option<Vec<u8>>,
}

impl NULL {
    /// Construct a new NULL RData
    pub fn new() -> NULL {
        Default::default()
    }

    /// Constructs a new NULL RData with the associated data
    pub fn with(anything: Vec<u8>) -> NULL {
        NULL {
            anything: Some(anything),
        }
    }

    /// Returns the buffer stored in the NULL
    pub fn anything(&self) -> Option<&[u8]> {
        self.anything.as_ref().map(|bytes| &bytes[..])
    }
}

/// Read the RData from the given Decoder
pub fn read(decoder: &mut BinDecoder, rdata_length: Restrict<u16>) -> ProtoResult<NULL> {
    let rdata_length = rdata_length.map(|u| u as usize).unverified(/*any u16 is valid*/);
    if rdata_length > 0 {
        let anything = decoder.read_vec(rdata_length)?.unverified(/*any byte array is good*/);
        Ok(NULL::with(anything))
    } else {
        Ok(NULL::new())
    }
}

/// Write the RData from the given Decoder
pub fn emit(encoder: &mut BinEncoder, nil: &NULL) -> ProtoResult<()> {
    if let Some(anything) = nil.anything() {
        for b in anything.iter() {
            encoder.emit(*b)?;
        }
    }

    Ok(())
}

#[test]
pub fn test() {
    let rdata = NULL::with(vec![0, 1, 2, 3, 4, 5, 6, 7]);

    let mut bytes = Vec::new();
    let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
    assert!(emit(&mut encoder, &rdata).is_ok());
    let bytes = encoder.into_bytes();

    println!("bytes: {:?}", bytes);

    let mut decoder: BinDecoder = BinDecoder::new(bytes);
    let restrict = Restrict::new(bytes.len() as u16);
    let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
    assert_eq!(rdata, read_rdata);
}
<|endoftext|>