<commit_before>use std::io::{self, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

pub const TYPE_ARRAY: u16 = 0x0000;
pub const TYPE_BLOB: u16 = 0x0002;
pub const TYPE_STRUCT: u16 = 0x0005;

pub const TYPE_VOID: u16 = 0x0080;
pub const TYPE_U16: u16 = 0x0081;
pub const TYPE_U32: u16 = 0x0082;
pub const TYPE_U64: u16 = 0x0083;
pub const TYPE_STRING: u16 = 0x0084;
pub const TYPE_RESULT_OK: u16 = 0x0085;
pub const TYPE_RESULT_ERR: u16 = 0x0086;
pub const TYPE_TUPLE: u16 = 0x0087;
pub const TYPE_I16: u16 = 0x0088;
pub const TYPE_I32: u16 = 0x0089;
pub const TYPE_I64: u16 = 0x008a;

pub trait Deserialize: Sized {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self>;
}

pub trait Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()>;
}

impl Serialize for [u8] {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_BLOB));
        try!(buf.write_u32::<BigEndian>(length as u32));
        try!(buf.write_all(self));
        Ok(())
    }
}

impl<T> Serialize for [T] where T: Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_ARRAY));
        try!(buf.write_u32::<BigEndian>(length as u32));
        for item in self.iter() {
            try!(Serialize::write(item, buf));
        }

        Ok(())
    }
}

impl Serialize for () {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_VOID));
        Ok(())
    }
}

impl Serialize for u16 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U16));
        try!(buf.write_u16::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for u32 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U32));
        try!(buf.write_u32::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for u64 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U64));
        try!(buf.write_u64::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for i64 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_I64));
        try!(buf.write_i64::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for str {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.as_bytes().len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_STRING));
        try!(buf.write_u32::<BigEndian>(length as u32));
        try!(buf.write_all(self.as_bytes()));
        Ok(())
    }
}

// TODO(sell): this shouldn't exist.
impl Serialize for (String, String) {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_TUPLE));
        try!(buf.write_u32::<BigEndian>(2));
        try!(Serialize::write(&self.0[..], buf));
        try!(Serialize::write(&self.1[..], buf));
        Ok(())
    }
}

impl<V, E> Serialize for Result<V, E> where V: Serialize, E: Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        match *self {
            Ok(ref ok) => {
                try!(buf.write_u16::<BigEndian>(TYPE_RESULT_OK));
                try!(Serialize::write(ok, buf));
            },
            Err(ref err) => {
                try!(buf.write_u16::<BigEndian>(TYPE_RESULT_ERR));
                try!(Serialize::write(err, buf));
            }
        }
        Ok(())
    }
}

impl Deserialize for Vec<u8> {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_BLOB {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        {
            let mut limit_reader = Read::by_ref(buf).take(length as u64);
            try!(limit_reader.read_to_end(&mut out));
        }

        Ok(out)
    }
}

// TODO(sell): this shouldn't exist.
impl Deserialize for (String, String) {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_TUPLE {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());
        if length != 2 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected length"));
        }

        let left = try!(Deserialize::read(buf));
        let right = try!(Deserialize::read(buf));
        Ok((left, right))
    }
}

impl<T> Deserialize for Vec<T> where T: Deserialize {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_ARRAY {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        for _ in 0..length {
            out.push(try!(Deserialize::read(buf)));
        }
        Ok(out)
    }
}

impl Deserialize for () {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_VOID {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        Ok(())
    }
}

impl Deserialize for u16 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U16 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u16::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for u32 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U32 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u32::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for u64 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U64 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u64::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for i64 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_I64 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_i64::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for String {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_STRING {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        {
            let mut limit_reader = Read::by_ref(buf).take(length as u64);
            try!(limit_reader.read_to_end(&mut out));
        }

        String::from_utf8(out)
            .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid string"))
    }
}

impl<V, E> Deserialize for Result<V, E> where V: Deserialize, E: Deserialize {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        match type_id {
            TYPE_RESULT_OK => {
                let val: V = try!(Deserialize::read(buf));
                Ok(Ok(val))
            },
            TYPE_RESULT_ERR => {
                let val: E = try!(Deserialize::read(buf));
                Ok(Err(val))
            },
            _ => {
                Err(io::Error::new(io::ErrorKind::Other, format!(
                    "unexpected type {} (want {} or {})",
                    type_id, TYPE_RESULT_OK, TYPE_RESULT_ERR)))
            }
        }
    }
}

pub fn null_read<R>(buf: &mut R, len: u64) -> io::Result<()> where R: Read {
    for byte in buf.by_ref().take(len).bytes() {
        try!(byte);
    }
    Ok(())
}

pub fn skip_entity(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    let type_id = try!(buf.read_u16::<BigEndian>());

    match type_id {
        TYPE_ARRAY => {
            let length = try!(buf.read_u32::<BigEndian>());
            for _ in 0..length {
                try!(skip_entity(buf));
            }
            Ok(())
        }
        TYPE_BLOB => {
            let length = try!(buf.read_u32::<BigEndian>());
            try!(null_read(buf, length as u64));
            Ok(())
        }
        TYPE_STRUCT => {
            let length = try!(buf.read_u32::<BigEndian>());
            for _ in 0..length {
                try!(skip_entity(buf));
                try!(skip_entity(buf));
            }
            Ok(())
        }
        TYPE_VOID => {
            Ok(())
        }
        TYPE_U16 => {
            try!(buf.read_u16::<BigEndian>());
            Ok(())
        }
        TYPE_U32 => {
            try!(buf.read_u32::<BigEndian>());
            Ok(())
        }
        TYPE_U64 => {
            try!(buf.read_u64::<BigEndian>());
            Ok(())
        }
        TYPE_I16 => {
            try!(buf.read_i16::<BigEndian>());
            Ok(())
        }
        TYPE_I32 => {
            try!(buf.read_i32::<BigEndian>());
            Ok(())
        }
        TYPE_I64 => {
            try!(buf.read_i64::<BigEndian>());
            Ok(())
        }
        TYPE_STRING => {
            let length = try!(buf.read_u32::<BigEndian>());
            try!(null_read(buf, length as u64));
            Ok(())
        }
        TYPE_RESULT_OK => {
            try!(skip_entity(buf));
            Ok(())
        }
        TYPE_RESULT_ERR => {
            try!(skip_entity(buf));
            Ok(())
        }
        _ => {
            Err(io::Error::new(io::ErrorKind::Other, "unknown type"))
        }
    }
}

pub fn expect_type(buf: &mut io::Cursor<Vec<u8>>, type_id: u16) -> io::Result<()> {
    let got_type_id = try!(buf.read_u16::<BigEndian>());
    if got_type_id != type_id {
        return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
    }
    Ok(())
}

pub fn write_empty_struct(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    try!(buf.write_u16::<BigEndian>(TYPE_STRUCT));
    try!(buf.write_u32::<BigEndian>(0));
    Ok(())
}

pub fn read_empty_struct(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    try!(expect_type(buf, TYPE_STRUCT));
    let field_count = try!(buf.read_u32::<BigEndian>());
    for _ in 0..field_count {
        let _: String = try!(Deserialize::read(buf));
        try!(skip_entity(buf));
    }

    Ok(())
}

pub fn deserialize<T>(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<T>
    where T: Deserialize
{
    Deserialize::read(buf)
}

pub fn serialize<T>(item: &T) -> io::Result<Vec<u8>>
    where T: Serialize
{
    let mut buf = io::Cursor::new(Vec::new());
    try!(Serialize::write(item, &mut buf));
    Ok(buf.into_inner())
}
<commit_msg>interface: better errors on some deserializers<commit_after>use std::io::{self, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

pub const TYPE_ARRAY: u16 = 0x0000;
pub const TYPE_BLOB: u16 = 0x0002;
pub const TYPE_STRUCT: u16 = 0x0005;

pub const TYPE_VOID: u16 = 0x0080;
pub const TYPE_U16: u16 = 0x0081;
pub const TYPE_U32: u16 = 0x0082;
pub const TYPE_U64: u16 = 0x0083;
pub const TYPE_STRING: u16 = 0x0084;
pub const TYPE_RESULT_OK: u16 = 0x0085;
pub const TYPE_RESULT_ERR: u16 = 0x0086;
pub const TYPE_TUPLE: u16 = 0x0087;
pub const TYPE_I16: u16 = 0x0088;
pub const TYPE_I32: u16 = 0x0089;
pub const TYPE_I64: u16 = 0x008a;

pub trait Deserialize: Sized {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self>;
}

pub trait Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()>;
}

impl Serialize for [u8] {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_BLOB));
        try!(buf.write_u32::<BigEndian>(length as u32));
        try!(buf.write_all(self));
        Ok(())
    }
}

impl<T> Serialize for [T] where T: Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_ARRAY));
        try!(buf.write_u32::<BigEndian>(length as u32));
        for item in self.iter() {
            try!(Serialize::write(item, buf));
        }

        Ok(())
    }
}

impl Serialize for () {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_VOID));
        Ok(())
    }
}

impl Serialize for u16 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U16));
        try!(buf.write_u16::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for u32 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U32));
        try!(buf.write_u32::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for u64 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_U64));
        try!(buf.write_u64::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for i64 {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_I64));
        try!(buf.write_i64::<BigEndian>(*self));
        Ok(())
    }
}

