<commit_before>extern crate byteorder;
extern crate sodiumoxide;
extern crate tokio_core;

use std;
use self::byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use self::sodiumoxide::crypto::secretbox;
use self::tokio_core::io::{Codec, EasyBuf};

pub struct SecretBoxCodec<T: Codec> {
    codec: T,
    key: secretbox::Key,
}

impl<T: Codec> SecretBoxCodec<T> {
    pub fn new(codec: T, key: secretbox::Key) -> SecretBoxCodec<T> {
        SecretBoxCodec {
            codec: codec,
            key: key,
        }
    }
}

impl<T: Codec> Codec for SecretBoxCodec<T> {
    type Out = T::Out;
    type In = T::In;

    fn decode(&mut self, buf: &mut EasyBuf) -> std::io::Result<Option<T::In>> {
        if buf.len() < secretbox::NONCEBYTES + 4 {
            return Ok(None);
        }
        let h_buf = buf.drain_to(secretbox::NONCEBYTES + 4);
        let mut h = h_buf.as_slice();

        let nonce = secretbox::Nonce::from_slice(&h[0..secretbox::NONCEBYTES]).unwrap();
        h = &h[secretbox::NONCEBYTES..];
        let c_len = BigEndian::read_u32(&h[0..4]) as usize;

        if buf.len() < c_len {
            return Ok(None);
        }
        let c_buf = buf.drain_to(c_len);
        match secretbox::open(c_buf.as_slice(), &nonce, &self.key) {
            Ok(p) => {
                debug!("open ok");
                self.codec.decode(&mut EasyBuf::from(p))
            }
            Err(()) => {
                debug!("open failed, invalid ciphertext");
                Err(std::io::Error::new(std::io::ErrorKind::Other, "message invalid"))
            }
        }
    }

    fn encode(&mut self, msg: T::Out, buf: &mut Vec<u8>) -> std::io::Result<()> {
        let nonce = secretbox::gen_nonce();
        buf.extend_from_slice(&nonce.0);

        let mut p = vec![];
        self.codec.encode(msg, &mut p)?;
        let mut c = secretbox::seal(&p, &nonce, &self.key);

        let mut l = vec![];
        l.write_u32::<BigEndian>(c.len() as u32)?;

        buf.append(&mut l);
        buf.append(&mut c);
        Ok(())
    }
}
<commit_msg>Add sodium box (public-key encryption) codec wrapper.<commit_after>extern crate byteorder;
extern crate sodiumoxide;
extern crate tokio_core;

use std;
use self::byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use self::sodiumoxide::crypto::{box_, secretbox};
use self::tokio_core::io::{Codec, EasyBuf};

pub struct SecretBoxCodec<T: Codec> {
    codec: T,
    key: secretbox::Key,
}

impl<T: Codec> SecretBoxCodec<T> {
    pub fn new(codec: T, key: secretbox::Key) -> SecretBoxCodec<T> {
        SecretBoxCodec {
            codec: codec,
            key: key,
        }
    }
}

impl<T: Codec> Codec for SecretBoxCodec<T> {
    type Out = T::Out;
    type In = T::In;

    fn decode(&mut self, buf: &mut EasyBuf) -> std::io::Result<Option<T::In>> {
        if buf.len() < secretbox::NONCEBYTES + 4 {
            return Ok(None);
        }
        let h_buf = buf.drain_to(secretbox::NONCEBYTES + 4);
        let mut h = h_buf.as_slice();

        let nonce = secretbox::Nonce::from_slice(&h[0..secretbox::NONCEBYTES]).unwrap();
        h = &h[secretbox::NONCEBYTES..];
        let c_len = BigEndian::read_u32(&h[0..4]) as usize;

        if buf.len() < c_len {
            return Ok(None);
        }
        let c_buf = buf.drain_to(c_len);
        match secretbox::open(c_buf.as_slice(), &nonce, &self.key) {
            Ok(p) => {
                debug!("open ok");
                self.codec.decode(&mut EasyBuf::from(p))
            }
            Err(()) => {
                debug!("open failed, invalid ciphertext");
                Err(std::io::Error::new(std::io::ErrorKind::Other, "message invalid"))
            }
        }
    }

    fn encode(&mut self, msg: T::Out, buf: &mut Vec<u8>) -> std::io::Result<()> {
        let nonce = secretbox::gen_nonce();
        buf.extend_from_slice(&nonce.0);

        let mut p = vec![];
        self.codec.encode(msg, &mut p)?;
        let mut c = secretbox::seal(&p, &nonce, &self.key);

        let mut l = vec![];
        l.write_u32::<BigEndian>(c.len() as u32)?;

        buf.append(&mut l);
        buf.append(&mut c);
        Ok(())
    }
}

pub struct BoxCodec<T: Codec> {
    codec: T,
    pubkey: box_::PublicKey,
    seckey: box_::SecretKey,
}

impl<T: Codec> BoxCodec<T> {
    pub fn new(codec: T, pubkey: box_::PublicKey, seckey: box_::SecretKey) -> BoxCodec<T> {
        BoxCodec {
            codec: codec,
            pubkey: pubkey,
            seckey: seckey,
        }
    }
}

impl<T: Codec> Codec for BoxCodec<T> {
    type Out = T::Out;
    type In = T::In;

    fn decode(&mut self, buf: &mut EasyBuf) -> std::io::Result<Option<T::In>> {
        if buf.len() < box_::NONCEBYTES + 4 {
            return Ok(None);
        }
        let h_buf = buf.drain_to(box_::NONCEBYTES + 4);
        let mut h = h_buf.as_slice();

        let nonce = box_::Nonce::from_slice(&h[0..box_::NONCEBYTES]).unwrap();
        h = &h[box_::NONCEBYTES..];
        let c_len = BigEndian::read_u32(&h[0..4]) as usize;

        if buf.len() < c_len {
            return Ok(None);
        }
        let c_buf = buf.drain_to(c_len);
        match box_::open(c_buf.as_slice(), &nonce, &self.pubkey, &self.seckey) {
            Ok(p) => {
                debug!("open ok");
                self.codec.decode(&mut EasyBuf::from(p))
            }
            Err(()) => {
                debug!("open failed, invalid ciphertext");
                Err(std::io::Error::new(std::io::ErrorKind::Other, "message invalid"))
            }
        }
    }

    fn encode(&mut self, msg: T::Out, buf: &mut Vec<u8>) -> std::io::Result<()> {
        let nonce = box_::gen_nonce();
        buf.extend_from_slice(&nonce.0);

        let mut p = vec![];
        self.codec.encode(msg, &mut p)?;
        let mut c = box_::seal(&p, &nonce, &self.pubkey, &self.seckey);

        let mut l = vec![];
        l.write_u32::<BigEndian>(c.len() as u32)?;

        buf.append(&mut l);
        buf.append(&mut c);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::hex::{ToHex, FromHex};
use rustc_serialize::base64::{ToBase64, FromBase64, STANDARD};
use std::str;
use std::io::BufReader;
use std::io::BufWriter;
use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::cmp;
use std::fs::OpenOptions;

pub struct CipherText(Vec<u8>);

impl CipherText {
    pub fn new(bytes: &[u8]) -> CipherText {
        CipherText(bytes.to_vec())
    }

    pub fn from_hex(hex: &str) -> Result<CipherText, Error> {
        let cypher_bytes: Vec<u8> = try!(hex.from_hex()
            .map_err(|e| Error::Hex(e.to_string())));
        Ok(CipherText(cypher_bytes))
    }

    pub fn to_hex(&self) -> String {
        let &CipherText(ref vec_bytes) = self;
        vec_bytes.to_hex()
    }

    pub fn from_b64(b64: &str) -> Result<CipherText, Error> {
        let cypher_bytes: Vec<u8> = try!(b64.from_base64()
            .map_err(|e| Error::Base64(e.to_string())));
        Ok(CipherText(cypher_bytes))
    }

    pub fn to_b64(&self) -> String {
        let &CipherText(ref vec_bytes) = self;
        vec_bytes.to_base64(STANDARD)
    }

    pub fn from_file(path: &str) -> Result<CipherText, Error> {
        let mut text = String::new();
        let file = try!(File::open(Path::new(path)).map_err(|e| Error::File(e.to_string())));
        let in_file = BufReader::new(file);
        for line in in_file.lines() {
            text.push_str(&line.unwrap());
        }
        CipherText::from_b64(&text)
    }

    pub fn to_file(&self, path: &str) -> Result<(), Error> {

        let text_b64 = self.to_b64();
        let mut file = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(path)
            .unwrap();

        let mut i = 0;
        let mut j = cmp::min(text_b64.len(), 80);
        while i != j {
            try!(file.write_all(&text_b64[i..j].as_bytes())
                .map_err(|e| Error::File(e.to_string())));

            try!(file.write_all("\n".as_bytes()).map_err(|e| Error::File(e.to_string())));

            i = j;
            j = cmp::min(text_b64.len(), j + 80);
        }
        Ok(())
    }
}

pub struct PlainText(Vec<u8>);

impl PlainText {
    pub fn from_string(string: &str) -> PlainText {
        PlainText(string.as_bytes().to_vec())
    }

    pub fn from_bytes(bytes: &[u8]) -> PlainText {
        PlainText(bytes.to_vec())
    }

    pub fn from_file(path: &str) -> Result<PlainText, Error> {
        let file = try!(File::open(Path::new(path)).map_err(|e| Error::File(e.to_string())));
        let mut in_file = BufReader::new(file);
        let mut text = String::new();
        try!(in_file.read_to_string(&mut text)
            .map_err(|e| Error::File(e.to_string())));
        Ok(PlainText::from_string(&text))

    }

    pub fn to_file(&self, path: &str) -> Result<(), Error> {
        let &PlainText(ref vec_bytes) = self;
        let mut out_file = BufWriter::new(File::create(Path::new(path)).unwrap());
        try!(out_file.write_all(&vec_bytes).map_err(|e| Error::File(e.to_string())));

        Ok(())
    }

    pub fn to_utf8(&self) -> Result<String, Error> {
        let &PlainText(ref vec_bytes) = self;
        let plain = try!(str::from_utf8(&vec_bytes).map_err(|e| Error::UTF8(e.to_string())));
        Ok(plain.to_string())
    }
}

pub fn encrypt(text: &PlainText, key: &str) -> CipherText {
    let &PlainText(ref bytes) = text;
    let xored: Vec<u8> = repeating_xor(bytes, key.as_bytes());
    CipherText::new(&xored)
}

pub fn decrypt(cypher_text: &CipherText, key: &str) -> Result<PlainText, Error> {
    let &CipherText(ref bytes) = cypher_text;
    let xored: Vec<u8> = repeating_xor(bytes, key.as_bytes());
    Ok(PlainText::from_bytes(&xored))
}

pub fn decrypt_file(input_path: &str, output_path: &str, key: &str) -> Result<(), Error> {
    let cipher = try!(CipherText::from_file(input_path));
    let plain = try!(decrypt(&cipher, key));
    try!(plain.to_file(output_path));
    Ok(())
}

pub fn encrypt_file(input_path: &str, output_path: &str, key: &str) -> Result<(), Error> {
    let plain = try!(PlainText::from_file(input_path));
    let cipher = encrypt(&plain, key);
    try!(cipher.to_file(output_path));
    Ok(())
}

#[derive(Debug)]
pub enum Error {
    Hex(String),
    Base64(String),
    UTF8(String),
    File(String),
}

fn repeating_xor(input: &[u8], key: &[u8]) -> Vec<u8> {
    let mut out: Vec<u8> = Vec::with_capacity(input.len());
    unsafe {
        out.set_len(input.len());
    }
    for (i, in_val) in input.iter().enumerate() {
        out[i] = in_val ^ key[i % key.len()];
    }
    out
}

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

    #[test]
    fn test_encrypt_and_decrypt() {
        let plain_text = PlainText::from_string("This is the plain text");
        let key: &str = "toy";
        assert_eq!(plain_text.to_utf8().unwrap(),
                   decrypt(&encrypt(&plain_text, key), key).unwrap().to_utf8().unwrap());
    }

}
<commit_msg>Added extra tests<commit_after>use rustc_serialize::hex::{ToHex, FromHex};
use rustc_serialize::base64::{ToBase64, FromBase64, STANDARD};
use std::str;
use std::io::BufReader;
use std::io::BufWriter;
use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::cmp;
use std::fs::OpenOptions;

pub struct CipherText(Vec<u8>);

impl CipherText {
    pub fn new(bytes: &[u8]) -> CipherText {
        CipherText(bytes.to_vec())
    }

    pub fn from_hex(hex: &str) -> Result<CipherText, Error> {
        let cypher_bytes: Vec<u8> = try!(hex.from_hex()
            .map_err(|e| Error::Hex(e.to_string())));
        Ok(CipherText(cypher_bytes))
    }

    pub fn to_hex(&self) -> String {
        let &CipherText(ref vec_bytes) = self;
        vec_bytes.to_hex()
    }

    pub fn from_b64(b64: &str) -> Result<CipherText, Error> {
        let cypher_bytes: Vec<u8> = try!(b64.from_base64()
            .map_err(|e| Error::Base64(e.to_string())));
        Ok(CipherText(cypher_bytes))
    }

    pub fn to_b64(&self) -> String {
        let &CipherText(ref vec_bytes) = self;
        vec_bytes.to_base64(STANDARD)
    }

    pub fn from_file(path: &str) -> Result<CipherText, Error> {
        let mut text = String::new();
        let file = try!(File::open(Path::new(path)).map_err(|e| Error::File(e.to_string())));
        let in_file = BufReader::new(file);
        for line in in_file.lines() {
            text.push_str(&line.unwrap());
        }
        CipherText::from_b64(&text)
    }

    pub fn to_file(&self, path: &str) -> Result<(), Error> {

        let text_b64 = self.to_b64();
        let mut file = OpenOptions::new()
            .write(true)
            .append(true)
            .create(true)
            .open(path)
            .unwrap();

        let mut i = 0;
        let mut j = cmp::min(text_b64.len(), 80);
        while i != j {
            try!(file.write_all(&text_b64[i..j].as_bytes())
                .map_err(|e| Error::File(e.to_string())));

            try!(file.write_all("\n".as_bytes()).map_err(|e| Error::File(e.to_string())));

            i = j;
            j = cmp::min(text_b64.len(), j + 80);
        }
        Ok(())
    }
}

pub struct PlainText(Vec<u8>);

impl PlainText {
    pub fn from_string(string: &str) -> PlainText {
        PlainText(string.as_bytes().to_vec())
    }

    pub fn from_bytes(bytes: &[u8]) -> PlainText {
        PlainText(bytes.to_vec())
    }

    pub fn from_file(path: &str) -> Result<PlainText, Error> {
        let file = try!(File::open(Path::new(path)).map_err(|e| Error::File(e.to_string())));
        let mut in_file = BufReader::new(file);
        let mut text = String::new();
        try!(in_file.read_to_string(&mut text)
            .map_err(|e| Error::File(e.to_string())));
        Ok(PlainText::from_string(&text))

    }

    pub fn to_file(&self, path: &str) -> Result<(), Error> {
        let &PlainText(ref vec_bytes) = self;
        let mut out_file = BufWriter::new(File::create(Path::new(path)).unwrap());
        try!(out_file.write_all(&vec_bytes).map_err(|e| Error::File(e.to_string())));

        Ok(())
    }

    pub fn to_utf8(&self) -> Result<String, Error> {
        let &PlainText(ref vec_bytes) = self;
        let plain = try!(str::from_utf8(&vec_bytes).map_err(|e| Error::UTF8(e.to_string())));
        Ok(plain.to_string())
    }
}

pub fn encrypt(text: &PlainText, key: &str) -> CipherText {
    let &PlainText(ref bytes) = text;
    let xored: Vec<u8> = repeating_xor(bytes, key.as_bytes());
    CipherText::new(&xored)
}

pub fn decrypt(cypher_text: &CipherText, key: &str) -> Result<PlainText, Error> {
    let &CipherText(ref bytes) = cypher_text;
    let xored: Vec<u8> = repeating_xor(bytes, key.as_bytes());
    Ok(PlainText::from_bytes(&xored))
}

pub fn decrypt_file(input_path: &str, output_path: &str, key: &str) -> Result<(), Error> {
    let cipher = try!(CipherText::from_file(input_path));
    let plain = try!(decrypt(&cipher, key));
    try!(plain.to_file(output_path));
    Ok(())
}

pub fn encrypt_file(input_path: &str, output_path: &str, key: &str) -> Result<(), Error> {
    let plain = try!(PlainText::from_file(input_path));
    let cipher = encrypt(&plain, key);
    try!(cipher.to_file(output_path));
    Ok(())
}

#[derive(Debug)]
pub enum Error {
    Hex(String),
    Base64(String),
    UTF8(String),
    File(String),
}

fn repeating_xor(input: &[u8], key: &[u8]) -> Vec<u8> {
    let mut out: Vec<u8> = Vec::with_capacity(input.len());
    unsafe {
        out.set_len(input.len());
    }
    for (i, in_val) in input.iter().enumerate() {
        out[i] = in_val ^ key[i % key.len()];
    }
    out
}

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

    #[test]
    fn test_encrypt_and_decrypt() {
        check_text("This is the plain text");
        check_text("123, 456");
        check_text("!£$%^&*@~:");
        check_text("日本語");
    }

    #[test]
    fn test_wrong_key() {
        let text = "This is the plain text";
        let plain_text = PlainText::from_string(text);
        let key: &str = "toy";
        let cipher_text = decrypt(&encrypt(&plain_text, key), "wrong_key").unwrap();
        assert!(text != cipher_text.to_utf8().unwrap());
    }

    fn check_text(text: &str) {
        let plain_text = PlainText::from_string(text);
        let key: &str = "toy";
        let cipher_text = decrypt(&encrypt(&plain_text, key), key).unwrap();
        assert_eq!(text, cipher_text.to_utf8().unwrap());
    }

}
<|endoftext|>


<commit_before>// Copyright © 2016 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::cell::{Ref, RefCell};
use math;
use math::Vec3;

pub struct Entity {
    position: RefCell<Vec3>,
    velocity: RefCell<Vec3>,
    angle: RefCell<Vec3>,
}

impl Entity {
    pub fn new() -> Self {
        Entity {
            position: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
            velocity: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
            angle: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
        }
    }

    pub fn get_position(&self) -> Vec3 {
        (*self.position.borrow()).clone()
    }

    pub fn set_position(&self, x: f32, y: f32, z: f32) {
        let mut pos = self.position.borrow_mut();
        pos[0] = x;
        pos[1] = y;
        pos[2] = z;
    }

    pub fn adjust_position(&self, x: f32, y: f32, z: f32) {
        let mut pos = self.position.borrow_mut();
        pos[0] += x;
        pos[1] += y;
        pos[2] += z;
    }

    pub fn get_angle(&self) -> Vec3 {
        (*self.angle.borrow()).clone()
    }

    fn clamp_angle(&self) {
        let mut angle = self.angle.borrow_mut();
        if angle[0] < -math::PI / 2.0 {
            angle[0] = -math::PI / 2.0;
        } else if angle[0] > math::PI / 2.0 {
            angle[0] = math::PI / 2.0;
        }
    }

    pub fn set_angle(&self, x: f32, y: f32, z: f32) {
        {
            let mut angle = self.angle.borrow_mut();
            angle[0] = x;
            angle[1] = y;
            angle[2] = z;
        }
        self.clamp_angle();
    }

    pub fn adjust_angle(&self, x: f32, y: f32, z: f32) {
        {
            let mut angle = self.angle.borrow_mut();
            angle[0] += x;
            angle[1] += y;
            angle[2] += z;
        }
        self.clamp_angle();
    }
}
<commit_msg>Add edict type<commit_after>// Copyright © 2016 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::cell::{Ref, RefCell};
use math;
use math::Vec3;

pub struct Entity {
    position: RefCell<Vec3>,
    velocity: RefCell<Vec3>,
    angle: RefCell<Vec3>,
}

impl Entity {
    pub fn new() -> Self {
        Entity {
            position: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
            velocity: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
            angle: RefCell::new(Vec3::new(0.0, 0.0, 0.0)),
        }
    }

    pub fn get_position(&self) -> Vec3 {
        (*self.position.borrow()).clone()
    }

    pub fn set_position(&self, x: f32, y: f32, z: f32) {
        let mut pos = self.position.borrow_mut();
        pos[0] = x;
        pos[1] = y;
        pos[2] = z;
    }

    pub fn adjust_position(&self, x: f32, y: f32, z: f32) {
        let mut pos = self.position.borrow_mut();
        pos[0] += x;
        pos[1] += y;
        pos[2] += z;
    }

    pub fn get_angle(&self) -> Vec3 {
        (*self.angle.borrow()).clone()
    }

    fn clamp_angle(&self) {
        let mut angle = self.angle.borrow_mut();
        if angle[0] < -math::PI / 2.0 {
            angle[0] = -math::PI / 2.0;
        } else if angle[0] > math::PI / 2.0 {
            angle[0] = math::PI / 2.0;
        }
    }

    pub fn set_angle(&self, x: f32, y: f32, z: f32) {
        {
            let mut angle = self.angle.borrow_mut();
            angle[0] = x;
            angle[1] = y;
            angle[2] = z;
        }
        self.clamp_angle();
    }

    pub fn adjust_angle(&self, x: f32, y: f32, z: f32) {
        {
            let mut angle = self.angle.borrow_mut();
            angle[0] += x;
            angle[1] += y;
            angle[2] += z;
        }
        self.clamp_angle();
    }
}

type StringAddr = i32;
type FuncAddr = i32;