impl Serialize for str {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        let length = self.as_bytes().len();
        if 0xFFFFFFFF < length {
            return Err(io::Error::new(io::ErrorKind::Other, "excessive length"));
        }

        try!(buf.write_u16::<BigEndian>(TYPE_STRING));
        try!(buf.write_u32::<BigEndian>(length as u32));
        try!(buf.write_all(self.as_bytes()));
        Ok(())
    }
}

// TODO(sell): this shouldn't exist.
impl Serialize for (String, String) {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        try!(buf.write_u16::<BigEndian>(TYPE_TUPLE));
        try!(buf.write_u32::<BigEndian>(2));
        try!(Serialize::write(&self.0[..], buf));
        try!(Serialize::write(&self.1[..], buf));
        Ok(())
    }
}

impl<V, E> Serialize for Result<V, E> where V: Serialize, E: Serialize {
    fn write(&self, buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
        match *self {
            Ok(ref ok) => {
                try!(buf.write_u16::<BigEndian>(TYPE_RESULT_OK));
                try!(Serialize::write(ok, buf));
            },
            Err(ref err) => {
                try!(buf.write_u16::<BigEndian>(TYPE_RESULT_ERR));
                try!(Serialize::write(err, buf));
            }
        }
        Ok(())
    }
}

impl Deserialize for Vec<u8> {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_BLOB {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        {
            let mut limit_reader = Read::by_ref(buf).take(length as u64);
            try!(limit_reader.read_to_end(&mut out));
        }

        Ok(out)
    }
}

// TODO(sell): this shouldn't exist.
impl Deserialize for (String, String) {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_TUPLE {
            return Err(io::Error::new(io::ErrorKind::Other,
                format!("unexpected type: {} != {}", TYPE_TUPLE, type_id)));
        }

        let length = try!(buf.read_u32::<BigEndian>());
        if length != 2 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected length"));
        }

        let left = try!(Deserialize::read(buf));
        let right = try!(Deserialize::read(buf));
        Ok((left, right))
    }
}

impl<T> Deserialize for Vec<T> where T: Deserialize {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_ARRAY {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        for _ in 0..length {
            out.push(try!(Deserialize::read(buf)));
        }
        Ok(out)
    }
}

impl Deserialize for () {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_VOID {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        Ok(())
    }
}

impl Deserialize for u16 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U16 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u16::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for u32 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U32 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u32::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for u64 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_U64 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_u64::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for i64 {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_I64 {
            return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
        }

        let value = try!(buf.read_i64::<BigEndian>());
        Ok(value)
    }
}

impl Deserialize for String {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        if type_id != TYPE_STRING {
            return Err(io::Error::new(io::ErrorKind::Other,
                format!("unexpected type: {} != {}", TYPE_STRING, type_id)));
        }

        let length = try!(buf.read_u32::<BigEndian>());

        let mut out = Vec::with_capacity(length as usize);
        {
            let mut limit_reader = Read::by_ref(buf).take(length as u64);
            try!(limit_reader.read_to_end(&mut out));
        }

        String::from_utf8(out).map_err(|inv| {
            io::Error::new(io::ErrorKind::Other,
                format!("invalid string: {:?}", inv))
        })
    }
}