#[repr(C)]
pub struct Edict {
    model_id: f32,
    abs_min: Vec3,
    abs_max: Vec3,
    ltime: f32,
    last_run: f32,
    move_type: f32,
    is_solid: f32,
    origin: Vec3,
    last_origin: Vec3,
    velocity: Vec3,
    angles: Vec3,
    avelocity: Vec3,
    classname: StringAddr,
    modelname: StringAddr,
    frame: f32,
    skin: f32,
    effects: f32,
    mins: Vec3,
    maxs: Vec3,
    size: Vec3,
    touch: FuncAddr,
    use_: FuncAddr,
    think: FuncAddr,
    blocked: FuncAddr,
    nextthink: f32,
    groundentity: i32,
    health: f32,
    frags: f32,
    weapon: f32,
    weaponmodel: StringAddr,
    weaponframe: f32,
    currentammo: f32,
    ammo_shells: f32,
    ammo_nails: f32,
    ammo_rockets: f32,
    ammo_cells: f32,
    items: f32,
    takedamage: f32,
    chain: i32,
    deadflag: f32,
    view_ofs: Vec3,
    button0: f32,
    button1: f32,
    button2: f32,
    impulse: f32,
    fixangle: f32,
    v_angle: Vec3,
    netname: StringAddr,
    enemy: i32,
    flags: f32,
    colormap: f32,
    team: f32,
    max_health: f32,
    teleport_time: f32,
    armortype: f32,
    armorvalue: f32,
    waterlevel: f32,
    watertype: f32,
    ideal_yaw: f32,
    yaw_speed: f32,
    aiment: i32,
    goalentity: i32,
    spawnflags: f32,
    target: StringAddr,
    targetname: StringAddr,
    dmg_take: f32,
    dmg_save: f32,
    dmg_inflictor: i32,
    owner: i32,
    movedir: Vec3,
    message: StringAddr,
    sounds: f32,
    noise: StringAddr,
    noise1: StringAddr,
    noise2: StringAddr,
    noise3: StringAddr,
}
<|endoftext|>


<commit_before>use std::io::Error as IoError;
use reqwest::Error as HttpError;
use reqwest::StatusCode;
use serde_json::error::Error as SerdeError;
use super::Errors;

/// an enumeration over potential errors
/// that may happen when sending a request to jira
#[derive(Debug)]
pub enum Error {
    /// error associated with http request
    Http(HttpError),
    /// error associated IO
    IO(IoError),
    /// error associated with parsing or serializing
    Serde(SerdeError),
    /// client request errors
    Fault { code: StatusCode, errors: Errors },
    /// invalid credentials
    Unauthorized,
}

impl From<SerdeError> for Error {
    fn from(error: SerdeError) -> Error {
        Error::Serde(error)
    }
}

impl From<HttpError> for Error {
    fn from(error: HttpError) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use Error::*;
        match self {
            &Http(ref e) => writeln!(f, "Http Error: {}", e),
            &IO(ref e) => writeln!(f, "IO Error: {}", e),
            &Serde(ref e) => writeln!(f, "Serialization Error: {}", e),
            &Fault {
                ref code,
                ref errors,
            } => writeln!(f, "Jira Client Error ({}):\n{:#?}", code, errors),
            &Unauthorized => writeln!(f, "Could not connect to Jira: Unauthorized!"),
        }
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        use Error::*;
        match self {
            &Http(ref e) => e.description(),
            &IO(ref e) => e.description(),
            &Serde(ref e) => e.description(),
            &Fault { .. } => "Jira client error",
            &Unauthorized => "Unauthorized",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        use Error::*;
        match self {
            &Http(ref e) => Some(e),
            &IO(ref e) => Some(e),
            &Serde(ref e) => Some(e),
            &Fault { .. } => None,
            &Unauthorized => None,
        }
    }
}
<commit_msg>Deference before matching<commit_after>use std::io::Error as IoError;
use reqwest::Error as HttpError;
use reqwest::StatusCode;
use serde_json::error::Error as SerdeError;
use super::Errors;

/// an enumeration over potential errors
/// that may happen when sending a request to jira
#[derive(Debug)]
pub enum Error {
    /// error associated with http request
    Http(HttpError),
    /// error associated IO
    IO(IoError),
    /// error associated with parsing or serializing
    Serde(SerdeError),
    /// client request errors
    Fault { code: StatusCode, errors: Errors },
    /// invalid credentials
    Unauthorized,
}

impl From<SerdeError> for Error {
    fn from(error: SerdeError) -> Error {
        Error::Serde(error)
    }
}

impl From<HttpError> for Error {
    fn from(error: HttpError) -> Error {
        Error::Http(error)
    }
}

impl From<IoError> for Error {
    fn from(error: IoError) -> Error {
        Error::IO(error)
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        use Error::*;
        match *self {
            Http(ref e) => writeln!(f, "Http Error: {}", e),
            IO(ref e) => writeln!(f, "IO Error: {}", e),
            Serde(ref e) => writeln!(f, "Serialization Error: {}", e),
            Fault {
                ref code,
                ref errors,
            } => writeln!(f, "Jira Client Error ({}):\n{:#?}", code, errors),
            Unauthorized => writeln!(f, "Could not connect to Jira: Unauthorized!"),
        }
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        use Error::*;
        match *self {
            Http(ref e) => e.description(),
            IO(ref e) => e.description(),
            Serde(ref e) => e.description(),
            Fault { .. } => "Jira client error",
            Unauthorized => "Unauthorized",
        }
    }

    fn cause(&self) -> Option<&::std::error::Error> {
        use Error::*;
        match *self {
            Http(ref e) => Some(e),
            IO(ref e) => Some(e),
            Serde(ref e) => Some(e),
            Fault { .. } => None,
            Unauthorized => None,
        }
    }
}
<|endoftext|>


<commit_before>//! Contains functions for performing XML special characters escaping.

use std::borrow::Cow;

use self::Value::{C, S};
use self::Process::{B, O};

enum Value {
    C(char),
    S(&'static str)
}

impl Value {
    fn dispatch_for_attribute(c: char) -> Value {
        match c {
            '<'  => S("<"),
            '>'  => S(">"),
            '"'  => S("""),
            '\'' => S("'"),
            '&'  => S("&"),
            _    => C(c)
        }
    }

    fn dispatch_for_pcdata(c: char) -> Value {
        match c {
            '<'  => S("<"),
            '&'  => S("&"),
            _    => C(c)
        }
    }
}

enum Process<'a> {
    B(&'a str),
    O(String)
}

impl<'a> Process<'a> {
    fn process(&mut self, (i, next): (usize, Value)) {
        match next {
            S(s) => if let O(ref mut o) = *self {
                o.push_str(s);
            } else if let B(b) = *self {
                let mut r = String::with_capacity(b.len() + s.len());
                r.push_str(&b[..i]);
                r.push_str(s);
                *self = O(r);
            },
            C(c) => match *self {
                B(_) => {}
                O(ref mut o) => o.push(c)
            }
        }
    }

    fn into_result(self) -> Cow<'a, str> {
        match self {
            B(b) => Cow::Borrowed(b),
            O(o) => Cow::Owned(o)
        }
    }
}

impl<'a> Extend<Value> for Process<'a> {
    fn extend<I: IntoIterator<Item=Value>>(&mut self, it: I) {
        for v in it.into_iter().enumerate() {
            self.process(v);
        }
    }
}

fn escape_str(s: &str, dispatch: fn(char) -> Value) -> Cow<str> {
    let mut p = B(s);
    p.extend(s.chars().map(dispatch));
    p.into_result()
}

/// Performs escaping of common XML characters inside an attribute value.
///
/// This function replaces several important markup characters with their
/// entity equivalents:
///
/// * `<` → `<`
/// * `>` → `>`
/// * `"` → `"`
/// * `'` → `'`
/// * `&` → `&`
///
/// The resulting string is safe to use inside XML attribute values or in PCDATA sections.
///
/// Does not perform allocations if the given string does not contain escapable characters.
#[inline]
pub fn escape_str_attribute(s: &str) -> Cow<str> {
    escape_str(s, Value::dispatch_for_attribute)
}

/// Performs escaping of common XML characters inside PCDATA.
///
/// This function replaces several important markup characters with their
/// entity equivalents:
///
/// * `<` → `<`
/// * `&` → `&`
///
/// The resulting string is safe to use inside PCDATA sections but NOT inside attribute values.
///
/// Does not perform allocations if the given string does not contain escapable characters.
#[inline]
pub fn escape_str_pcdata(s: &str) -> Cow<str> {
    escape_str(s, Value::dispatch_for_pcdata)
}
<commit_msg>Fix escaping multibyte string<commit_after>//! Contains functions for performing XML special characters escaping.

use std::borrow::Cow;

use self::Value::{C, S};
use self::Process::{B, O};

enum Value {
    C(char),
    S(&'static str)
}

impl Value {
    fn dispatch_for_attribute(c: char) -> Value {
        match c {
            '<'  => S("<"),
            '>'  => S(">"),
            '"'  => S("""),
            '\'' => S("'"),
            '&'  => S("&"),
            _    => C(c)
        }
    }

    fn dispatch_for_pcdata(c: char) -> Value {
        match c {
            '<'  => S("<"),
            '&'  => S("&"),
            _    => C(c)
        }
    }
}

enum Process<'a> {
    B(&'a str),
    O(String)
}

impl<'a> Process<'a> {
    fn process(&mut self, (i, next): (usize, Value)) {
        match next {
            S(s) => if let O(ref mut o) = *self {
                o.push_str(s);
            } else if let B(b) = *self {
                let mut r = String::with_capacity(b.len() + s.len());
                r.push_str(&b[..i]);
                r.push_str(s);
                *self = O(r);
            },
            C(c) => match *self {
                B(_) => {}
                O(ref mut o) => o.push(c)
            }
        }
    }

    fn into_result(self) -> Cow<'a, str> {
        match self {
            B(b) => Cow::Borrowed(b),
            O(o) => Cow::Owned(o)
        }
    }
}

impl<'a> Extend<(usize, Value)> for Process<'a> {
    fn extend<I: IntoIterator<Item=(usize, Value)>>(&mut self, it: I) {
        for v in it.into_iter() {
            self.process(v);
        }
    }
}

fn escape_str(s: &str, dispatch: fn(char) -> Value) -> Cow<str> {
    let mut p = B(s);
    p.extend(s.char_indices().map(|(ind, c)| (ind, dispatch(c))));
    p.into_result()
}

/// Performs escaping of common XML characters inside an attribute value.
///
/// This function replaces several important markup characters with their
/// entity equivalents:
///
/// * `<` → `<`
/// * `>` → `>`
/// * `"` → `"`
/// * `'` → `'`
/// * `&` → `&`
///
/// The resulting string is safe to use inside XML attribute values or in PCDATA sections.
///
/// Does not perform allocations if the given string does not contain escapable characters.
#[inline]
pub fn escape_str_attribute(s: &str) -> Cow<str> {
    escape_str(s, Value::dispatch_for_attribute)
}

/// Performs escaping of common XML characters inside PCDATA.
///
/// This function replaces several important markup characters with their
/// entity equivalents:
///
/// * `<` → `<`
/// * `&` → `&`
///
/// The resulting string is safe to use inside PCDATA sections but NOT inside attribute values.
///
/// Does not perform allocations if the given string does not contain escapable characters.
#[inline]
pub fn escape_str_pcdata(s: &str) -> Cow<str> {
    escape_str(s, Value::dispatch_for_pcdata)
}

#[test]
fn test_escape() {
    assert_eq!(escape_str_pcdata("☃<"), "☃<");
}
<|endoftext|>


<commit_before>use convert;

use syntax::ast::{Expr, Ident, TokenTree, Stmt};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::print::pprust;

// Copy from libsyntax
//
// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use syntax::diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

fn filter_tts_by_span(span: Span, tts: &[TokenTree]) -> Vec<TokenTree> {
    tts.iter()
        .filter(|tt| span.lo <= tt.get_span().lo && tt.get_span().hi <= span.hi)
        .cloned()
        .collect()
}

fn expr_prelude(cx: &ExtCtxt) -> Vec<P<Stmt>> {
    let use_stmt = quote_stmt!(cx, use std::io::{self, Write};);
    let fn_stmt = quote_stmt!(
        cx,
        fn inspect(mut vals: Vec<(i32, String)>, offset: i32, repr: &str) {
            fn width(s: &str) -> i32 { s.len() as i32 }
            fn align<T: Write>(writer: &mut T, cur: &mut i32, col: i32, s: &str) {
                while *cur < col {
                    let _ = write!(writer, " ");
                    *cur += 1;
                }
                let _ = write!(writer, "{}", s);
                *cur += width(s);
            }

            vals.sort();

            let mut err = io::stderr();
            let _ = writeln!(err, "{}", repr);
            {
                let mut cur = 0;
                for &(c, _) in &vals {
                    align(&mut err, &mut cur, offset + c, "|");
                }
                let _ = writeln!(err, "");
            }
            while !vals.is_empty() {
                let mut cur = 0;
                let mut i = 0;
                while i < vals.len() {
                    if i == vals.len() - 1 ||
                        vals[i].0 + width(&vals[i].1) < vals[i + 1].0 {
                            align(&mut err, &mut cur, offset + vals[i].0, &vals[i].1);
                            let _ = vals.remove(i);
                        } else {
                            align(&mut err, &mut cur, offset + vals[i].0, "|");
                            i += 1;
                        }
                }
                let _ = writeln!(err, "");
            }
        });

    vec![use_stmt.unwrap(), fn_stmt.unwrap()]
}

struct ExprGen {
    ident: Ident,
    tts: Vec<TokenTree>,
    expr: P<Expr>,
    ppstr: String
}

impl ExprGen {
    fn new(ident: &str, expr: P<Expr>, tts: &[TokenTree]) -> ExprGen {
        let tts = filter_tts_by_span(expr.span, tts);
        let ppstr = pprust::expr_to_string(&expr);

        ExprGen {
            ident: token::gensym_ident(ident),
            tts: tts,
            expr: expr,
            ppstr: ppstr
        }
    }

    fn ppstr(&self) -> &str { &self.ppstr }

    fn init_stmt(&self, cx: &mut ExtCtxt) -> P<Stmt> {
        let ident = self.ident;
        quote_stmt!(cx, let mut $ident = vec![];).unwrap()
    }

    fn converted_expr(&self, cx: &mut ExtCtxt) -> P<Expr> {
        convert::convert_expr(cx, self.expr.clone(), self.ident, &self.tts)
    }

    fn inspect_expr(&self, cx: &mut ExtCtxt, left: &str, right: &str) -> P<Expr> {
        let offset = left.len() as i32;
        let repr = format!("{}{}{}", left, self.ppstr, right);
        let ident = self.ident;
        quote_expr!(cx, inspect($ident, $offset, $repr))
    }
}