impl<V, E> Deserialize for Result<V, E> where V: Deserialize, E: Deserialize {
    fn read(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<Self> {
        let type_id = try!(buf.read_u16::<BigEndian>());
        match type_id {
            TYPE_RESULT_OK => {
                let val: V = try!(Deserialize::read(buf));
                Ok(Ok(val))
            },
            TYPE_RESULT_ERR => {
                let val: E = try!(Deserialize::read(buf));
                Ok(Err(val))
            },
            _ => {
                Err(io::Error::new(io::ErrorKind::Other, format!(
                    "unexpected type {} (want {} or {})",
                    type_id, TYPE_RESULT_OK, TYPE_RESULT_ERR)))
            }
        }
    }
}

pub fn null_read<R>(buf: &mut R, len: u64) -> io::Result<()> where R: Read {
    for byte in buf.by_ref().take(len).bytes() {
        try!(byte);
    }
    Ok(())
}

pub fn skip_entity(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    let type_id = try!(buf.read_u16::<BigEndian>());

    match type_id {
        TYPE_ARRAY => {
            let length = try!(buf.read_u32::<BigEndian>());
            for _ in 0..length {
                try!(skip_entity(buf));
            }
            Ok(())
        }
        TYPE_BLOB => {
            let length = try!(buf.read_u32::<BigEndian>());
            try!(null_read(buf, length as u64));
            Ok(())
        }
        TYPE_STRUCT => {
            let length = try!(buf.read_u32::<BigEndian>());
            for _ in 0..length {
                try!(skip_entity(buf));
                try!(skip_entity(buf));
            }
            Ok(())
        }
        TYPE_VOID => {
            Ok(())
        }
        TYPE_U16 => {
            try!(buf.read_u16::<BigEndian>());
            Ok(())
        }
        TYPE_U32 => {
            try!(buf.read_u32::<BigEndian>());
            Ok(())
        }
        TYPE_U64 => {
            try!(buf.read_u64::<BigEndian>());
            Ok(())
        }
        TYPE_I16 => {
            try!(buf.read_i16::<BigEndian>());
            Ok(())
        }
        TYPE_I32 => {
            try!(buf.read_i32::<BigEndian>());
            Ok(())
        }
        TYPE_I64 => {
            try!(buf.read_i64::<BigEndian>());
            Ok(())
        }
        TYPE_STRING => {
            let length = try!(buf.read_u32::<BigEndian>());
            try!(null_read(buf, length as u64));
            Ok(())
        }
        TYPE_RESULT_OK => {
            try!(skip_entity(buf));
            Ok(())
        }
        TYPE_RESULT_ERR => {
            try!(skip_entity(buf));
            Ok(())
        }
        _ => {
            Err(io::Error::new(io::ErrorKind::Other, "unknown type"))
        }
    }
}

pub fn expect_type(buf: &mut io::Cursor<Vec<u8>>, type_id: u16) -> io::Result<()> {
    let got_type_id = try!(buf.read_u16::<BigEndian>());
    if got_type_id != type_id {
        return Err(io::Error::new(io::ErrorKind::Other, "unexpected type"));
    }
    Ok(())
}

pub fn write_empty_struct(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    try!(buf.write_u16::<BigEndian>(TYPE_STRUCT));
    try!(buf.write_u32::<BigEndian>(0));
    Ok(())
}

pub fn read_empty_struct(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<()> {
    try!(expect_type(buf, TYPE_STRUCT));
    let field_count = try!(buf.read_u32::<BigEndian>());
    for _ in 0..field_count {
        let _: String = try!(Deserialize::read(buf));
        try!(skip_entity(buf));
    }

    Ok(())
}

pub fn deserialize<T>(buf: &mut io::Cursor<Vec<u8>>) -> io::Result<T>
    where T: Deserialize
{
    Deserialize::read(buf)
}

pub fn serialize<T>(item: &T) -> io::Result<Vec<u8>>
    where T: Serialize
{
    let mut buf = io::Cursor::new(Vec::new());
    try!(Serialize::write(item, &mut buf));
    Ok(buf.into_inner())
}
<|endoftext|>


<commit_before>use value::{Id, Relation};
use index::Index;

use std::collections::btree_map::{BTreeMap, Entry};
use std::cell::{RefCell, RefMut};

#[derive(Clone, Debug)]
struct World {
    views: BTreeMap<Id, RefCell<Relation>>,
}

impl World {
    fn view<Id: ToString>(&mut self, id: Id) -> RefMut<Relation> {
        match self.views.entry(id.to_string()) {
            Entry::Vacant(vacant) => {
                let relation = RefCell::new(Index::new());
                vacant.insert(relation).borrow_mut()
            }
            Entry::Occupied(occupied) => {
                occupied.into_mut().borrow_mut()
            }
        }
    }
}

// TODO
// check schema table
// check schemas on every table
// check every input is a view with kind=input
// gather function refs (change function to take ixes?)
// poison rows in rounds until changes stop
//   foreign keys don't exist or are poisoned
//   ixes are not 0-n
// order upstream deps
// gather downstream deps
// stratify and schedule (warn about cycles through aggregates)<commit_msg>Dump aborted compiler<commit_after>use value::{Id, Value, Tuple, Relation, ToValue, ToTuple, ToRelation};
use index::Index;
use query::{Ref, ConstraintOp, Constraint, Source, Clause, Query};
use flow::{FlowState, View, Node, Flow};

use std::collections::{HashMap, BitSet};
use std::cell::{RefCell, RefMut};
use std::num::ToPrimitive;

struct World {
    views: HashMap<Id, RefCell<Relation>>,
}

impl World {
    fn view<Id: ToString>(&self, id: Id) -> RefMut<Relation> {
        self.views.get(&id.to_string()).unwrap().borrow_mut()
    }
}

impl Index<Tuple> {
    pub fn find_all(&self, ix: usize, value: &Value) -> Vec<&Tuple> {
        self.iter().filter(|t| &t[ix] == value).collect()
    }

    pub fn find_one(&self, ix: usize, value: &Value) -> Option<&Tuple> {
        match &*self.find_all(ix, value) {
            [t] => Some(t),
            _ => None,
        }
    }
}

// TODO
// check schema table
// check view table
// fill in missing tables
// check schemas on every table
// check every table has a schema
// check every table is a view with kind=input
// check upstream etc are empty
// gather function refs
// poison rows in rounds until changes stop
//   foreign keys don't exist or are poisoned
//   ixes are not 0-n

// static view_id_ix: usize = 0;
// static view_kind_ix: usize = 2;

// static field_ix_ix: usize = 1;
// static field_id_ix: usize = 2;

// static source_ix_ix: usize = 0;
// static source_view_ix: usize = 1;
// static source_id_ix: usize = 2;
// static source_data_ix: usize = 3;
// static source_action_ix: usize = 4;

// static constraint_left_ix: usize = 0;
// static constraint_op_ix: usize = 1;
// static constraint_right_ix: usize = 2;

// static column_source_ix: usize = 0;
// static column_field_ix: usize = 1;

// static schedule_ix_ix: usize = 0;
// static schedule_view_ix: usize = 1;

// static upstream_downstream_ix: usize = 0;
// static upstream_upstream_ix: usize = 2;

// fn create_upstream(world: &mut World) {
//     // TODO have to handle unions too
//     let mut upstream = world.view("upstream");
//     for view in world.view("view").iter() {
//         let mut ix = 0.0;
//         let downstream_id = &view[view_id_ix];
//         for source in world.view("source").find_all(source_view_ix, downstream_id) {
//             let data = &source[source_data_ix];
//             if data[0] == "view".to_value() {
//                 let upstream_id = &data[1];
//                 upstream.insert((downstream_id.clone(), ix, upstream_id.clone()).to_tuple());
//                 ix += 1.0;
//             }
//         }
//     }
// }

// fn create_schedule(world: &mut World) {
//     // TODO actually schedule sensibly
//     // TODO warn about cycles through aggregates
//     let mut schedule = world.view("schedule");
//     let mut ix = 0.0;
//     for view in world.view("view").iter() {
//         let view_id = &view[view_id_ix];
//         schedule.insert((ix, view_id.clone()).to_tuple());
//         ix += 1.0;
//     }
// }

// fn get_source_ix(world: &World, source_id: &Value) -> usize {
//     let source = world.view("source").find_one(source_id_ix, source_id).unwrap();
//     source[source_ix_ix].to_usize().unwrap();
// }

// fn get_field_ix(world: &World, field_id: &Value) -> usize {
//     let field = world.view("field").find_one(field_id_ix, field_id).unwrap();
//     field[field_ix_ix].to_usize().unwrap();
// }

// fn get_num_fields(world: &World, view_id: &Value) -> usize {
//     let view = world.view("view").get_one(view_id_ix, view_id);
//     let schema_id = view[view_schema_ix];
//     world.view("fields").get_all(field_schema_ix, schema_id).len()
// }

// fn create_constraint(world: &World, constraint: Value) {
//     let my_column = constraint[constraint_left_ix][column_field_ix];
//     let op = match constraint[constraint_op_ix].to_string() {
//           "<" => ConstraintOp::LT,
//           "<=" => ConstraintOp::LTE,
//           "=" => ConstraintOp::EQ,
//           "!=" => ConstraintOp::NEQ,
//           ">" => ConstraintOp::GT,
//           ">=" => ConstraintOp::GTE,
//           _ => panic!("Dont know this op"),
//     };
//     let other_source_id = constraint[constraint_right_ix][column_source_ix];
//     let other_source_ix = get_source_ix(world, other_source_id);
//     let other_field_id = constraint[constraint_right_ix][column_field_ix];
//     let other_field_ix = get_field_ix(world, other_field_id);
//     Constraint{
//         my_column: my_column.to_usize().unwrap(),
//         op: op,
//         other_ref: Ref::Value{
//             clause: other_source_ix,
//             field: other_field_ix,
//         }
//     }
// }

// fn create_clause(world: &World, view_id: Value, source: Vec<Value>) {
//     let source_id = source[source_id_ix];
//     let source_data = source[source_data_ix];
//     if source_data[0].to_string() == "view" {
//         let other_view_id = source_data[1];
//         let schedule = world.view("schedule").find_one(schedule_view_ix, other_view_id);
//         let other_view_ix = schedule[schedule_ix_ix];
//         let constraints = world.view("constraint").iter().filter(|constraint| {
//             constraint[constraint_left_ix][column_source_ix] == other_view_id
//         }).map(|constraint| {
//             create_constraint(world, constraint)
//         }).collect::<Vec<_>>();
//         if source[source_action_ix].to_string() == "get-tuple" {
//             Clause::Tuple(Source{
//                 relation: other_view_ix,
//                 constraints: constraints,
//             })
//         } else {
//             Clause::Relation(Source{
//                 relation: other_view_ix,
//                 constraints: constraints,
//             })
//         }
//     } else {
//         panic!("Can't compile functions yet")
//     }

// }

// fn create_query(world: &World, view_id: &Value) -> Query {
//     let sources = world.view("source").find_all(source_view_ix, view_id); // arrives in ix order
//     let clauses = sources.map(|source| create_clause(world, view_id, source)).collect();
//     Query{clauses: clauses}
// }

// fn create_union(world: &World, view_id: &Value) -> Union {
//     // TODO have to handle mappings in upstream order...
//     let num_sink_fields = get_num_fields(view_id);
//     let view_mappings = Vec::new();
//     for view_mapping in world.view("view-mapping").find_all(view_mapping_view_ix, view_id) {
//         view_mapping_id = view_mapping[view_mapping_id_ix];
//         let source_view
//         let invalid = ::std::usize::MAX;
//         let field_mappings = vec![(invalid, invalid); num_sink_fields];
//         for field_mapping in world.view("field-mapping").find_all(field_mapping_view_mapping_ix) {
//             let source_field_id = field_mapping[field_mapping_source_field_ix];
//             let source_field_ix = get_field_ix(world, source_field_id);
//             let source_column_ix = field_mapping[field_mapping_source_column_ix].to_usize().unwrap();
//             let sink_field_id = field_mapping[field_mapping_sink_field_ix];
//             let sink_field_ix = get_field_ix(world, sink_field_id);
//             field_mappings[sink_field_ix] = (source_field_ix, source_column_ix);
//         }
//         let num_source_fields = get_num_fields()
//         view_mappings.push((num))
//     }
// }

// fn create_node(world: &World, view_id: &Value, view_kind: &Value) -> Node {
//     let view = match *view_kind.to_string() {
//         "input" => View::Input,
//         "query" => View::Query(create_query(world, view_id)),
//         "union" => View::Union(create_union(world, view_id)),
//     };
//     let upstream = world.view("upstream").find_all(upstream_downstream_ix, view_id).map(|upstream| {
//         upstream[upstream_upstream_ix];
//     }).collect(); // arrives in ix order so it will match the arg order selected by create_query/union
//     let downstream = world.view("upstream").find_all(upstream_upstream_ix, view_id).map(|upstream| {
//         upstream[upstream_downstream_ix];
//     }).collect();
//     Node{
//         id: view_id.to_string(),
//         view: view,
//         upstream: upstream,
//         downstream: downstream,
//     }
// }

// fn create_flow(world: &World) -> Flow {
//     let nodes = Vec::new();
//     for schedule in world.view("schedule").iter() { // arrives in ix order
//         let view_id = &schedule[schedule_view_ix];
//         let view = world.view("view").find_one(view_id_ix, view_id).unwrap();
//         let view_kind = &view[view_kind_ix];
//         let node = create_node(world, view);
//         nodes.push(node);
//     }
//     Flow{
//         nodes: nodes
//     }
// }

// fn create_flow_state(world: &World, flow: &Flow) -> FlowState {
//     let mut dirty = BitSet::new();
//     let mut outputs = Vec::new();
//     for (ix, node) in flow.nodes.iter().enumerate() {
//         outputs.push(RefCell::new(world.view(node.id).clone()));
//         match node.view {
//             View::Input => (),
//             _ => dirty.insert(ix),
//         }
//     }
//     FlowState{
//         dirty: dirty,
//         outputs: outputs
//     }
// }<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().collect();
    let query = &args[1];
    let filename = &args[2];

    println!("Searching for {}", query);
    println!("In file {}", filename);

    let mut f = File::open(filename).expect("file not found");

    let mut contents = String::new();
    f.read_to_string(&mut contents).expect("something went wrong while reading the file");

    println!("With text:\n{}", contents);
}
<commit_msg>create config struct greprs<commit_after>use std::env;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = parse_config(&args);

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);

    let mut f = File::open(config.filename).expect("file not found");

    let mut contents = String::new();
    f.read_to_string(&mut contents).expect("something went wrong while reading the file");

    println!("With text:\n{}", contents);
}

struct Config {
    query: String,
    filename: String,
}

fn parse_config(args: &[String]) -> Config {
    let query = args[1].clone();
    let filename = args[2].clone();

    Config { query, filename }
}
<|endoftext|>


<commit_before>use std::f64;