pub fn expand_assert(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
                 -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let cond_expr = parser.parse_expr();

    let msg_tts = if panictry!(parser.eat(&token::Token::Comma)) {
        let mut span = sp.clone();
        span.lo = parser.span.lo;
        Some(filter_tts_by_span(span, args))
    } else {
        None
    };

    let prelude = expr_prelude(cx);

    let gen = ExprGen::new("vals", cond_expr, args);
    let init = gen.init_stmt(cx);
    let inspect = gen.inspect_expr(cx, "power_assert!(", ")");
    let converted = gen.converted_expr(cx);

    let panic_expr = if let Some(tts) = msg_tts {
        quote_expr!(cx, panic!($tts))
    } else {
        let msg = format!("assertion failed: {}", gen.ppstr());
        quote_expr!(cx, panic!($msg))
    };

    let expr = quote_expr!(cx, {
        $init;
        let cond = $converted;
        if !cond {
            $prelude;
            $inspect;
            $panic_expr;
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}

pub fn expand_assert_eq(cx: &mut ExtCtxt, _sp: Span, args: &[TokenTree])
                    -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let lhs = parser.parse_expr();
    panictry!(parser.expect(&token::Token::Comma));
    let rhs = parser.parse_expr();

    let lhs_gen = ExprGen::new("lhs_vals", lhs, args);
    let rhs_gen = ExprGen::new("rhs_vals", rhs, args);

    let prelude = expr_prelude(cx);

    let lhs_init = lhs_gen.init_stmt(cx);
    let rhs_init = rhs_gen.init_stmt(cx);
    let lhs_inspect = lhs_gen.inspect_expr(cx, "left: ", "");
    let rhs_inspect = rhs_gen.inspect_expr(cx, "right: ", "");
    let lhs_converted = lhs_gen.converted_expr(cx);
    let rhs_converted = rhs_gen.converted_expr(cx);
    let assert_msg = format!("power_assert_eq!({}, {})",
                             lhs_gen.ppstr(), rhs_gen.ppstr());

    let expr = quote_expr!(cx, {
        $lhs_init;
        $rhs_init;
        match (&$lhs_converted, &$rhs_converted) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    $prelude;
                    let _ = writeln!(io::stderr(), $assert_msg);
                    $lhs_inspect;
                    $rhs_inspect;
                    panic!("assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`)", *left_val, *right_val);
                }
            }
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}
<commit_msg>Change assertion message to be shown as panic message<commit_after>use convert;

use syntax::ast::{Expr, Ident, TokenTree, Stmt};
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;
use syntax::ptr::P;
use syntax::print::pprust;

// Copy from libsyntax
//
// A variant of 'try!' that panics on Err(FatalError). This is used as a
// crutch on the way towards a non-panic!-prone parser. It should be used
// for fatal parsing errors; eventually we plan to convert all code using
// panictry to just use normal try
macro_rules! panictry {
    ($e:expr) => ({
        use std::result::Result::{Ok, Err};
        use syntax::diagnostic::FatalError;
        match $e {
            Ok(e) => e,
            Err(FatalError) => panic!(FatalError)
        }
    })
}

fn filter_tts_by_span(span: Span, tts: &[TokenTree]) -> Vec<TokenTree> {
    tts.iter()
        .filter(|tt| span.lo <= tt.get_span().lo && tt.get_span().hi <= span.hi)
        .cloned()
        .collect()
}

fn expr_prelude(cx: &ExtCtxt) -> Vec<P<Stmt>> {
    let use_stmt = quote_stmt!(cx, use std::io::Write;);
    let fn_stmt = quote_stmt!(
        cx,
        fn inspect<T: Write>(writer: &mut T, mut vals: Vec<(i32, String)>, offset: i32, repr: &str) {
            fn width(s: &str) -> i32 { s.len() as i32 }
            fn align<T: Write>(writer: &mut T, cur: &mut i32, col: i32, s: &str) {
                while *cur < col {
                    let _ = write!(writer, " ");
                    *cur += 1;
                }
                let _ = write!(writer, "{}", s);
                *cur += width(s);
            }

            vals.sort();

            let _ = writeln!(writer, "{}", repr);
            {
                let mut cur = 0;
                for &(c, _) in &vals {
                    align(writer, &mut cur, offset + c, "|");
                }
                let _ = writeln!(writer, "");
            }
            while !vals.is_empty() {
                let mut cur = 0;
                let mut i = 0;
                while i < vals.len() {
                    if i == vals.len() - 1 ||
                        vals[i].0 + width(&vals[i].1) < vals[i + 1].0 {
                            align(writer, &mut cur, offset + vals[i].0, &vals[i].1);
                            let _ = vals.remove(i);
                        } else {
                            align(writer, &mut cur, offset + vals[i].0, "|");
                            i += 1;
                        }
                }
                let _ = writeln!(writer, "");
            }
        });
    let write_stmt = quote_stmt!(cx, let mut write_buf = vec![];);

    vec![use_stmt.unwrap(), fn_stmt.unwrap(), write_stmt.unwrap()]
}

struct ExprGen {
    ident: Ident,
    tts: Vec<TokenTree>,
    expr: P<Expr>,
    ppstr: String
}

impl ExprGen {
    fn new(ident: &str, expr: P<Expr>, tts: &[TokenTree]) -> ExprGen {
        let tts = filter_tts_by_span(expr.span, tts);
        let ppstr = pprust::expr_to_string(&expr);

        ExprGen {
            ident: token::gensym_ident(ident),
            tts: tts,
            expr: expr,
            ppstr: ppstr
        }
    }

    fn ppstr(&self) -> &str { &self.ppstr }

    fn init_stmt(&self, cx: &mut ExtCtxt) -> P<Stmt> {
        let ident = self.ident;
        quote_stmt!(cx, let mut $ident = vec![];).unwrap()
    }

    fn converted_expr(&self, cx: &mut ExtCtxt) -> P<Expr> {
        convert::convert_expr(cx, self.expr.clone(), self.ident, &self.tts)
    }

    fn inspect_expr(&self, cx: &mut ExtCtxt, left: &str, right: &str) -> P<Expr> {
        let offset = left.len() as i32;
        let repr = format!("{}{}{}", left, self.ppstr, right);
        let ident = self.ident;
        quote_expr!(cx, inspect(&mut write_buf, $ident, $offset, $repr))
    }
}

pub fn expand_assert(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
                 -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let cond_expr = parser.parse_expr();

    let msg_tts = if panictry!(parser.eat(&token::Token::Comma)) {
        let mut span = sp.clone();
        span.lo = parser.span.lo;
        Some(filter_tts_by_span(span, args))
    } else {
        None
    };

    let prelude = expr_prelude(cx);

    let gen = ExprGen::new("vals", cond_expr, args);
    let init = gen.init_stmt(cx);
    let inspect = gen.inspect_expr(cx, "power_assert!(", ")");
    let converted = gen.converted_expr(cx);

    let panic_msg = if let Some(tts) = msg_tts {
        quote_expr!(cx, format!($tts))
    } else {
        let msg = format!("assertion failed: {}", gen.ppstr());
        quote_expr!(cx, $msg)
    };

    let expr = quote_expr!(cx, {
        $init;
        let cond = $converted;
        if !cond {
            $prelude;
            let _ = writeln!(&mut write_buf, $panic_msg);
            $inspect;
            panic!(String::from_utf8(write_buf).unwrap())
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}

pub fn expand_assert_eq(cx: &mut ExtCtxt, _sp: Span, args: &[TokenTree])
                    -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(args);
    let lhs = parser.parse_expr();
    panictry!(parser.expect(&token::Token::Comma));
    let rhs = parser.parse_expr();

    let lhs_gen = ExprGen::new("lhs_vals", lhs, args);
    let rhs_gen = ExprGen::new("rhs_vals", rhs, args);

    let prelude = expr_prelude(cx);

    let lhs_init = lhs_gen.init_stmt(cx);
    let rhs_init = rhs_gen.init_stmt(cx);
    let lhs_inspect = lhs_gen.inspect_expr(cx, "left: ", "");
    let rhs_inspect = rhs_gen.inspect_expr(cx, "right: ", "");
    let lhs_converted = lhs_gen.converted_expr(cx);
    let rhs_converted = rhs_gen.converted_expr(cx);
    let assert_msg = format!("power_assert_eq!({}, {})",
                             lhs_gen.ppstr(), rhs_gen.ppstr());

    let expr = quote_expr!(cx, {
        $lhs_init;
        $rhs_init;
        match (&$lhs_converted, &$rhs_converted) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    $prelude;
                    let _ = writeln!(&mut write_buf, "assertion failed: `(left == right)` \
                            (left: `{:?}`, right: `{:?}`)", *left_val, *right_val);
                    let _ = writeln!(&mut write_buf, $assert_msg);
                    $lhs_inspect;
                    $rhs_inspect;
                    panic!(String::from_utf8(write_buf).unwrap());
                }
            }
        }
    });

    // println!("{:?}", expr);

    MacEager::expr(expr)
}
<|endoftext|>


<commit_before>//! Checks for any invariants we expect to be true, and pages whoever is on call
//! if they are not.
//!
//! Usage:
//!     cargo run --bin monitor

#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use cargo_registry::{db, schema::*, util::Error};
use diesel::prelude::*;

fn main() -> Result<(), Error> {
    let conn = db::connect_now()?;

    check_stalled_background_jobs(&conn)?;
    check_stalled_update_downloads(&conn)?;
    check_spam_attack(&conn)?;
    Ok(())
}

/// Check for old background jobs that are not currently running
fn check_stalled_background_jobs(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use diesel::dsl::*;
    use diesel::sql_types::Integer;

    const EVENT_KEY: &str = "background_jobs";

    println!("Checking for stalled background jobs");

    let max_job_time = dotenv::var("MAX_JOB_TIME")
        .map(|s| s.parse::<i32>().unwrap())
        .unwrap_or(15);

    let stalled_job_count = background_jobs
        .select(1.into_sql::<Integer>())
        .filter(created_at.lt(now - max_job_time.minutes()))
        .for_update()
        .skip_locked()
        .load::<i32>(conn)?
        .len();

    let event = if stalled_job_count > 0 {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!(
                "{} jobs have been in the queue for more than {} minutes",
                stalled_job_count, max_job_time
            ),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No stalled background jobs".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

/// Check for an `update_downloads` job that has run longer than expected
fn check_stalled_update_downloads(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use chrono::{DateTime, NaiveDateTime, Utc};

    const EVENT_KEY: &str = "update_downloads_stalled";

    println!("Checking for stalled background jobs");

    let max_job_time = dotenv::var("MONITOR_MAX_UPDATE_DOWNLOADS_TIME")
        .map(|s| s.parse::<u32>().unwrap() as i64)
        .unwrap_or(120);

    let start_time = background_jobs
        .filter(job_type.eq("update_downloads"))
        .select(created_at)
        .first::<NaiveDateTime>(conn);

    if let Ok(start_time) = start_time {
        let start_time = DateTime::<Utc>::from_utc(start_time, Utc);
        let minutes = Utc::now().signed_duration_since(start_time).num_minutes();

        if minutes > max_job_time {
            return log_and_trigger_event(on_call::Event::Trigger {
                incident_key: Some(EVENT_KEY.into()),
                description: format!("update_downloads job running for {} minutes", minutes),
            });
        }
    };

    log_and_trigger_event(on_call::Event::Resolve {
        incident_key: EVENT_KEY.into(),
        description: Some("No stalled update_downloads job".into()),
    })
}

/// Check for known spam patterns
fn check_spam_attack(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::models::krate::canon_crate_name;
    use diesel::dsl::*;
    use diesel::sql_types::Bool;

    const EVENT_KEY: &str = "spam_attack";

    println!("Checking for crates indicating someone is spamming us");

    let bad_crate_names = dotenv::var("SPAM_CRATE_NAMES");
    let bad_crate_names: Vec<_> = bad_crate_names
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();
    let bad_author_patterns = dotenv::var("SPAM_AUTHOR_PATTERNS");
    let bad_author_patterns: Vec<_> = bad_author_patterns
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();

    let mut event_description = None;

    let bad_crate = crates::table
        .filter(canon_crate_name(crates::name).eq(any(bad_crate_names)))
        .select(crates::name)
        .first::<String>(conn)
        .optional()?;

    if let Some(bad_crate) = bad_crate {
        event_description = Some(format!("Crate named {} published", bad_crate));
    }

    let mut query = version_authors::table
        .select(version_authors::name)
        .filter(false.into_sql::<Bool>()) // Never return anything if we have no patterns
        .into_boxed();
    for author_pattern in bad_author_patterns {
        query = query.or_filter(version_authors::name.like(author_pattern));
    }
    let bad_author = query.first::<String>(conn).optional()?;

    if let Some(bad_author) = bad_author {
        event_description = Some(format!("Crate with author {} published", bad_author));
    }

    let event = if let Some(event_description) = event_description {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!("{}, possible spam attack underway", event_description,),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No spam crates detected".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn log_and_trigger_event(event: on_call::Event) -> Result<(), Error> {
    match event {
        on_call::Event::Trigger {
            ref description, ..
        } => println!("Paging on-call: {}", description),
        on_call::Event::Resolve {
            description: Some(ref description),
            ..
        } => println!("{}", description),
        _ => {} // noop
    }
    event.send()
}
<commit_msg>Improve comments and nomenclature in monitoring logic<commit_after>//! Checks for any invariants we expect to be true, and pages whoever is on call
//! if they are not.
//!
//! Usage:
//!     cargo run --bin monitor

#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use cargo_registry::{db, schema::*, util::Error};
use diesel::prelude::*;

fn main() -> Result<(), Error> {
    let conn = db::connect_now()?;

    check_failing_background_jobs(&conn)?;
    check_stalled_update_downloads(&conn)?;
    check_spam_attack(&conn)?;
    Ok(())
}

/// Check for old background jobs that are not currently running.
///
/// This check includes `skip_locked` in the query and will only trigger on
/// enqueued jobs that have attempted to run and have failed (and are in the
/// queue awaiting a retry).
///
/// Within the default 15 minute time, a job should have already had several
/// failed retry attempts.
fn check_failing_background_jobs(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use diesel::dsl::*;
    use diesel::sql_types::Integer;

    const EVENT_KEY: &str = "background_jobs";

    println!("Checking for failed background jobs");

    // Max job execution time in minutes
    let max_job_time = dotenv::var("MAX_JOB_TIME")
        .map(|s| s.parse::<i32>().unwrap())
        .unwrap_or(15);

    let stalled_job_count = background_jobs
        .select(1.into_sql::<Integer>())
        .filter(created_at.lt(now - max_job_time.minutes()))
        .for_update()
        .skip_locked()
        .load::<i32>(conn)?
        .len();

    let event = if stalled_job_count > 0 {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!(
                "{} jobs have been in the queue for more than {} minutes",
                stalled_job_count, max_job_time
            ),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No stalled background jobs".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

/// Check for an `update_downloads` job that has run longer than expected
fn check_stalled_update_downloads(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::schema::background_jobs::dsl::*;
    use chrono::{DateTime, NaiveDateTime, Utc};

    const EVENT_KEY: &str = "update_downloads_stalled";

    println!("Checking for stalled background jobs");

    // Max job execution time in minutes
    let max_job_time = dotenv::var("MONITOR_MAX_UPDATE_DOWNLOADS_TIME")
        .map(|s| s.parse::<u32>().unwrap() as i64)
        .unwrap_or(120);

    let start_time = background_jobs
        .filter(job_type.eq("update_downloads"))
        .select(created_at)
        .first::<NaiveDateTime>(conn);

    if let Ok(start_time) = start_time {
        let start_time = DateTime::<Utc>::from_utc(start_time, Utc);
        let minutes = Utc::now().signed_duration_since(start_time).num_minutes();

        if minutes > max_job_time {
            return log_and_trigger_event(on_call::Event::Trigger {
                incident_key: Some(EVENT_KEY.into()),
                description: format!("update_downloads job running for {} minutes", minutes),
            });
        }
    };

    log_and_trigger_event(on_call::Event::Resolve {
        incident_key: EVENT_KEY.into(),
        description: Some("No stalled update_downloads job".into()),
    })
}

/// Check for known spam patterns
fn check_spam_attack(conn: &PgConnection) -> Result<(), Error> {
    use cargo_registry::models::krate::canon_crate_name;
    use diesel::dsl::*;
    use diesel::sql_types::Bool;

    const EVENT_KEY: &str = "spam_attack";

    println!("Checking for crates indicating someone is spamming us");

    let bad_crate_names = dotenv::var("SPAM_CRATE_NAMES");
    let bad_crate_names: Vec<_> = bad_crate_names
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();
    let bad_author_patterns = dotenv::var("SPAM_AUTHOR_PATTERNS");
    let bad_author_patterns: Vec<_> = bad_author_patterns
        .as_ref()
        .map(|s| s.split(',').collect())
        .unwrap_or_default();

    let mut event_description = None;

    let bad_crate = crates::table
        .filter(canon_crate_name(crates::name).eq(any(bad_crate_names)))
        .select(crates::name)
        .first::<String>(conn)
        .optional()?;

    if let Some(bad_crate) = bad_crate {
        event_description = Some(format!("Crate named {} published", bad_crate));
    }

    let mut query = version_authors::table
        .select(version_authors::name)
        .filter(false.into_sql::<Bool>()) // Never return anything if we have no patterns
        .into_boxed();
    for author_pattern in bad_author_patterns {
        query = query.or_filter(version_authors::name.like(author_pattern));
    }
    let bad_author = query.first::<String>(conn).optional()?;

    if let Some(bad_author) = bad_author {
        event_description = Some(format!("Crate with author {} published", bad_author));
    }

    let event = if let Some(event_description) = event_description {
        on_call::Event::Trigger {
            incident_key: Some(EVENT_KEY.into()),
            description: format!("{}, possible spam attack underway", event_description,),
        }
    } else {
        on_call::Event::Resolve {
            incident_key: EVENT_KEY.into(),
            description: Some("No spam crates detected".into()),
        }
    };

    log_and_trigger_event(event)?;
    Ok(())
}

fn log_and_trigger_event(event: on_call::Event) -> Result<(), Error> {
    match event {
        on_call::Event::Trigger {
            ref description, ..
        } => println!("Paging on-call: {}", description),
        on_call::Event::Resolve {
            description: Some(ref description),
            ..
        } => println!("{}", description),
        _ => {} // noop
    }
    event.send()
}
<|endoftext|>


<commit_before>// Just instantiate some data structures to make sure we got all their foreign items covered.
// Requires full MIR on Windows.

use std::sync;

fn main() {
    let m = sync::Mutex::new(0);
    drop(m.lock());
    drop(m);

    // We don't provide RwLock on Windows
    #[cfg(not(target_os = "windows"))]
    {
        let rw = sync::RwLock::new(0);
        drop(rw.read());
        drop(rw.write());
        drop(rw);
    }
}
<commit_msg>improve comment<commit_after>// Just instantiate some data structures to make sure we got all their foreign items covered.
// Requires full MIR on Windows.

use std::sync;

fn main() {
    let m = sync::Mutex::new(0);
    drop(m.lock());
    drop(m);

    #[cfg(not(target_os = "windows"))] // TODO: implement RwLock on Windows
    {
        let rw = sync::RwLock::new(0);
        drop(rw.read());
        drop(rw.write());
        drop(rw);
    }
}
<|endoftext|>


<commit_before>use log::trace;
use amq_protocol::frame::AMQPFrame;
use parking_lot::Mutex;

use std::{
  collections::{VecDeque, HashMap},
  sync::Arc,
};

use crate::{
  channel::Reply,
  connection_status::ConnectionState,
  id_sequence::IdSequence,
  wait::{Wait, WaitHandle},
  error::ErrorKind,
};

pub(crate) type SendId = u64;

#[derive(Clone, Debug)]
pub(crate) enum Priority {
  LOW,
  NORMAL,
  CRITICAL,
}

impl Default for Priority {
  fn default() -> Self {
    Priority::NORMAL
  }
}

#[derive(Clone, Debug, Default)]
pub(crate) struct Frames {
  inner: Arc<Mutex<Inner>>,
}

impl Frames {
  pub(crate) fn push(&self, channel_id: u16, priority: Priority, frame: AMQPFrame, expected_reply: Option<Reply>) -> Wait<()> {
    self.inner.lock().push(channel_id, priority, frame, expected_reply)
  }

  pub(crate) fn retry(&self, send_id: SendId, frame: AMQPFrame) {
    self.inner.lock().priority_frames.push_back((send_id, frame))
  }

  pub(crate) fn pop(&self, flow: bool) -> Option<(SendId, AMQPFrame)> {
    self.inner.lock().pop(flow)
  }

  pub(crate) fn next_expected_reply(&self, channel_id: u16) -> Option<Reply> {
    self.inner.lock().expected_replies.get_mut(&channel_id).and_then(|replies| replies.pop_front())
  }

  pub(crate) fn clear_expected_replies(&self, channel_id: u16) {
    self.inner.lock().expected_replies.remove(&channel_id);
  }

  pub(crate) fn mark_sent(&self, send_id: SendId) {
    if let Some(send) = self.inner.lock().outbox.remove(&send_id) {
      send.finish(());
    }
  }

  pub(crate) fn drop_pending(&self) {
    self.inner.lock().drop_pending();
  }

  pub(crate) fn clear_with_error(&self) {
    for (_, wait_handle) in self.inner.lock().outbox.drain() {
      wait_handle.error(ErrorKind::InvalidConnectionState(ConnectionState::Error).into())
    }
  }
}

#[derive(Debug)]
struct Inner {
  priority_frames:  VecDeque<(SendId, AMQPFrame)>,
  frames:           VecDeque<(SendId, AMQPFrame)>,
  low_prio_frames:  VecDeque<(SendId, AMQPFrame)>,
  expected_replies: HashMap<u16, VecDeque<Reply>>,
  outbox:           HashMap<SendId, WaitHandle<()>>,
  send_id:          IdSequence<SendId>,
}

impl Default for Inner {
  fn default() -> Self {
    Self {
      priority_frames:  VecDeque::default(),
      frames:           VecDeque::default(),
      low_prio_frames:  VecDeque::default(),
      expected_replies: HashMap::default(),
      outbox:           HashMap::default(),
      send_id:          IdSequence::new(false),
    }
  }
}

impl Inner {
  fn push(&mut self, channel_id: u16, priority: Priority, frame: AMQPFrame, expected_reply: Option<Reply>) -> Wait<()> {
    let send_id = if let Priority::CRITICAL = priority { 0 } else { self.send_id.next() };
    match priority {
      Priority::LOW      => self.low_prio_frames.push_back((send_id, frame)),
      Priority::NORMAL   => self.frames.push_back((send_id, frame)),
      Priority::CRITICAL => self.priority_frames.push_front((send_id, frame)),
    }
    let (wait, wait_handle) = Wait::new();
    self.outbox.insert(send_id, wait_handle);
    if let Some(reply) = expected_reply {
      trace!("channel {} state is now waiting for {:?}", channel_id, reply);
      self.expected_replies.entry(channel_id).or_default().push_back(reply);
    }
    wait
  }

  fn pop(&mut self, flow: bool) -> Option<(SendId, AMQPFrame)> {
    self.priority_frames.pop_front().or_else(|| self.frames.pop_front()).or_else(|| if flow { self.low_prio_frames.pop_front() } else { None })
  }

  fn drop_pending(&mut self) {
    self.priority_frames.clear();
    self.frames.clear();
    self.low_prio_frames.clear();
    self.expected_replies.clear();
    for (_, wait_handle) in self.outbox.drain() {
      wait_handle.finish(());
    }
  }
}
<commit_msg>frames: store channel id<commit_after>use log::trace;
use amq_protocol::frame::AMQPFrame;
use parking_lot::Mutex;

use std::{
  collections::{VecDeque, HashMap},
  sync::Arc,
};

use crate::{
  channel::Reply,
  connection_status::ConnectionState,
  id_sequence::IdSequence,
  wait::{Wait, WaitHandle},
  error::ErrorKind,
};

pub(crate) type SendId = u64;

#[derive(Clone, Debug)]
pub(crate) enum Priority {
  LOW,
  NORMAL,
  CRITICAL,
}

impl Default for Priority {
  fn default() -> Self {
    Priority::NORMAL
  }
}

#[derive(Clone, Debug, Default)]
pub(crate) struct Frames {
  inner: Arc<Mutex<Inner>>,
}

impl Frames {
  pub(crate) fn push(&self, channel_id: u16, priority: Priority, frame: AMQPFrame, expected_reply: Option<Reply>) -> Wait<()> {
    self.inner.lock().push(channel_id, priority, frame, expected_reply)
  }

  pub(crate) fn retry(&self, send_id: SendId, frame: AMQPFrame) {
    self.inner.lock().priority_frames.push_back((send_id, frame))
  }

  pub(crate) fn pop(&self, flow: bool) -> Option<(SendId, AMQPFrame)> {
    self.inner.lock().pop(flow)
  }

  pub(crate) fn next_expected_reply(&self, channel_id: u16) -> Option<Reply> {
    self.inner.lock().expected_replies.get_mut(&channel_id).and_then(|replies| replies.pop_front())
  }

  pub(crate) fn clear_expected_replies(&self, channel_id: u16) {
    self.inner.lock().expected_replies.remove(&channel_id);
  }

  pub(crate) fn mark_sent(&self, send_id: SendId) {
    if let Some((_, send)) = self.inner.lock().outbox.remove(&send_id) {
      send.finish(());
    }
  }

  pub(crate) fn drop_pending(&self) {
    self.inner.lock().drop_pending();
  }

  pub(crate) fn clear_with_error(&self) {
    for (_, (_, wait_handle)) in self.inner.lock().outbox.drain() {
      wait_handle.error(ErrorKind::InvalidConnectionState(ConnectionState::Error).into())
    }
  }
}

#[derive(Debug)]
struct Inner {
  priority_frames:  VecDeque<(SendId, AMQPFrame)>,
  frames:           VecDeque<(SendId, AMQPFrame)>,
  low_prio_frames:  VecDeque<(SendId, AMQPFrame)>,
  expected_replies: HashMap<u16, VecDeque<Reply>>,
  outbox:           HashMap<SendId, (u16, WaitHandle<()>)>,
  send_id:          IdSequence<SendId>,
}

impl Default for Inner {
  fn default() -> Self {
    Self {
      priority_frames:  VecDeque::default(),
      frames:           VecDeque::default(),
      low_prio_frames:  VecDeque::default(),
      expected_replies: HashMap::default(),
      outbox:           HashMap::default(),
      send_id:          IdSequence::new(false),
    }
  }
}

impl Inner {
  fn push(&mut self, channel_id: u16, priority: Priority, frame: AMQPFrame, expected_reply: Option<Reply>) -> Wait<()> {
    let send_id = if let Priority::CRITICAL = priority { 0 } else { self.send_id.next() };
    match priority {
      Priority::LOW      => self.low_prio_frames.push_back((send_id, frame)),
      Priority::NORMAL   => self.frames.push_back((send_id, frame)),
      Priority::CRITICAL => self.priority_frames.push_front((send_id, frame)),
    }
    let (wait, wait_handle) = Wait::new();
    self.outbox.insert(send_id, (channel_id, wait_handle));
    if let Some(reply) = expected_reply {
      trace!("channel {} state is now waiting for {:?}", channel_id, reply);
      self.expected_replies.entry(channel_id).or_default().push_back(reply);
    }
    wait
  }

  fn pop(&mut self, flow: bool) -> Option<(SendId, AMQPFrame)> {
    self.priority_frames.pop_front().or_else(|| self.frames.pop_front()).or_else(|| if flow { self.low_prio_frames.pop_front() } else { None })
  }

  fn drop_pending(&mut self) {
    self.priority_frames.clear();
    self.frames.clear();
    self.low_prio_frames.clear();
    self.expected_replies.clear();
    for (_, (_, wait_handle)) in self.outbox.drain() {
      wait_handle.finish(());
    }
  }
}
<|endoftext|>


<commit_before>
use std::io::Read;  // Trait providing read_to_string()

use hyper;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: hyper::Client::new()
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token)
    }

    pub fn get(&self, command: &str) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url(&command);
        self.client.get(&url).send()
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        self.get("version")
    }

    pub fn projects(&self) {
        let mut res: hyper::client::Response = self.get("projects").unwrap();
        println!("####################################################################");
        println!("res: {:?}", res);
        println!("####################################################################");
        println!("Response: {}", res.status);
        println!("####################################################################");
        println!("Headers:\n{}", res.headers);
        println!("####################################################################");

        println!("####################################################################");
        let mut body = String::new();
        println!("####################################################################");
        let result = res.read_to_string(&mut body);
        println!("####################################################################");
        println!("body:\n{}", body);
    }

    // pub fn projects(&self) -> ProjectManager {
    //     ProjectManager()
    // }
}

#[cfg(test)]
mod tests {
    use std::env;
    use gitlab::GitLab;

    #[test]
    fn list_projects() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let gl = GitLab::new_https("gitlab.com", &token);
        println!("gl: {:?}", gl);

        gl.projects();
        // assert_eq!(gl.attempt_connection().unwrap().status, hyper::Ok);

    }
}
<commit_msg>Close connection after each GET to fix the "deadlock". Was only a timeout.<commit_after>
use std::io::Read;  // Trait providing read_to_string()

use hyper;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
}

impl GitLab {

    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port:   port,
            private_token: private_token.to_string(),
            client: hyper::Client::new()
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}",
                                self.scheme,
                                self.domain,
                                self.port,
                                API_VERSION,
                                command,
                                self.private_token)
    }

    pub fn get(&self, command: &str) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url(&command);
        // Close connections after each GET.
        self.client.get(&url).header(hyper::header::Connection::close()).send()
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        self.get("version")
    }

    pub fn projects(&self) {
        let mut res: hyper::client::Response = self.get("projects").unwrap();
        println!("####################################################################");
        println!("res: {:?}", res);
        println!("####################################################################");
        println!("Response: {}", res.status);
        println!("####################################################################");
        println!("Headers:\n{}", res.headers);
        println!("####################################################################");

        println!("####################################################################");
        let mut body = String::new();
        println!("####################################################################");
        let result = res.read_to_string(&mut body);
        println!("####################################################################");
        println!("body:\n{}", body);
    }

    // pub fn projects(&self) -> ProjectManager {
    //     ProjectManager()
    // }
}

#[cfg(test)]
mod tests {
    use std::env;
    use gitlab::GitLab;

    #[test]
    fn list_projects() {
        let token = match env::var("GITLAB_TOKEN") {
            Ok(val) => val,
            Err(_)  => panic!("Please set environment variable 'GITLAB_TOKEN'"),
        };

        let gl = GitLab::new_https("gitlab.com", &token);
        println!("gl: {:?}", gl);

        gl.projects();
        // assert_eq!(gl.attempt_connection().unwrap().status, hyper::Ok);

    }
}
<|endoftext|>