// main defines the program entry point
fn main() {
	// println!() is a macro which functions similar to C printf()
	println!("hello, {}!\n", "world");

	// All variables are immutable unless declared with "let mut"
	let item = "apple";

	// Declaring price using conditional statements
	let price: f32 =
		if item == "apple" {
			1.00
		} else if item == "banana" {
			1.50
		} else {
			2.00
		};

	println!("The price of {} is {}", item, price);

	// Calling and using function return
	println!("signum({}) = {}\n", -10i, signum(-10));

	// The "match" statement is a more generalized version of C "switch", which accepts expressions
	let num = 1i;
	match num {
		// Single condition
		0     => { println!("zero") }
		// One or the other
		1 | 2 => { println!("one or two") }
		// Range of conditions
		3..10 => { println!("three to ten") }
		// match is exhaustive, so a wildcard case is required at compile-time
		_     => { println!("something else") }
	}

	// Declare a tuple
	let tup = (0.0f64, 1.0f64);
	// Compute angle using tuple
	let ang = angle(tup);
	println!("\nangle({}) = {}\n", tup, ang);

	// Bind subpattern to variable
	match 18i {
		age @ 0..20 => println!("{} years old", age),
		_ => println!("some other age")
	}

	// Destructure tuple using let
	let (x, y) = tup;
	println!("\n(x:{}, y:{}) == tup:{}\n", x, y, tup);

	// while loop
	let mut amount = 8i;
	while amount > 0 {
		amount -= 1;
		println!("cake: {}", amount);
	}

	// infinite loop
	loop {
		// ... not so much
		println!("\nbreak!\n");
		break;
	}

	// for loop
	for i in range(0u, 5) {
		println!("count: {}", i);
	}
}

// signum returns the sign of an integer
fn signum(x: int) -> int {
	if x < 0 { -1 }
	else if x > 0 { 1 }
	else { 0 }
}

// Using pattern matching for "destructuring": matching to bind names to the contents of data types
// A tuple is declared as follows
fn angle(vector: (f64, f64)) -> f64 {
	let pi = f64::consts::PI;
	match vector {
		// A variable name matches any value, AND binds the value to the arm's action
		// Matches any tuple whose first element is zero, AND binds y to the second element
		(0.0, y) if y < 0.0 => 1.5 * pi,
		// Matches any tuple whose first element is zero, but does not bind anything to the second element
		(0.0, _) => 0.5 * pi,
		// Matches any tuple, and binds both elements to variables
		(x, y) => (y / x).atan()
	}
}
<commit_msg>rust: add structs and struct matching<commit_after>extern crate debug;

use std::f64;

// main defines the program entry point
fn main() {
	// println!() is a macro which functions similar to C printf()
	println!("hello, {}!\n", "world");

	// All variables are immutable unless declared with "let mut"
	let item = "apple";

	// Declaring price using conditional statements
	let price: f32 =
		if item == "apple" {
			1.00
		} else if item == "banana" {
			1.50
		} else {
			2.00
		};

	println!("The price of {} is {}", item, price);

	// Calling and using function return
	println!("signum({}) = {}\n", -10i, signum(-10));

	// The "match" statement is a more generalized version of C "switch", which accepts expressions
	let num = 1i;
	match num {
		// Single condition
		0     => { println!("zero") }
		// One or the other
		1 | 2 => { println!("one or two") }
		// Range of conditions
		3..10 => { println!("three to ten") }
		// match is exhaustive, so a wildcard case is required at compile-time
		_     => { println!("something else") }
	}

	// Declare a tuple
	let tup = (0.0f64, 1.0f64);
	// Compute angle using tuple
	let ang = angle(tup);
	println!("\nangle({}) = {}\n", tup, ang);

	// Bind subpattern to variable
	match 18i {
		age @ 0..20 => println!("{} years old", age),
		_ => println!("some other age")
	}

	// Destructure tuple using let
	let (x, y) = tup;
	println!("\n(x:{}, y:{}) == tup:{}\n", x, y, tup);

	// while loop
	let mut amount = 8i;
	while amount > 0 {
		amount -= 1;
		println!("cake: {}", amount);
	}

	// infinite loop
	loop {
		// ... not so much
		println!("\nbreak!\n");
		break;
	}

	// for loop
	for i in range(0u, 5) {
		println!("count: {}", i);
	}

	// Structs also follow immutability rules
	let origin = Point{x: 1.0, y: 1.0};
	let mut x_pos = Point{x: 1.0, y: 1.0};
	x_pos.x += 2.0;

	// Print syntax is made possible by "extern crate debug;" at top of file
	println!("\norigin: {:?}, x_pos: {:?}\n", origin, x_pos);

	// match statements can also be used to destructure structs
	match x_pos {
		// Match if x == 3.0
		Point{x: 3.0, y: yy} => println!("y: {}\n", yy),
		// Wildcard
		Point{x: xx, y: yy} => println!("x: {}, y: {}\n", xx, yy),
	}

	// There is a shorthand match syntax for structs as well
	match x_pos {
		// Shorthand
		Point{x, ..} => println!("x: {}\n", x),
	}
}

// signum returns the sign of an integer
fn signum(x: int) -> int {
	if x < 0 { -1 }
	else if x > 0 { 1 }
	else { 0 }
}

// Using pattern matching for "destructuring": matching to bind names to the contents of data types
// A tuple is declared as follows
fn angle(vector: (f64, f64)) -> f64 {
	let pi = f64::consts::PI;
	match vector {
		// A variable name matches any value, AND binds the value to the arm's action
		// Matches any tuple whose first element is zero, AND binds y to the second element
		(0.0, y) if y < 0.0 => 1.5 * pi,
		// Matches any tuple whose first element is zero, but does not bind anything to the second element
		(0.0, _) => 0.5 * pi,
		// Matches any tuple, and binds both elements to variables
		(x, y) => (y / x).atan()
	}
}

// Point is a struct containing two f64 coordinates
struct Point {
	x: f64,
	y: f64
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::path::{self, PathBuf};
use std::fs;
use std::fmt::{self, Debug};
use std::io::{self, Read, Write, Seek};

use {GameResult, GameError};

pub type Path = str;

pub trait VFile: Read + Write + Seek + Debug {}

impl<T> VFile for T where T: Read + Write + Seek + Debug {}

/// Options for opening files
///
/// We need our own version of this structure because the one in
/// std annoyingly doesn't let you get data out of it.
#[derive(Debug, Default)]
pub struct OpenOptions {
    read: bool,
    write: bool,
    create: bool,
    append: bool,
    truncate: bool,
}

impl OpenOptions {
    /// Create a new instance
    pub fn new() -> OpenOptions {
        Default::default()
    }

    /// Open for reading
    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
        self.read = read;
        self
    }

    /// Open for writing
    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
        self.write = write;
        self
    }

    /// Create the file if it does not exist yet
    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
        self.create = create;
        self
    }

    /// Append at the end of the file
    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
        self.append = append;
        self
    }

    /// Truncate the file to 0 bytes after opening
    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
        self.truncate = truncate;
        self
    }

    fn to_fs_openoptions(&self) -> fs::OpenOptions {
        let mut opt = fs::OpenOptions::new();
        opt.read(self.read)
            .write(self.write)
            .create(self.create)
            .append(self.append)
            .truncate(self.truncate)
            .create(self.create);
        opt
    }
}

pub trait VFS: Debug {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>>;
    /// Open the file at this path for reading
    fn open(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().read(true))
    }
    /// Open the file at this path for writing, truncating it if it exists already
    fn create(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).truncate(true))
    }
    /// Open the file at this path for appending, creating it if necessary
    fn append(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).append(true))
    }
    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or an empty directory.
    fn rm(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()>;

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool;

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>>;

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>>;
}

pub trait VMetadata {}

/// A VFS that points to a directory and uses it as the root of its
/// file heirarchy.
pub struct PhysicalFS {
    root: Arc<PathBuf>,
    readonly: bool,
}

pub struct PhysicalMetadata(fs::Metadata);

impl VMetadata for PhysicalMetadata {}

/// Helper function to turn a path::Component into an Option<String> iff the Component
/// is a normal portion.
///
/// Basically this is to help turn a canonicalized absolute path into a relative path.
fn component_filter(comp: path::Component) -> Option<String> {
    match comp {
        path::Component::Normal(osstr) => Some(osstr.to_string_lossy().into_owned()),
        _ => None
    }
}

/// This takes a path and returns whether not it is what we want.
///
/// What we want is an absolute path with no `..`'s in it, so, something
/// like "/foo" or "/foo/bar.txt".  This means a path with components
/// starting with a RootDir, and zero or more Normal components.
fn path_is_valid(path: &path::Path) -> bool {
    let mut c = path.components();
    match c.next() {
        Some(path::Component::RootDir) => (),
        _ => return false,
    }

    fn is_normal_component(comp: path::Component) -> bool {
        match comp {
            path::Component::Normal(_) => true,
            _ => false,
        }
    }

    c.all(is_normal_component)
}

impl PhysicalFS {
    fn new(root: &str, readonly: bool) -> Self {
        PhysicalFS {
            root: Arc::new(root.into()),
            readonly: readonly,
        }
    }