<commit_before>use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::BufferedStream;
use std::string::String;

fn main () {

    let listener = TcpListener::bind("127.0.0.1", 8099).unwrap();
    let mut acceptor = listener.listen().unwrap();

    fn handle_client(mut stream: BufferedStream<TcpStream>) {
        let body = String::from_str("
Hi, World!
");
        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\n").unwrap(); // byte literal
        stream.write(b"Content-type: text/html\n").unwrap();
        stream.write(b"X-header: from bytes\n");
        stream.write(body_length.into_bytes().as_slice()).unwrap();
        stream.write(b"\n\n").unwrap();
        stream.write(body.into_bytes().as_slice()).unwrap();
        stream.flush().unwrap();

        println!("Handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<commit_msg>echo first sent line<commit_after>use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::BufferedStream;
use std::string::String;

fn main () {

    let listener = TcpListener::bind("127.0.0.1", 8099).unwrap();
    let mut acceptor = listener.listen().unwrap();

    fn handle_client(mut stream: BufferedStream<TcpStream>) {

        let mut body: String = stream.read_line().unwrap();
        body = body + "
You sent it!
";

        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\n").unwrap(); // byte literal
        stream.write(b"Content-type: text/html\n").unwrap();
        stream.write(b"X-header: from bytes\n");
        stream.write(body_length.into_bytes().as_slice()).unwrap();
        stream.write(b"\n\n").unwrap();
        stream.write(body.into_bytes().as_slice()).unwrap();
        stream.flush().unwrap();

        println!("Handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<|endoftext|>


<commit_before>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

mod entity_generator;
mod header_generator;
mod object_generator;
mod other_helpers;
mod table_generator;
mod test_helper_generator;
mod xml_helpers;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

include!("../src/expected_type.rs");

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let generated_dir = Path::new(&out_dir).join("generated");
    let _ = std::fs::create_dir(&generated_dir); // might fail if it's already there

    let mut file = File::create(generated_dir.join("mod.rs")).ok().unwrap();
    file.write_all("// The contents of this file are automatically generated and should not be modified directly.  See the `build` directory.

pub mod entities;
pub mod header;
pub mod objects;
pub mod tables;
".as_bytes()).ok().unwrap();

    entity_generator::generate_entities(&generated_dir);
    header_generator::generate_header(&generated_dir);
    object_generator::generate_objects(&generated_dir);
    table_generator::generate_tables(&generated_dir);

    test_helper_generator::generate_test_helpers(&generated_dir);

    // only watch the `build/` and `spec/` directories
    println!("cargo:rerun-if-changed=build/");
    println!("cargo:rerun-if-changed=spec/");
}
<commit_msg>be explicit with rerunning the build script<commit_after>// Copyright (c) IxMilia.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

mod entity_generator;
mod header_generator;
mod object_generator;
mod other_helpers;
mod table_generator;
mod test_helper_generator;
mod xml_helpers;

use std::env;
use std::error::Error;
use std::fmt::Debug;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;

include!("../src/expected_type.rs");

fn main() -> Result<(), Box<Error>> {
    // watch everything under the `build/` and `spec/` directories and also one specific file
    rerun_if_changed("src/expected_type.rs");
    let dirs_to_watch = vec!["build/", "spec/"];
    for sub_dir in dirs_to_watch {
        rerun_if_changed(sub_dir);
        let path = Path::new(&sub_dir);
        for entry in fs::read_dir(path)? {
            let entry = entry?;
            if entry.file_type()?.is_file() {
                rerun_if_changed(&entry.path().as_os_str());
            }
        }
    }

    let out_dir = env::var("OUT_DIR").unwrap();
    let generated_dir = Path::new(&out_dir).join("generated");
    let _ = std::fs::create_dir(&generated_dir); // might fail if it's already there

    let mut file = File::create(generated_dir.join("mod.rs")).ok().unwrap();
    file.write_all("// The contents of this file are automatically generated and should not be modified directly.  See the `build` directory.

pub mod entities;
pub mod header;
pub mod objects;
pub mod tables;
".as_bytes()).ok().unwrap();

    entity_generator::generate_entities(&generated_dir);
    header_generator::generate_header(&generated_dir);
    object_generator::generate_objects(&generated_dir);
    table_generator::generate_tables(&generated_dir);

    test_helper_generator::generate_test_helpers(&generated_dir);

    Ok(())
}

fn rerun_if_changed<S: Debug + ?Sized>(s: &S) {
    let s = format!("{:?}", s)
        .replace("\\\\", "/") // normalize directory separators
        .replace("\"", ""); // ignore surrounding quotes
    println!("cargo:rerun-if-changed={}", s);
}
<|endoftext|>


<commit_before>use parser_combinators::primitives::{Stream, State, Parser};
use parser_combinators::{ParseResult, parser};
use parser_combinators::combinator::{optional, ParserExt, sep_by, many, many1};

use super::Block;
use super::token::{Token, ParseToken};
use super::token::TokenType as Tok;
use super::token::lift;

#[derive(Debug, Clone)]
pub struct Param {
    pub name: String,
    pub default_value: Option<String>,
}

#[derive(Debug, Clone)]
pub enum Expression {
    Name(String),
    New(Box<Expression>),
    Attr(Box<Expression>, String),
    Call(Box<Expression>, Vec<Expression>),
}

#[derive(Debug, Clone)]
pub enum LinkDest {
    Stream(Expression),
    Mapping(Expression, Expression),
}

#[derive(Debug, Clone)]
pub enum Link {
    One(String, LinkDest),
    Multi(Vec<(String, Option<String>)>, LinkDest),
}

#[derive(Debug, Clone)]
pub enum Statement {
    Element {
        name: String,
        classes: Vec<String>,
        attributes: Vec<(String, Expression)>,
        body: Vec<Statement>,
    },
    Text(String),
    Store(String, Expression),
    Link(Vec<Link>),
    Condition(Vec<(Expression, Statement)>, Option<Box<Statement>>),
}

fn param<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Param, I>
{
    lift(Tok::Ident).and(optional(lift(Tok::Equals)
        .with(lift(Tok::String))))  // TODO(tailhook) more expressions
    .map(|((_, name, _), opt)| Param {
        name: String::from(name),
        default_value: opt.map(|x| String::from(x.1)),
    })
    .parse_state(input)
}

fn element_start<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<(String, Vec<String>), I>
{
    let element_head = lift(Tok::Ident)
        .map(ParseToken::into_string)
        .and(many::<Vec<_>, _>(lift(Tok::Dot)
            .with(lift(Tok::Ident).map(ParseToken::into_string))));
    let div_head = many1::<Vec<_>, _>(lift(Tok::Dot)
            .with(lift(Tok::Ident).map(ParseToken::into_string)))
        .map(|items| (String::from("div"), items));
    element_head
    .or(div_head)
    .parse_state(input)
}

fn attributes<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<(String, Expression)>>, I>
{
    optional(lift(Tok::OpenBracket)
        .with(sep_by::<Vec<_>, _, _>(
            lift(Tok::Ident).map(ParseToken::into_string)
                .skip(lift(Tok::Equals))
                .and(parser(expression)),
            lift(Tok::Comma)))
        .skip(lift(Tok::CloseBracket)))
    .parse_state(input)
}

fn element<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    parser(element_start)
    .and(parser(attributes))
    .skip(lift(Tok::Newline))
    .and(optional(lift(Tok::Indent)
        .with(many1(parser(statement)))
        .skip(lift(Tok::Dedent))))
    .map(|(((name, classes), opt_attributes), opt_body)| Statement::Element {
        name: name,
        classes: classes,
        attributes: opt_attributes.unwrap_or(vec!()),
        body: opt_body.unwrap_or(vec!()),
        })
    .parse_state(input)
}

fn literal<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::String).skip(lift(Tok::Newline))
    .map(|tok| Statement::Text(tok.unescape()))
    .parse_state(input)
}

fn expr_params<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<Expression>>, I>
{
    optional(lift(Tok::OpenParen)
        .with(sep_by::<Vec<_>, _, _>(
            parser(expression),
            lift(Tok::Comma)))
        .skip(lift(Tok::CloseParen)))
    .parse_state(input)
}

fn expression<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Expression, I>
{
    sep_by::<Vec<_>, _, _>(
        lift(Tok::Ident).map(ParseToken::into_string),
        lift(Tok::Dot))
    .map(|vec| {
        vec[..vec.len()-1].iter().rev().fold(
            Expression::Name(vec[vec.len()-1].clone()),
            |expr, name| Expression::Attr(Box::new(expr), name.clone()))
    })
    .and(parser(expr_params))
    .map(|(mut expr, opt_paren)| {
        if let Some(paren) = opt_paren {
            expr = Expression::Call(Box::new(expr), paren);
        }
        expr
    })
    .or(lift(Tok::New).with(parser(expression))
        .map(|x| Expression::New(Box::new(x))))
    .parse_state(input)
}

fn store<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::Store)
    .with(lift(Tok::Ident).map(ParseToken::into_string))
    .and(parser(expression)).skip(lift(Tok::Newline))
    .map(|(name, value)| Statement::Store(name, value))
    .parse_state(input)
}

fn link_dest<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<LinkDest, I>
{
    parser(expression)
    .and(optional(lift(Tok::ArrowRight)
                  .with(parser(expression))))
    .map(|(x, y)| match y {
        Some(dest) => LinkDest::Mapping(x, dest),
        None => LinkDest::Stream(x),
    })
    .parse_state(input)
}

fn multi_link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Link, I>
{
    lift(Tok::OpenBrace)
    .with(sep_by::<Vec<_>, _, _>(
        lift(Tok::Ident).map(ParseToken::into_string)
        .and(optional(lift(Tok::Colon).with(
            lift(Tok::Ident).map(ParseToken::into_string)))),
        lift(Tok::Comma)))
    .skip(lift(Tok::Equals))
    .and(parser(link_dest))
    .map(|(lst, dest)| Link::Multi(lst, dest))
    .parse_state(input)
}

fn single_link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Link, I>
{
    lift(Tok::Ident)
    .skip(lift(Tok::Equals))
    .and(parser(link_dest))
    .map(|(name, dest)| Link::One(name.into_string(), dest))
    .parse_state(input)
}

fn link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::Link)
    .with(sep_by::<Vec<_>, _, _>(
        parser(single_link).or(parser(multi_link)),
        lift(Tok::Comma)))
    .map(Statement::Link)
    .parse_state(input)
}

fn condition<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::If)
    .with(parser(expression))
    .skip(Tok::Colon)
    .skip(Tok::Newline)
}

fn statement<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    parser(element)
    .or(parser(literal))
    .or(parser(store))
    .or(parser(link))
    .or(parser(condition))
    .parse_state(input)
}

fn params<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<Param>>, I>
{
    optional(lift(Tok::OpenParen)
        .with(sep_by::<Vec<_>, _, _>(parser(param), lift(Tok::Comma)))
        .skip(lift(Tok::CloseParen)))
    .parse_state(input)
}

fn events<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<String>>, I>
{
    optional(lift(Tok::Events)
        .with(sep_by::<Vec<_>, _, _>(
            lift(Tok::Ident).map(ParseToken::into_string),
            lift(Tok::Comma),
        )))
    .parse_state(input)
}

pub fn block<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Block, I>
{
    lift(Tok::Ident).map(ParseToken::into_string)
    .and(parser(params))
    .and(parser(events))
    .skip(lift(Tok::Colon))
    .skip(lift(Tok::Newline))
    .and(optional(
        lift(Tok::Indent)
        .with(many1(parser(statement)))
        .skip(lift(Tok::Dedent))))
    .map(|(((name, opt_params), opt_events), opt_stmtlist)| {
        Block::Html {
            name: name,
            params: opt_params.unwrap_or(vec!()),
            events: opt_events.unwrap_or(vec!()),
            statements: opt_stmtlist.unwrap_or(vec!()),
        }
    })
    .parse_state(input)
}

<commit_msg>Implement parsing of conditionals (ifs)<commit_after>use parser_combinators::primitives::{Stream, State, Parser};
use parser_combinators::{ParseResult, parser};
use parser_combinators::combinator::{optional, ParserExt, sep_by, many, many1};

use super::Block;
use super::token::{Token, ParseToken};
use super::token::TokenType as Tok;
use super::token::lift;

#[derive(Debug, Clone)]
pub struct Param {
    pub name: String,
    pub default_value: Option<String>,
}

#[derive(Debug, Clone)]
pub enum Expression {
    Name(String),
    New(Box<Expression>),
    Attr(Box<Expression>, String),
    Call(Box<Expression>, Vec<Expression>),
}

#[derive(Debug, Clone)]
pub enum LinkDest {
    Stream(Expression),
    Mapping(Expression, Expression),
}

#[derive(Debug, Clone)]
pub enum Link {
    One(String, LinkDest),
    Multi(Vec<(String, Option<String>)>, LinkDest),
}

#[derive(Debug, Clone)]
pub enum Statement {
    Element {
        name: String,
        classes: Vec<String>,
        attributes: Vec<(String, Expression)>,
        body: Vec<Statement>,
    },
    Text(String),
    Store(String, Expression),
    Link(Vec<Link>),
    Condition(Vec<(Expression, Vec<Statement>)>, Option<Vec<Statement>>),
}

fn param<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Param, I>
{
    lift(Tok::Ident).and(optional(lift(Tok::Equals)
        .with(lift(Tok::String))))  // TODO(tailhook) more expressions
    .map(|((_, name, _), opt)| Param {
        name: String::from(name),
        default_value: opt.map(|x| String::from(x.1)),
    })
    .parse_state(input)
}

fn element_start<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<(String, Vec<String>), I>
{
    let element_head = lift(Tok::Ident)
        .map(ParseToken::into_string)
        .and(many::<Vec<_>, _>(lift(Tok::Dot)
            .with(lift(Tok::Ident).map(ParseToken::into_string))));
    let div_head = many1::<Vec<_>, _>(lift(Tok::Dot)
            .with(lift(Tok::Ident).map(ParseToken::into_string)))
        .map(|items| (String::from("div"), items));
    element_head
    .or(div_head)
    .parse_state(input)
}

fn attributes<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<(String, Expression)>>, I>
{
    optional(lift(Tok::OpenBracket)
        .with(sep_by::<Vec<_>, _, _>(
            lift(Tok::Ident).map(ParseToken::into_string)
                .skip(lift(Tok::Equals))
                .and(parser(expression)),
            lift(Tok::Comma)))
        .skip(lift(Tok::CloseBracket)))
    .parse_state(input)
}

fn element<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    parser(element_start)
    .and(parser(attributes))
    .skip(lift(Tok::Newline))
    .and(parser(chunk))
    .map(|(((name, classes), opt_attributes), opt_body)| Statement::Element {
        name: name,
        classes: classes,
        attributes: opt_attributes.unwrap_or(vec!()),
        body: opt_body.unwrap_or(vec!()),
        })
    .parse_state(input)
}

fn literal<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::String).skip(lift(Tok::Newline))
    .map(|tok| Statement::Text(tok.unescape()))
    .parse_state(input)
}

fn expr_params<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<Expression>>, I>
{
    optional(lift(Tok::OpenParen)
        .with(sep_by::<Vec<_>, _, _>(
            parser(expression),
            lift(Tok::Comma)))
        .skip(lift(Tok::CloseParen)))
    .parse_state(input)
}

fn expression<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Expression, I>
{
    sep_by::<Vec<_>, _, _>(
        lift(Tok::Ident).map(ParseToken::into_string),
        lift(Tok::Dot))
    .map(|vec| {
        vec[..vec.len()-1].iter().rev().fold(
            Expression::Name(vec[vec.len()-1].clone()),
            |expr, name| Expression::Attr(Box::new(expr), name.clone()))
    })
    .and(parser(expr_params))
    .map(|(mut expr, opt_paren)| {
        if let Some(paren) = opt_paren {
            expr = Expression::Call(Box::new(expr), paren);
        }
        expr
    })
    .or(lift(Tok::New).with(parser(expression))
        .map(|x| Expression::New(Box::new(x))))
    .parse_state(input)
}

fn store<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::Store)
    .with(lift(Tok::Ident).map(ParseToken::into_string))
    .and(parser(expression)).skip(lift(Tok::Newline))
    .map(|(name, value)| Statement::Store(name, value))
    .parse_state(input)
}

fn link_dest<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<LinkDest, I>
{
    parser(expression)
    .and(optional(lift(Tok::ArrowRight)
                  .with(parser(expression))))
    .map(|(x, y)| match y {
        Some(dest) => LinkDest::Mapping(x, dest),
        None => LinkDest::Stream(x),
    })
    .parse_state(input)
}

fn multi_link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Link, I>
{
    lift(Tok::OpenBrace)
    .with(sep_by::<Vec<_>, _, _>(
        lift(Tok::Ident).map(ParseToken::into_string)
        .and(optional(lift(Tok::Colon).with(
            lift(Tok::Ident).map(ParseToken::into_string)))),
        lift(Tok::Comma)))
    .skip(lift(Tok::Equals))
    .and(parser(link_dest))
    .map(|(lst, dest)| Link::Multi(lst, dest))
    .parse_state(input)
}

fn single_link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Link, I>
{
    lift(Tok::Ident)
    .skip(lift(Tok::Equals))
    .and(parser(link_dest))
    .map(|(name, dest)| Link::One(name.into_string(), dest))
    .parse_state(input)
}

fn link<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::Link)
    .with(sep_by::<Vec<_>, _, _>(
        parser(single_link).or(parser(multi_link)),
        lift(Tok::Comma)))
    .map(Statement::Link)
    .parse_state(input)
}

fn condition<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    lift(Tok::If)
    .with(parser(expression))
    .skip(lift(Tok::Colon))
    .skip(lift(Tok::Newline))
    .and(parser(chunk))
    .and(optional(many::<Vec<_>,_>(
        lift(Tok::Elif)
        .with(parser(expression))
        .skip(lift(Tok::Colon))
        .skip(lift(Tok::Newline))
        .and(parser(chunk))
        )))
    .and(optional(lift(Tok::Else)
        .skip(lift(Tok::Colon))
        .skip(lift(Tok::Newline))
        .with(parser(chunk))
        ))
    .map(|(((cond, body), opt_elifs), opt_else)| Statement::Condition(
        vec![(cond, body.unwrap_or(vec!()))]
        .into_iter()
        .chain(opt_elifs.map(
            |v| v.into_iter()
                 .map(|(expr, opt_body)| (expr, opt_body.unwrap_or(vec!())))
                 .collect()
            ).unwrap_or(vec!()).into_iter())
        .collect(),
        opt_else.and_then(|x| x)))
    .parse_state(input)
}

fn statement<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Statement, I>
{
    parser(element)
    .or(parser(literal))
    .or(parser(store))
    .or(parser(link))
    .or(parser(condition))
    .parse_state(input)
}

fn params<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<Param>>, I>
{
    optional(lift(Tok::OpenParen)
        .with(sep_by::<Vec<_>, _, _>(parser(param), lift(Tok::Comma)))
        .skip(lift(Tok::CloseParen)))
    .parse_state(input)
}

fn events<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<String>>, I>
{
    optional(lift(Tok::Events)
        .with(sep_by::<Vec<_>, _, _>(
            lift(Tok::Ident).map(ParseToken::into_string),
            lift(Tok::Comma),
        )))
    .parse_state(input)
}

pub fn chunk<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Option<Vec<Statement>>, I>
{
    optional(
        lift(Tok::Indent)
        .with(many1(parser(statement)))
        .skip(lift(Tok::Dedent)))
    .parse_state(input)
}

pub fn block<'a, I: Stream<Item=Token<'a>>>(input: State<I>)
    -> ParseResult<Block, I>
{
    lift(Tok::Ident).map(ParseToken::into_string)
    .and(parser(params))
    .and(parser(events))
    .skip(lift(Tok::Colon))
    .skip(lift(Tok::Newline))
    .and(parser(chunk))
    .map(|(((name, opt_params), opt_events), opt_stmtlist)| {
        Block::Html {
            name: name,
            params: opt_params.unwrap_or(vec!()),
            events: opt_events.unwrap_or(vec!()),
            statements: opt_stmtlist.unwrap_or(vec!()),
        }
    })
    .parse_state(input)
}

<|endoftext|>