    /// Takes a given path (&str) and returns
    /// a new PathBuf containing the canonical
    /// absolute path you get when appending it
    /// to this filesystem's root.
    fn get_absolute(&self, p: &str) -> GameResult<PathBuf> {
        let pathbuf = PathBuf::from(p);
        let relative_path = pathbuf.components().filter_map(component_filter);
        let mut full_path = (*self.root).clone();
        full_path.extend(relative_path);
        //
        //full_path.canonicalize()?;
        if !full_path.starts_with(&*self.root) {
            panic!("Tried to create an AltPath that exits the AltrootFS's root dir");
        }
        Ok(full_path)
    }
}

impl Debug for PhysicalFS {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "<PhysicalFS root: {}>", self.root.display())
    }
}

impl VFS for PhysicalFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        if self.readonly {
            if open_options.write || open_options.create || open_options.append || open_options.truncate {
                let msg = format!("Cannot alter file {:?} in root {:?}, filesystem read-only", path, self);
                return Err(GameError::FilesystemError(msg));
            }
        }
        let p = self.get_absolute(path)?;
        open_options.to_fs_openoptions().open(p)
            .map(|x| Box::new(x) as Box<VFile>)
            .map_err(GameError::from)
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to make directory {} but FS is read-only".to_string()));
        }
        let p = self.get_absolute(path)?;
        println!("Creating {:?}", p);
        panic!("fdsfdsafs");
        fs::DirBuilder::new()
            .recursive(true)
            .create(p)
            .map_err(GameError::from)
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file/dir {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir_all(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        match self.get_absolute(path) {
            Ok(p) => p.exists(),
            _ => false
        }
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        let p = self.get_absolute(path)?;
        p.metadata()
            .map(|m| Box::new(PhysicalMetadata(m)) as Box<VMetadata>)
            .map_err(GameError::from)
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

/// A structure that joins several VFS's together in order.
/// VecDeque instead of Vec?
#[derive(Debug)]
struct OverlayFS {
    roots: Vec<Box<VFS>>,
}

impl OverlayFS {
    fn new() -> Self {
        Self {
            roots: Vec::new()
        }
    }

    /// Adds a new VFS to the end of the list.
    fn push(&mut self, fs: Box<VFS>) {
        &self.roots.push(fs);
    }
}

impl VFS for OverlayFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        for vfs in &self.roots {
            match vfs.open_with_options(path, open_options) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("File {} not found", path)))
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.mkdir(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not find anywhere writeable to make dir {}", path)))
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rm(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file {}", path)))
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rmrf(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        for vfs in &self.roots {
            if vfs.exists(path) {
                return true;
            }
        }

        false
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        for vfs in &self.roots {
            match vfs.metadata(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO: This is tricky 'cause we have to actually merge iterators together...
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

#[cfg(test)]
mod tests {
    use std::io::{self, BufRead};
    use super::*;

    fn test_path_filtering() {
        let p = path::Path::new("/foo");
        assert!(path_is_valid(p));

        let p = path::Path::new("/foo/");
        assert!(path_is_valid(p));

        let p = path::Path::new("/foo/bar.txt");
        assert!(path_is_valid(p));

        let p = path::Path::new("/");
        assert!(path_is_valid(p));

        let p = path::Path::new("../foo");
        assert!(!path_is_valid(p));

        let p = path::Path::new("foo");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/foo/../../");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/foo/../bop");
        assert!(!path_is_valid(p));

        let p = path::Path::new("/../bar");
        assert!(!path_is_valid(p));

        let p = path::Path::new("");
        assert!(!path_is_valid(p));
    }

    #[test]
    fn test_read() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let f = fs.open("Cargo.toml").unwrap();
        let mut bf = io::BufReader::new(f);
        let mut s = String::new();
        bf.read_line(&mut s).unwrap();
        assert_eq!(&s, "[package]\n");
    }

    #[test]
    fn test_read_overlay() {
        let fs1 = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let mut f2path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        f2path.push("src");
        let fs2 = PhysicalFS::new(f2path.to_str().unwrap(), true);
        let mut ofs = OverlayFS::new();
        ofs.push(Box::new(fs1));
        ofs.push(Box::new(fs2));

        assert!(ofs.exists("Cargo.toml"));
        assert!(ofs.exists("lib.rs"));
        assert!(!ofs.exists("foobaz.rs"));
    }

    #[test]
    fn test_physical_all() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), false);
        let testdir = "testdir";
        let f1 = "testdir/file1.txt";
        let f2 = "testdir/file2.txt";

        // Create and delete test dir
        //assert!(!fs.exists(testdir));
        //fs.mkdir(testdir).unwrap();
        //assert!(fs.exists(testdir));
        //fs.rm(testdir).unwrap();
        //assert!(!fs.exists(testdir));

        //fs.create(testdir);
    }

}
<commit_msg>Augh paths are the worst thing ever.<commit_after>use std::sync::Arc;
use std::path::{self, PathBuf};
use std::fs;
use std::fmt::{self, Debug};
use std::io::{self, Read, Write, Seek};

use {GameResult, GameError};

pub type Path = str;

pub trait VFile: Read + Write + Seek + Debug {}

impl<T> VFile for T where T: Read + Write + Seek + Debug {}

/// Options for opening files
///
/// We need our own version of this structure because the one in
/// std annoyingly doesn't let you get data out of it.
#[derive(Debug, Default)]
pub struct OpenOptions {
    read: bool,
    write: bool,
    create: bool,
    append: bool,
    truncate: bool,
}

impl OpenOptions {
    /// Create a new instance
    pub fn new() -> OpenOptions {
        Default::default()
    }

    /// Open for reading
    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
        self.read = read;
        self
    }

    /// Open for writing
    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
        self.write = write;
        self
    }

    /// Create the file if it does not exist yet
    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
        self.create = create;
        self
    }

    /// Append at the end of the file
    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
        self.append = append;
        self
    }

    /// Truncate the file to 0 bytes after opening
    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
        self.truncate = truncate;
        self
    }

    fn to_fs_openoptions(&self) -> fs::OpenOptions {
        let mut opt = fs::OpenOptions::new();
        opt.read(self.read)
            .write(self.write)
            .create(self.create)
            .append(self.append)
            .truncate(self.truncate)
            .create(self.create);
        opt
    }
}

pub trait VFS: Debug {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>>;
    /// Open the file at this path for reading
    fn open(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().read(true))
    }
    /// Open the file at this path for writing, truncating it if it exists already
    fn create(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).truncate(true))
    }
    /// Open the file at this path for appending, creating it if necessary
    fn append(&self, path: &Path) -> GameResult<Box<VFile>> {
        self.open_with_options(path, OpenOptions::new().write(true).create(true).append(true))
    }
    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or an empty directory.
    fn rm(&self, path: &Path) -> GameResult<()>;

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()>;

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool;

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>>;

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>>;
}

pub trait VMetadata {}

/// A VFS that points to a directory and uses it as the root of its
/// file heirarchy.
pub struct PhysicalFS {
    root: Arc<PathBuf>,
    readonly: bool,
}

pub struct PhysicalMetadata(fs::Metadata);

impl VMetadata for PhysicalMetadata {}

/// This takes an absolute path and returns either a sanitized relative
/// version of it, or None if there's something bad in it.
///
/// What we want is an absolute path with no `..`'s in it, so, something
/// like "/foo" or "/foo/bar.txt".  This means a path with components
/// starting with a RootDir, and zero or more Normal components.
///
/// We gotta return a new path because there's apparently no real good way
/// to turn an absolute path into a relative path with the same
/// components (other than the first), and pushing an absolute Path
/// onto a PathBuf just completely nukes its existing contents.
/// Thanks, Obama.
fn sanitize_path(path: &path::Path) -> Option<PathBuf> {
    let mut c = path.components();
    match c.next() {
        Some(path::Component::RootDir) => (),
        _ => return None,
    }

    fn is_normal_component(comp: path::Component) -> Option<&str> {
        match comp {
            path::Component::Normal(s) => s.to_str(),
            _ => None,
        }
    }

    // This could be done more cleverly but meh
    let mut accm = PathBuf::new();
    for component in c {
        if let Some(s) = is_normal_component(component) {
            accm.push(s)
        } else {
            return None;
        }
    }
    Some(accm)
}

impl PhysicalFS {
    fn new(root: &str, readonly: bool) -> Self {
        PhysicalFS {
            root: Arc::new(root.into()),
            readonly: readonly,
        }
    }

    /// Takes a given path (&str) and returns
    /// a new PathBuf containing the canonical
    /// absolute path you get when appending it
    /// to this filesystem's root.
    fn get_absolute(&self, p: &str) -> GameResult<PathBuf> {
        let p = path::Path::new(p);
        if let Some(safe_path) = sanitize_path(p) {
            let mut root_path = (*self.root).clone();
            root_path.push(safe_path);
            println!("Path is {:?}", root_path);
            Ok(root_path)
        } else {
            let msg = format!("Path {:?} is not valid: must be an absolute path with no references to parent directories", p);
            Err(GameError::FilesystemError(msg))
        }
    }
}

impl Debug for PhysicalFS {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "<PhysicalFS root: {}>", self.root.display())
    }
}

impl VFS for PhysicalFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        if self.readonly {
            if open_options.write || open_options.create || open_options.append || open_options.truncate {
                let msg = format!("Cannot alter file {:?} in root {:?}, filesystem read-only", path, self);
                return Err(GameError::FilesystemError(msg));
            }
        }
        let p = self.get_absolute(path)?;
        open_options.to_fs_openoptions().open(p)
            .map(|x| Box::new(x) as Box<VFile>)
            .map_err(GameError::from)
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to make directory {} but FS is read-only".to_string()));
        }
        let p = self.get_absolute(path)?;
        println!("Creating {:?}", p);
        panic!("fdsfdsafs");
        fs::DirBuilder::new()
            .recursive(true)
            .create(p)
            .map_err(GameError::from)
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        if self.readonly {
            return Err(GameError::FilesystemError("Tried to remove file/dir {} but FS is read-only".to_string()));
        }

        let p = self.get_absolute(path)?;
        if p.is_dir() {
            fs::remove_dir_all(p)
                .map_err(GameError::from)
        } else {
            fs::remove_file(p)
                .map_err(GameError::from)
        }
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        match self.get_absolute(path) {
            Ok(p) => p.exists(),
            _ => false
        }
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        let p = self.get_absolute(path)?;
        p.metadata()
            .map(|m| Box::new(PhysicalMetadata(m)) as Box<VMetadata>)
            .map_err(GameError::from)
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

/// A structure that joins several VFS's together in order.
/// VecDeque instead of Vec?
#[derive(Debug)]
struct OverlayFS {
    roots: Vec<Box<VFS>>,
}

impl OverlayFS {
    fn new() -> Self {
        Self {
            roots: Vec::new()
        }
    }

    /// Adds a new VFS to the end of the list.
    fn push(&mut self, fs: Box<VFS>) {
        &self.roots.push(fs);
    }
}

impl VFS for OverlayFS {
    /// Open the file at this path with the given options
    fn open_with_options(&self, path: &Path, open_options: &OpenOptions) -> GameResult<Box<VFile>> {
        for vfs in &self.roots {
            match vfs.open_with_options(path, open_options) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("File {} not found", path)))
    }

    /// Create a directory at the location by this path
    fn mkdir(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.mkdir(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not find anywhere writeable to make dir {}", path)))
    }

    /// Remove a file
    fn rm(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rm(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file {}", path)))
    }

    /// Remove a file or directory and all its contents
    fn rmrf(&self, path: &Path) -> GameResult<()> {
        for vfs in &self.roots {
            match vfs.rmrf(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Check if the file exists
    fn exists(&self, path: &Path) -> bool {
        for vfs in &self.roots {
            if vfs.exists(path) {
                return true;
            }
        }

        false
    }

    /// Get the file's metadata
    fn metadata(&self, path: &Path) -> GameResult<Box<VMetadata>> {
        for vfs in &self.roots {
            match vfs.metadata(path) {
                Err(_) => (),
                f => return f,
            }
        }
        Err(GameError::FilesystemError(format!("Could not remove file/dir {}", path)))
    }

    /// Retrieve the path entries in this path
    fn read_dir(&self, path: &Path) -> GameResult<Box<Iterator<Item = GameResult<Box<Path>>>>> {
        // TODO: This is tricky 'cause we have to actually merge iterators together...
        Err(GameError::FilesystemError("Foo!".to_string()))
    }
}

#[cfg(test)]
mod tests {
    use std::io::{self, BufRead};
    use super::*;

    #[test]
    fn test_path_filtering() {
        let p = path::Path::new("/foo");
        sanitize_path(p).unwrap();

        let p = path::Path::new("/foo/");
        sanitize_path(p).unwrap();

        let p = path::Path::new("/foo/bar.txt");
        sanitize_path(p).unwrap();

        let p = path::Path::new("/");
        sanitize_path(p).unwrap();

        let p = path::Path::new("../foo");
        assert!(sanitize_path(p).is_none());

        let p = path::Path::new("foo");
        assert!(sanitize_path(p).is_none());

        let p = path::Path::new("/foo/../../");
        assert!(sanitize_path(p).is_none());

        let p = path::Path::new("/foo/../bop");
        assert!(sanitize_path(p).is_none());

        let p = path::Path::new("/../bar");
        assert!(sanitize_path(p).is_none());

        let p = path::Path::new("");
        assert!(sanitize_path(p).is_none());
    }

    #[test]
    fn test_read() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let f = fs.open("/Cargo.toml").unwrap();
        let mut bf = io::BufReader::new(f);
        let mut s = String::new();
        bf.read_line(&mut s).unwrap();
        assert_eq!(&s, "[package]\n");
    }

    #[test]
    fn test_read_overlay() {
        let fs1 = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), true);
        let mut f2path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        f2path.push("src");
        let fs2 = PhysicalFS::new(f2path.to_str().unwrap(), true);
        let mut ofs = OverlayFS::new();
        ofs.push(Box::new(fs1));
        ofs.push(Box::new(fs2));

        assert!(ofs.exists("/Cargo.toml"));
        assert!(ofs.exists("/lib.rs"));
        assert!(!ofs.exists("/foobaz.rs"));
    }

    #[test]
    fn test_physical_all() {
        let fs = PhysicalFS::new(env!("CARGO_MANIFEST_DIR"), false);
        let testdir = "testdir";
        let f1 = "testdir/file1.txt";
        let f2 = "testdir/file2.txt";

        // Create and delete test dir
        //assert!(!fs.exists(testdir));
        //fs.mkdir(testdir).unwrap();
        //assert!(fs.exists(testdir));
        //fs.rm(testdir).unwrap();
        //assert!(!fs.exists(testdir));

        //fs.create(testdir);
    }

}
<|endoftext|>


<commit_before>use libc::c_char;
use libc::c_int;
use libc::c_uint;
use libc::c_void;
use libc::size_t;
use libc::uint32_t;
use std::char::from_u32;
use std::default;
use std::fmt;
use std::ptr;
use std::slice;
use std::char;
use std::result::Result;

use ::tsm::*;
use ::screen::*;

// A trait that reads the output of the vte, and does whatever it wants with it.
pub trait VteReader {
    fn read(&mut self, String);
}

pub struct NullReader;

impl VteReader for NullReader {
    fn read(&mut self, _: String) {}
}

// A callback that puts the output in a string and sends it to the configured VteReader.
extern "C" fn callback<T: VteReader>(_: *mut tsm_vte, input_ptr: *const c_char, input_size: size_t, reader: *mut c_void) {
    let reader: &mut T = unsafe { &mut *(reader as *mut T) };

    //// Note: is it safe to put this stuff in a string? May it not be invalid unicode? Probably.
    let input = unsafe { slice::from_raw_parts(input_ptr, input_size as usize) };
    let input = input.iter().map(|c| *c as u8).collect();
    let string = String::from_utf8(input).unwrap();

    reader.read(string);
}

#[repr(C)]
pub struct Vte<T: VteReader> {
  pub vte: *mut tsm_vte,
  pub screen: *mut tsm_screen,
  pub reader: Box<T>,
}

impl<T: VteReader> Vte<T> {
    // Create a new Vte aka Virtual Terminal Emulator
    pub fn new(reader: T) -> Result<Vte<T>, String> {
        let mut screen = ptr::null_mut();
        let err = unsafe { tsm_screen_new(&mut screen, None, ptr::null_mut()) }; // ignore err for now

        let mut vte: Vte<T> = Vte {
            vte: ptr::null_mut(),
            screen: screen,
            reader: Box::new(reader),
        };

        let mut vte_ptr = ptr::null_mut();
        let err = unsafe {
            tsm_vte_new(
                &mut vte_ptr,
                screen,
                callback::<T>,
                &mut *vte.reader as *mut _ as *mut c_void,
                None,
                ptr::null_mut()
            )
        };
        vte.vte = vte_ptr;

        if err != 0 {
            return Err(format!("err is {}", err))
        }

        Ok(vte)
    }

    pub fn handle_keyboard(&self, c: char) {
        unsafe { tsm_vte_handle_keyboard(self.vte, 0, 0, 0, c as u32); };
    }

    // consider unicode here. I wonder what pty uses for the stream datatypes?
    //pub fn tty_input(&self, string: &str) {
        //unsafe { tsm_vte_input(self.vte, ); };
    //}
}

impl<T: VteReader> Drop for Vte<T> {
    fn drop(&mut self) {
        unsafe { tsm_vte_unref(self.vte) }
    }
}
<commit_msg>Add comments<commit_after>use libc::c_char;
use libc::c_int;
use libc::c_uint;
use libc::c_void;
use libc::size_t;
use libc::uint32_t;
use std::char::from_u32;
use std::default;
use std::fmt;
use std::ptr;
use std::slice;
use std::char;
use std::result::Result;

use ::tsm::*;
use ::screen::*;

// A trait that reads the output of the vte, and does whatever it wants with it.  Is there a well
// known trait I can reuse? Probably.
// http://doc.rust-lang.org/stable/style/features/traits/generics.html#favor-widespread-traits.-<strong>[fixme:-needs-rfc]</strong>
pub trait VteReader {
    fn read(&mut self, String);
}

pub struct NullReader;

impl VteReader for NullReader {
    fn read(&mut self, _: String) {}
}

// A callback that puts the output in a string and sends it to the configured VteReader.
extern "C" fn callback<T: VteReader>(_: *mut tsm_vte, input_ptr: *const c_char, input_size: size_t, reader: *mut c_void) {
    let reader: &mut T = unsafe { &mut *(reader as *mut T) };

    //// Note: is it safe to put this stuff in a string? May it not be invalid unicode? Probably.
    let input = unsafe { slice::from_raw_parts(input_ptr, input_size as usize) };
    let input = input.iter().map(|c| *c as u8).collect();
    let string = String::from_utf8(input).unwrap();

    reader.read(string);
}

// Common traits to impl: Clone, Show, Hash, Eq
//
// Do I need this?
// |
// v
#[repr(C)]
pub struct Vte<T: VteReader> {
  pub vte: *mut tsm_vte,
  pub screen: *mut tsm_screen,
  pub reader: Box<T>,
}

impl<T: VteReader> Vte<T> {
    // Create a new Vte aka Virtual Terminal Emulator
    pub fn new(reader: T) -> Result<Vte<T>, String> {
        let mut screen = ptr::null_mut();
        let err = unsafe { tsm_screen_new(&mut screen, None, ptr::null_mut()) }; // ignore err for now

        let mut vte: Vte<T> = Vte {
            vte: ptr::null_mut(),
            screen: screen,
            reader: Box::new(reader),
        };

        let mut vte_ptr = ptr::null_mut();
        let err = unsafe {
            tsm_vte_new(
                &mut vte_ptr,
                screen,
                callback::<T>,
                &mut *vte.reader as *mut _ as *mut c_void,
                None,
                ptr::null_mut()
            )
        };
        vte.vte = vte_ptr;

        if err != 0 {
            return Err(format!("err is {}", err))
        }

        Ok(vte)
    }

    pub fn handle_keyboard(&self, c: char) {
        unsafe { tsm_vte_handle_keyboard(self.vte, 0, 0, 0, c as u32); };
    }

    // consider unicode here. I wonder what pty uses for the stream datatypes?
    //pub fn tty_input(&self, string: &str) {
        //unsafe { tsm_vte_input(self.vte, ); };
    //}
}

impl<T: VteReader> Drop for Vte<T> {
    fn drop(&mut self) {
        unsafe { tsm_vte_unref(self.vte) }
    }
}
<|endoftext|>


<commit_before>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};
use hyper::header::ContentType;

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            if res.is_empty() {
                return None
            }
            let string = res.to_owned();
            if let Some(value) = string.split(concat!("<", $tag, ">")).nth(1).unwrap_or("")
                                       .split(concat!("</", $tag, ">")).next() {
                Some(value.to_owned())
            } else {
                None
            }
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            if res.is_empty() {
                return None
            }
            let string = res.to_owned();
            if let Some(value) = string.split(concat!("\"", $tag, "\""))
                                       .nth(1).unwrap_or("")
                                       .split(",").next().unwrap_or("")
                                       .split("\"").nth(1) {
                Some(format!(concat!($prefix, "{}"), value.to_owned().replace("\\", "")))
            } else {
                None
            }
        }
    }
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// - If you send a lot of requests from one IP, it can be
    /// blocked. If you plan to add more then 100 URLs in one hour, please let
    /// the technical support know. Otherwise your IP can be blocked
    /// unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants. This list is in order of
/// provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant vector.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::IsGd,
        Provider::BnGy,
        Provider::VGd,
        Provider::Rdd,
        Provider::BamBz,
        Provider::TinyPh,
        Provider::FifoCc,
        Provider::SCoop,
        Provider::Bmeo,

        // The following list are items that have long response sometimes:
        Provider::TnyIm,

        // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
        Provider::SirBz,
        // Reason: rate limit (100 requests per hour)
        Provider::Rlu,
        // Reason: rate limit (3000 requests per day)
        Provider::HecSu,
        // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
        Provider::Abv8,
        // Reason: does not provide an api
        Provider::TinyUrl,
        // Reason: unstable work
        Provider::PsbeCo,

        // The following list are items that show previews instead of direct links.
        Provider::NowLinks,
    ]
}

fn abv8_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn abv8_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://abv8.me/?url={}", url))
        .send()
        .ok()
}

parse_json_tag!(bambz_parse, "url", "");

fn bambz_request(url: &str, client: &Client) -> Option<Response> {
    client.post("https://bam.bz/api/short")
        .body(&format!("target={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

parse_json_tag!(bmeo_parse, "short", "");

fn bmeo_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://bmeo.org/api.php?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(bngy_parse, "ShortenedUrl");

fn bngy_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://bn.gy/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");

fn fifocc_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://fifo.cc/api/v2?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(hecsu_parse, "short");

fn hecsu_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://hec.su/api?url={}&method=xml", url))
        .send()
        .ok()
}

fn isgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn isgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://is.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

fn nowlinks_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn nowlinks_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://nowlinks.net/api?url={}", url))
        .send()
        .ok()
}

fn phxcoin_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn phxcoin_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://phx.co.in/shrink.asp?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(psbeco_parse, "ShortUrl");

fn psbeco_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://psbe.co/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

fn scoop_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn scoop_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN", url))
        .send()
        .ok()
}

parse_json_tag!(rdd_parse, "rdd_url", "");

fn rdd_request(url: &str, client: &Client) -> Option<Response> {
    client.post("https://readability.com/api/shortener/v1/urls")
        .body(&format!("url={}", url))
        .send()
        .ok()
}

fn rlu_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn rlu_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://rlu.ru/index.sema?a=api&link={}", url))
        .send()
        .ok()
}

parse_json_tag!(sirbz_parse, "short_link", "");

fn sirbz_request(url: &str, client: &Client) -> Option<Response> {
    client.post("http://sirbz.com/api/shorten_url")
        .body(&format!("url={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

fn tinyurl_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let value = string.split("data-clipboard-text=\"")
                      .nth(1).unwrap_or("")
                      .split("\">").next();
    if let Some(string) = value {
        Some(string.to_owned())
    } else {
        None
    }
}

fn tinyurl_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://tinyurl.com/create.php?url={}", url))
        .send()
        .ok()
}

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");

fn tinyph_request(url: &str, client: &Client) -> Option<Response> {
    client.post("http://tiny.ph/api/url/create")
        .body(&format!("url={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

parse_xml_tag!(tnyim_parse, "shorturl");

fn tnyim_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://tny.im/yourls-api.php?action=shorturl&url={}", url))
        .send()
        .ok()
}

fn vgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn vgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://v.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Response to be parsed or `None` on a error.
pub fn request(url: &str, client: &Client, provider: Provider) -> Option<Response> {
    match provider {
        Provider::Abv8 => abv8_request(url, client),
        Provider::BamBz => bambz_request(url, client),
        Provider::Bmeo => bmeo_request(url, client),
        Provider::BnGy => bngy_request(url, client),
        Provider::FifoCc => fifocc_request(url, client),
        Provider::HecSu => hecsu_request(url, client),
        Provider::IsGd => isgd_request(url, client),
        Provider::NowLinks => nowlinks_request(url, client),
        Provider::PhxCoIn => phxcoin_request(url, client),
        Provider::PsbeCo => psbeco_request(url, client),
        Provider::SCoop => scoop_request(url, client),
        Provider::SirBz => sirbz_request(url, client),
        Provider::Rdd => rdd_request(url, client),
        Provider::Rlu => rlu_request(url, client),
        Provider::TinyUrl => tinyurl_request(url, client),
        Provider::TinyPh => tinyph_request(url, client),
        Provider::TnyIm => tnyim_request(url, client),
        Provider::VGd => vgd_request(url, client),
    }
}
<commit_msg>Don't derive Eq, Hash, PartialEq on Provider<commit_after>//! Library service providers implementation.

extern crate hyper;

use hyper::client::{Client, Response};
use hyper::header::ContentType;

macro_rules! parse_xml_tag {
    ($fname: ident, $tag: expr) => {
        fn $fname(res: &str) -> Option<String> {
            if res.is_empty() {
                return None
            }
            let string = res.to_owned();
            if let Some(value) = string.split(concat!("<", $tag, ">")).nth(1).unwrap_or("")
                                       .split(concat!("</", $tag, ">")).next() {
                Some(value.to_owned())
            } else {
                None
            }
        }
    }
}

macro_rules! parse_json_tag {
    ($fname: ident, $tag: expr, $prefix: expr) => {
        fn $fname(res: &str) -> Option<String> {
            if res.is_empty() {
                return None
            }
            let string = res.to_owned();
            if let Some(value) = string.split(concat!("\"", $tag, "\""))
                                       .nth(1).unwrap_or("")
                                       .split(",").next().unwrap_or("")
                                       .split("\"").nth(1) {
                Some(format!(concat!($prefix, "{}"), value.to_owned().replace("\\", "")))
            } else {
                None
            }
        }
    }
}

/// Used to specify which provider to use to generate a short URL.
#[derive(Clone, Copy, Debug)]
pub enum Provider {
    /// http://abv8.me provider
    ///
    /// Notes:
    ///
    /// * You may not shorten more than 20 unique URLs within a 3-minute period.
    /// * You may not shorten more than 60 unique URLs within a 15-minute
    ///   period.
    Abv8,
    /// https://bam.bz provider
    BamBz,
    /// http://bmeo.org provider
    Bmeo,
    /// https://bn.gy provider
    BnGy,
    /// http://fifo.cc provider
    FifoCc,
    /// https://hec.su provider
    ///
    /// Notes:
    ///
    /// * Limited to 3000 API requests per day
    HecSu,
    /// https://is.gd provider
    IsGd,
    /// http://nowlinks.net provider
    NowLinks,
    /// http://phx.co.in provider
    ///
    /// Notes:
    ///
    /// * After some time the service will display ads
    /// * Instead of redirecting, a preview page will be displayed
    /// * Currently unstable
    PhxCoIn,
    /// http://psbe.co provider
    PsbeCo,
    /// http://s.coop provider
    SCoop,
    /// http://readbility.com provider
    Rdd,
    /// http://rlu.ru provider
    ///
    /// Notes:
    ///
    /// * If you send a lot of requests from one IP, it can be
    ///   blocked. If you plan to add more then 100 URLs in one hour, please let
    ///   the technical support know. Otherwise your IP can be blocked
    ///   unexpectedly. Prior added URLs can be deleted.
    Rlu,
    /// http://sirbz.com provider
    ///
    /// Notes:
    ///
    /// * By default, you are limited to 250 requests per 15 minutes.
    SirBz,
    /// http://tinyurl.com provider
    ///
    /// Notes:
    ///
    /// * This service does not provide any API.
    /// * The implementation result depends on the service result web page.
    TinyUrl,
    /// http://tiny.ph provider
    TinyPh,
    /// http://tny.im provider
    TnyIm,
    /// https://v.gd provider
    VGd,
}

impl Provider {
    /// Converts the Provider variant into its domain name equivilant
    pub fn to_name(&self) -> &str {
        match *self {
            Provider::Abv8 => "abv8.me",
            Provider::BamBz => "bam.bz",
            Provider::Bmeo => "bmeo.org",
            Provider::BnGy => "bn.gy",
            Provider::FifoCc => "fifo.cc",
            Provider::HecSu => "hec.su",
            Provider::IsGd => "is.gd",
            Provider::NowLinks => "nowlinks.net",
            Provider::PhxCoIn => "phx.co.in",
            Provider::PsbeCo => "psbe.co",
            Provider::SCoop => "s.coop",
            Provider::SirBz => "sirbz.com",
            Provider::Rdd => "readability.com",
            Provider::Rlu => "rlu.ru",
            Provider::TinyUrl => "tinyurl.com",
            Provider::TinyPh => "tiny.ph",
            Provider::TnyIm => "tny.im",
            Provider::VGd => "v.gd",
        }
    }
}

/// Returns a vector of all `Provider` variants. This list is in order of
/// provider quality.
///
/// The providers which are discouraged from use - due to problems such as rate
/// limitations - are at the end of the resultant vector.
///
/// Note that some providers may not provide a generated short URL because the
/// submitted URL may already be short enough and would not benefit from
/// shortening via their service.
pub fn providers() -> Vec<Provider> {
    vec![
        Provider::IsGd,
        Provider::BnGy,
        Provider::VGd,
        Provider::Rdd,
        Provider::BamBz,
        Provider::TinyPh,
        Provider::FifoCc,
        Provider::SCoop,
        Provider::Bmeo,

        // The following list are items that have long response sometimes:
        Provider::TnyIm,

        // The following list are items that are discouraged from use:

        // Reasons:
        //
        // * rate limit (250 requests per 15 minutes)
        // * does not accept short urls (ex: http://google.com)
        Provider::SirBz,
        // Reason: rate limit (100 requests per hour)
        Provider::Rlu,
        // Reason: rate limit (3000 requests per day)
        Provider::HecSu,
        // Reason: rate limit (20r/3min; 60r/15min for a UNIQUE urls only)
        Provider::Abv8,
        // Reason: does not provide an api
        Provider::TinyUrl,
        // Reason: unstable work
        Provider::PsbeCo,

        // The following list are items that show previews instead of direct links.
        Provider::NowLinks,
    ]
}

fn abv8_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn abv8_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://abv8.me/?url={}", url))
        .send()
        .ok()
}

parse_json_tag!(bambz_parse, "url", "");

fn bambz_request(url: &str, client: &Client) -> Option<Response> {
    client.post("https://bam.bz/api/short")
        .body(&format!("target={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

parse_json_tag!(bmeo_parse, "short", "");

fn bmeo_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://bmeo.org/api.php?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(bngy_parse, "ShortenedUrl");

fn bngy_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://bn.gy/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

parse_json_tag!(fifocc_parse, "shortner", "http://fifo.cc/");

fn fifocc_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://fifo.cc/api/v2?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(hecsu_parse, "short");

fn hecsu_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://hec.su/api?url={}&method=xml", url))
        .send()
        .ok()
}

fn isgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn isgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("https://is.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

fn nowlinks_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn nowlinks_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://nowlinks.net/api?url={}", url))
        .send()
        .ok()
}

fn phxcoin_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn phxcoin_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://phx.co.in/shrink.asp?url={}", url))
        .send()
        .ok()
}

parse_xml_tag!(psbeco_parse, "ShortUrl");

fn psbeco_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://psbe.co/API.asmx/CreateUrl?real_url={}", url))
        .send()
        .ok()
}

fn scoop_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn scoop_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://s.coop/devapi.php?action=shorturl&url={}&format=RETURN", url))
        .send()
        .ok()
}

parse_json_tag!(rdd_parse, "rdd_url", "");

fn rdd_request(url: &str, client: &Client) -> Option<Response> {
    client.post("https://readability.com/api/shortener/v1/urls")
        .body(&format!("url={}", url))
        .send()
        .ok()
}

fn rlu_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn rlu_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://rlu.ru/index.sema?a=api&link={}", url))
        .send()
        .ok()
}

parse_json_tag!(sirbz_parse, "short_link", "");

fn sirbz_request(url: &str, client: &Client) -> Option<Response> {
    client.post("http://sirbz.com/api/shorten_url")
        .body(&format!("url={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

fn tinyurl_parse(res: &str) -> Option<String> {
    if res.is_empty() {
        return None
    }
    let string = res.to_owned();
    let value = string.split("data-clipboard-text=\"")
                      .nth(1).unwrap_or("")
                      .split("\">").next();
    if let Some(string) = value {
        Some(string.to_owned())
    } else {
        None
    }
}

fn tinyurl_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://tinyurl.com/create.php?url={}", url))
        .send()
        .ok()
}

parse_json_tag!(tinyph_parse, "hash", "http://tiny.ph/");

fn tinyph_request(url: &str, client: &Client) -> Option<Response> {
    client.post("http://tiny.ph/api/url/create")
        .body(&format!("url={}", url))
        .header(ContentType::form_url_encoded())
        .send()
        .ok()
}

parse_xml_tag!(tnyim_parse, "shorturl");

fn tnyim_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://tny.im/yourls-api.php?action=shorturl&url={}", url))
        .send()
        .ok()
}

fn vgd_parse(res: &str) -> Option<String> {
    Some(res.to_owned())
}

fn vgd_request(url: &str, client: &Client) -> Option<Response> {
    client.get(&format!("http://v.gd/create.php?format=simple&url={}", url))
        .send()
        .ok()
}

/// Parses the response from a successful request to a provider into the
/// URL-shortened string.
pub fn parse(res: &str, provider: Provider) -> Option<String> {
    match provider {
        Provider::Abv8 => abv8_parse(res),
        Provider::BamBz => bambz_parse(res),
        Provider::Bmeo => bmeo_parse(res),
        Provider::BnGy => bngy_parse(res),
        Provider::FifoCc => fifocc_parse(res),
        Provider::HecSu => hecsu_parse(res),
        Provider::IsGd => isgd_parse(res),
        Provider::NowLinks => nowlinks_parse(res),
        Provider::PhxCoIn => phxcoin_parse(res),
        Provider::PsbeCo => psbeco_parse(res),
        Provider::SCoop => scoop_parse(res),
        Provider::SirBz => sirbz_parse(res),
        Provider::Rdd => rdd_parse(res),
        Provider::Rlu => rlu_parse(res),
        Provider::TinyUrl => tinyurl_parse(res),
        Provider::TinyPh => tinyph_parse(res),
        Provider::TnyIm => tnyim_parse(res),
        Provider::VGd => vgd_parse(res),
    }
}

/// Performs a request to the short link provider.
/// Returns the parsed response on success or a `None` on error.
pub fn request(url: &str, client: &Client, provider: Provider) -> Option<Response> {
    match provider {
        Provider::Abv8 => abv8_request(url, client),
        Provider::BamBz => bambz_request(url, client),
        Provider::Bmeo => bmeo_request(url, client),
        Provider::BnGy => bngy_request(url, client),
        Provider::FifoCc => fifocc_request(url, client),
        Provider::HecSu => hecsu_request(url, client),
        Provider::IsGd => isgd_request(url, client),
        Provider::NowLinks => nowlinks_request(url, client),
        Provider::PhxCoIn => phxcoin_request(url, client),
        Provider::PsbeCo => psbeco_request(url, client),
        Provider::SCoop => scoop_request(url, client),
        Provider::SirBz => sirbz_request(url, client),
        Provider::Rdd => rdd_request(url, client),
        Provider::Rlu => rlu_request(url, client),
        Provider::TinyUrl => tinyurl_request(url, client),
        Provider::TinyPh => tinyph_request(url, client),
        Provider::TnyIm => tnyim_request(url, client),
        Provider::VGd => vgd_request(url, client),
    }
}
<|endoftext|>