<commit_before>/// Literal kind.
///
/// E.g. `"foo"`, `42`, `12.34` or `bool`
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Lit {
    /// A string literal (`"foo"`)
    Str(String, StrStyle),
    /// A byte string (`b"foo"`)
    ByteStr(Vec<u8>, StrStyle),
    /// A byte char (`b'f'`)
    Byte(u8),
    /// A character literal (`'a'`)
    Char(char),
    /// An integer literal (`1`)
    Int(u64, IntTy),
    /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
    Float(String, FloatTy),
    /// A boolean literal
    Bool(bool),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StrStyle {
    /// A regular string, like `"foo"`
    Cooked,
    /// A raw string, like `r##"foo"##`
    ///
    /// The uint is the number of `#` symbols used
    Raw(usize),
}

impl From<String> for Lit {
    fn from(input: String) -> Lit {
        Lit::Str(input, StrStyle::Cooked)
    }
}

impl<'a> From<&'a str> for Lit {
    fn from(input: &str) -> Lit {
        Lit::Str(input.into(), StrStyle::Cooked)
    }
}

impl From<Vec<u8>> for Lit {
    fn from(input: Vec<u8>) -> Lit {
        Lit::ByteStr(input, StrStyle::Cooked)
    }
}

impl<'a> From<&'a [u8]> for Lit {
    fn from(input: &[u8]) -> Lit {
        Lit::ByteStr(input.into(), StrStyle::Cooked)
    }
}

impl From<char> for Lit {
    fn from(input: char) -> Lit {
        Lit::Char(input)
    }
}

impl From<bool> for Lit {
    fn from(input: bool) -> Lit {
        Lit::Bool(input)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IntTy {
    Isize,
    I8,
    I16,
    I32,
    I64,
    Usize,
    U8,
    U16,
    U32,
    U64,
    Unsuffixed,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum FloatTy {
    F32,
    F64,
    Unsuffixed,
}

macro_rules! impl_from_for_lit {
    (Int, [$($rust_type:ty => $syn_type:expr),+]) => {
        $(
            impl From<$rust_type> for Lit {
                fn from(input: $rust_type) -> Lit {
                    Lit::Int(input as u64, $syn_type)
                }
            }
        )+
    };
    (Float, [$($rust_type:ty => $syn_type:expr),+]) => {
        $(
            impl From<$rust_type> for Lit {
                fn from(input: $rust_type) -> Lit {
                    Lit::Float(format!("{}", input), $syn_type)
                }
            }
        )+
    };
}

impl_from_for_lit! {Int, [
    isize => IntTy::Isize,
    i8 => IntTy::I8,
    i16 => IntTy::I16,
    i32 => IntTy::I32,
    i64 => IntTy::I64,
    usize => IntTy::Usize,
    u8 => IntTy::U8,
    u16 => IntTy::U16,
    u32 => IntTy::U32,
    u64 => IntTy::U64
]}

impl_from_for_lit! {Float, [
    f32 => FloatTy::F32,
    f64 => FloatTy::F64
]}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use escape::{cooked_byte, cooked_byte_string, cooked_char, cooked_string, raw_string};
    use space::skip_whitespace;
    use nom::IResult;
    use unicode_xid::UnicodeXID;

    named!(pub lit -> Lit, alt!(
        string
        |
        byte_string
        |
        byte
        |
        character
        |
        float // must be before int
        |
        int => { |(value, ty)| Lit::Int(value, ty) }
        |
        boolean
    ));

    named!(string -> Lit, alt!(
        quoted_string => { |s| Lit::Str(s, StrStyle::Cooked) }
        |
        preceded!(
            punct!("r"),
            raw_string
        ) => { |(s, n)| Lit::Str(s, StrStyle::Raw(n)) }
    ));

    named!(pub quoted_string -> String, delimited!(
        punct!("\""),
        cooked_string,
        tag!("\"")
    ));

    named!(byte_string -> Lit, alt!(
        delimited!(
            punct!("b\""),
            cooked_byte_string,
            tag!("\"")
        ) => { |vec| Lit::ByteStr(vec, StrStyle::Cooked) }
        |
        preceded!(
            punct!("br"),
            raw_string
        ) => { |(s, n): (String, _)| Lit::ByteStr(s.into_bytes(), StrStyle::Raw(n)) }
    ));

    named!(byte -> Lit, do_parse!(
        punct!("b") >>
        tag!("'") >>
        b: cooked_byte >>
        tag!("'") >>
        (Lit::Byte(b))
    ));

    named!(character -> Lit, do_parse!(
        punct!("'") >>
        ch: cooked_char >>
        tag!("'") >>
        (Lit::Char(ch))
    ));

    named!(float -> Lit, do_parse!(
        value: float_string >>
        suffix: alt!(
            tag!("f32") => { |_| FloatTy::F32 }
            |
            tag!("f64") => { |_| FloatTy::F64 }
            |
            epsilon!() => { |_| FloatTy::Unsuffixed }
        ) >>
        (Lit::Float(value, suffix))
    ));

    named!(pub int -> (u64, IntTy), tuple!(
        digits,
        alt!(
            tag!("isize") => { |_| IntTy::Isize }
            |
            tag!("i8") => { |_| IntTy::I8 }
            |
            tag!("i16") => { |_| IntTy::I16 }
            |
            tag!("i32") => { |_| IntTy::I32 }
            |
            tag!("i64") => { |_| IntTy::I64 }
            |
            tag!("usize") => { |_| IntTy::Usize }
            |
            tag!("u8") => { |_| IntTy::U8 }
            |
            tag!("u16") => { |_| IntTy::U16 }
            |
            tag!("u32") => { |_| IntTy::U32 }
            |
            tag!("u64") => { |_| IntTy::U64 }
            |
            epsilon!() => { |_| IntTy::Unsuffixed }
        )
    ));

    named!(boolean -> Lit, alt!(
        keyword!("true") => { |_| Lit::Bool(true) }
        |
        keyword!("false") => { |_| Lit::Bool(false) }
    ));

    fn float_string(mut input: &str) -> IResult<&str, String> {
        input = skip_whitespace(input);

        let mut chars = input.chars().peekable();
        match chars.next() {
            Some(ch) if ch >= '0' && ch <= '9' => {}
            _ => return IResult::Error,
        }

        let mut len = 1;
        let mut has_dot = false;
        let mut has_exp = false;
        while let Some(&ch) = chars.peek() {
            match ch {
                '0'...'9' | '_' => {
                    chars.next();
                    len += 1;
                }
                '.' => {
                    if has_dot {
                        break;
                    }
                    chars.next();
                    if chars.peek()
                        .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
                        .unwrap_or(false) {
                        return IResult::Error;
                    }
                    len += 1;
                    has_dot = true;
                }
                'e' | 'E' => {
                    chars.next();
                    len += 1;
                    has_exp = true;
                    break;
                }
                _ => break,
            }
        }

        let rest = &input[len..];
        if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
            return IResult::Error;
        }

        if has_exp {
            let mut has_exp_value = false;
            while let Some(&ch) = chars.peek() {
                match ch {
                    '+' | '-' => {
                        if has_exp_value {
                            break;
                        }
                        chars.next();
                        len += 1;
                    }
                    '0'...'9' => {
                        chars.next();
                        len += 1;
                        has_exp_value = true;
                    }
                    '_' => {
                        chars.next();
                        len += 1;
                    }
                    _ => break,
                }
            }
            if !has_exp_value {
                return IResult::Error;
            }
        }

        IResult::Done(&input[len..], input[..len].replace("_", ""))
    }

    pub fn digits(mut input: &str) -> IResult<&str, u64> {
        input = skip_whitespace(input);

        let base = if input.starts_with("0x") {
            input = &input[2..];
            16
        } else if input.starts_with("0o") {
            input = &input[2..];
            8
        } else if input.starts_with("0b") {
            input = &input[2..];
            2
        } else {
            10
        };

        let mut value = 0u64;
        let mut len = 0;
        let mut empty = true;
        for b in input.bytes() {
            let digit = match b {
                b'0'...b'9' => (b - b'0') as u64,
                b'a'...b'f' => 10 + (b - b'a') as u64,
                b'A'...b'F' => 10 + (b - b'A') as u64,
                b'_' => {
                    if empty {
                        return IResult::Error;
                    }
                    len += 1;
                    continue;
                }
                _ => break,
            };
            if digit >= base {
                return IResult::Error;
            }
            value = match value.checked_mul(base) {
                Some(value) => value,
                None => return IResult::Error,
            };
            value = match value.checked_add(digit) {
                Some(value) => value,
                None => return IResult::Error,
            };
            len += 1;
            empty = false;
        }
        if empty {
            IResult::Error
        } else {
            IResult::Done(&input[len..], value)
        }
    }
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};
    use std::{ascii, iter};
    use std::fmt::{self, Display};
    use std::str;

    impl ToTokens for Lit {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                Lit::Str(ref s, StrStyle::Cooked) => s.to_tokens(tokens),
                Lit::Str(ref s, StrStyle::Raw(n)) => {
                    tokens.append(&format!("r{delim}\"{string}\"{delim}",
                        delim = iter::repeat("#").take(n).collect::<String>(),
                        string = s));
                }
                Lit::ByteStr(ref v, StrStyle::Cooked) => {
                    let mut escaped = "b\"".to_string();
                    for &ch in v.iter() {
                        match ch {
                            0 => escaped.push_str(r"\0"),
                            b'\'' => escaped.push('\''),
                            _ => escaped.extend(ascii::escape_default(ch).map(|c| c as char)),
                        }
                    }
                    escaped.push('"');
                    tokens.append(&escaped);
                }
                Lit::ByteStr(ref vec, StrStyle::Raw(n)) => {
                    tokens.append(&format!("br{delim}\"{string}\"{delim}",
                        delim = iter::repeat("#").take(n).collect::<String>(),
                        string = str::from_utf8(vec).unwrap()));
                }
                Lit::Byte(b) => {
                    match b {
                        0 => tokens.append(r"b'\0'"),
                        b'\"' => tokens.append("b'\"'"),
                        _ => {
                            let mut escaped = "b'".to_string();
                            escaped.extend(ascii::escape_default(b).map(|c| c as char));
                            escaped.push('\'');
                            tokens.append(&escaped);
                        }
                    }
                }
                Lit::Char(ch) => ch.to_tokens(tokens),
                Lit::Int(value, ty) => tokens.append(&format!("{}{}", value, ty)),
                Lit::Float(ref value, ty) => tokens.append(&format!("{}{}", value, ty)),
                Lit::Bool(true) => tokens.append("true"),
                Lit::Bool(false) => tokens.append("false"),
            }
        }
    }

    impl Display for IntTy {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            match *self {
                IntTy::Isize => formatter.write_str("isize"),
                IntTy::I8 => formatter.write_str("i8"),
                IntTy::I16 => formatter.write_str("i16"),
                IntTy::I32 => formatter.write_str("i32"),
                IntTy::I64 => formatter.write_str("i64"),
                IntTy::Usize => formatter.write_str("usize"),
                IntTy::U8 => formatter.write_str("u8"),
                IntTy::U16 => formatter.write_str("u16"),
                IntTy::U32 => formatter.write_str("u32"),
                IntTy::U64 => formatter.write_str("u64"),
                IntTy::Unsuffixed => Ok(()),
            }
        }
    }

    impl Display for FloatTy {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            match *self {
                FloatTy::F32 => formatter.write_str("f32"),
                FloatTy::F64 => formatter.write_str("f64"),
                FloatTy::Unsuffixed => Ok(()),
            }
        }
    }
}
<commit_msg>Allow underscore after 0x / 0o / 0b<commit_after>/// Literal kind.
///
/// E.g. `"foo"`, `42`, `12.34` or `bool`
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Lit {
    /// A string literal (`"foo"`)
    Str(String, StrStyle),
    /// A byte string (`b"foo"`)
    ByteStr(Vec<u8>, StrStyle),
    /// A byte char (`b'f'`)
    Byte(u8),
    /// A character literal (`'a'`)
    Char(char),
    /// An integer literal (`1`)
    Int(u64, IntTy),
    /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
    Float(String, FloatTy),
    /// A boolean literal
    Bool(bool),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StrStyle {
    /// A regular string, like `"foo"`
    Cooked,
    /// A raw string, like `r##"foo"##`
    ///
    /// The uint is the number of `#` symbols used
    Raw(usize),
}

impl From<String> for Lit {
    fn from(input: String) -> Lit {
        Lit::Str(input, StrStyle::Cooked)
    }
}

impl<'a> From<&'a str> for Lit {
    fn from(input: &str) -> Lit {
        Lit::Str(input.into(), StrStyle::Cooked)
    }
}

impl From<Vec<u8>> for Lit {
    fn from(input: Vec<u8>) -> Lit {
        Lit::ByteStr(input, StrStyle::Cooked)
    }
}

impl<'a> From<&'a [u8]> for Lit {
    fn from(input: &[u8]) -> Lit {
        Lit::ByteStr(input.into(), StrStyle::Cooked)
    }
}

impl From<char> for Lit {
    fn from(input: char) -> Lit {
        Lit::Char(input)
    }
}

impl From<bool> for Lit {
    fn from(input: bool) -> Lit {
        Lit::Bool(input)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum IntTy {
    Isize,
    I8,
    I16,
    I32,
    I64,
    Usize,
    U8,
    U16,
    U32,
    U64,
    Unsuffixed,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum FloatTy {
    F32,
    F64,
    Unsuffixed,
}

macro_rules! impl_from_for_lit {
    (Int, [$($rust_type:ty => $syn_type:expr),+]) => {
        $(
            impl From<$rust_type> for Lit {
                fn from(input: $rust_type) -> Lit {
                    Lit::Int(input as u64, $syn_type)
                }
            }
        )+
    };
    (Float, [$($rust_type:ty => $syn_type:expr),+]) => {
        $(
            impl From<$rust_type> for Lit {
                fn from(input: $rust_type) -> Lit {
                    Lit::Float(format!("{}", input), $syn_type)
                }
            }
        )+
    };
}

impl_from_for_lit! {Int, [
    isize => IntTy::Isize,
    i8 => IntTy::I8,
    i16 => IntTy::I16,
    i32 => IntTy::I32,
    i64 => IntTy::I64,
    usize => IntTy::Usize,
    u8 => IntTy::U8,
    u16 => IntTy::U16,
    u32 => IntTy::U32,
    u64 => IntTy::U64
]}

impl_from_for_lit! {Float, [
    f32 => FloatTy::F32,
    f64 => FloatTy::F64
]}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use escape::{cooked_byte, cooked_byte_string, cooked_char, cooked_string, raw_string};
    use space::skip_whitespace;
    use nom::IResult;
    use unicode_xid::UnicodeXID;

    named!(pub lit -> Lit, alt!(
        string
        |
        byte_string
        |
        byte
        |
        character
        |
        float // must be before int
        |
        int => { |(value, ty)| Lit::Int(value, ty) }
        |
        boolean
    ));

    named!(string -> Lit, alt!(
        quoted_string => { |s| Lit::Str(s, StrStyle::Cooked) }
        |
        preceded!(
            punct!("r"),
            raw_string
        ) => { |(s, n)| Lit::Str(s, StrStyle::Raw(n)) }
    ));

    named!(pub quoted_string -> String, delimited!(
        punct!("\""),
        cooked_string,
        tag!("\"")
    ));

    named!(byte_string -> Lit, alt!(
        delimited!(
            punct!("b\""),
            cooked_byte_string,
            tag!("\"")
        ) => { |vec| Lit::ByteStr(vec, StrStyle::Cooked) }
        |
        preceded!(
            punct!("br"),
            raw_string
        ) => { |(s, n): (String, _)| Lit::ByteStr(s.into_bytes(), StrStyle::Raw(n)) }
    ));

    named!(byte -> Lit, do_parse!(
        punct!("b") >>
        tag!("'") >>
        b: cooked_byte >>
        tag!("'") >>
        (Lit::Byte(b))
    ));

    named!(character -> Lit, do_parse!(
        punct!("'") >>
        ch: cooked_char >>
        tag!("'") >>
        (Lit::Char(ch))
    ));

    named!(float -> Lit, do_parse!(
        value: float_string >>
        suffix: alt!(
            tag!("f32") => { |_| FloatTy::F32 }
            |
            tag!("f64") => { |_| FloatTy::F64 }
            |
            epsilon!() => { |_| FloatTy::Unsuffixed }
        ) >>
        (Lit::Float(value, suffix))
    ));

    named!(pub int -> (u64, IntTy), tuple!(
        digits,
        alt!(
            tag!("isize") => { |_| IntTy::Isize }
            |
            tag!("i8") => { |_| IntTy::I8 }
            |
            tag!("i16") => { |_| IntTy::I16 }
            |
            tag!("i32") => { |_| IntTy::I32 }
            |
            tag!("i64") => { |_| IntTy::I64 }
            |
            tag!("usize") => { |_| IntTy::Usize }
            |
            tag!("u8") => { |_| IntTy::U8 }
            |
            tag!("u16") => { |_| IntTy::U16 }
            |
            tag!("u32") => { |_| IntTy::U32 }
            |
            tag!("u64") => { |_| IntTy::U64 }
            |
            epsilon!() => { |_| IntTy::Unsuffixed }
        )
    ));

    named!(boolean -> Lit, alt!(
        keyword!("true") => { |_| Lit::Bool(true) }
        |
        keyword!("false") => { |_| Lit::Bool(false) }
    ));

    fn float_string(mut input: &str) -> IResult<&str, String> {
        input = skip_whitespace(input);

        let mut chars = input.chars().peekable();
        match chars.next() {
            Some(ch) if ch >= '0' && ch <= '9' => {}
            _ => return IResult::Error,
        }

        let mut len = 1;
        let mut has_dot = false;
        let mut has_exp = false;
        while let Some(&ch) = chars.peek() {
            match ch {
                '0'...'9' | '_' => {
                    chars.next();
                    len += 1;
                }
                '.' => {
                    if has_dot {
                        break;
                    }
                    chars.next();
                    if chars.peek()
                        .map(|&ch| ch == '.' || UnicodeXID::is_xid_start(ch))
                        .unwrap_or(false) {
                        return IResult::Error;
                    }
                    len += 1;
                    has_dot = true;
                }
                'e' | 'E' => {
                    chars.next();
                    len += 1;
                    has_exp = true;
                    break;
                }
                _ => break,
            }
        }

        let rest = &input[len..];
        if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) {
            return IResult::Error;
        }

        if has_exp {
            let mut has_exp_value = false;
            while let Some(&ch) = chars.peek() {
                match ch {
                    '+' | '-' => {
                        if has_exp_value {
                            break;
                        }
                        chars.next();
                        len += 1;
                    }
                    '0'...'9' => {
                        chars.next();
                        len += 1;
                        has_exp_value = true;
                    }
                    '_' => {
                        chars.next();
                        len += 1;
                    }
                    _ => break,
                }
            }
            if !has_exp_value {
                return IResult::Error;
            }
        }

        IResult::Done(&input[len..], input[..len].replace("_", ""))
    }

    pub fn digits(mut input: &str) -> IResult<&str, u64> {
        input = skip_whitespace(input);

        let base = if input.starts_with("0x") {
            input = &input[2..];
            16
        } else if input.starts_with("0o") {
            input = &input[2..];
            8
        } else if input.starts_with("0b") {
            input = &input[2..];
            2
        } else {
            10
        };

        let mut value = 0u64;
        let mut len = 0;
        let mut empty = true;
        for b in input.bytes() {
            let digit = match b {
                b'0'...b'9' => (b - b'0') as u64,
                b'a'...b'f' => 10 + (b - b'a') as u64,
                b'A'...b'F' => 10 + (b - b'A') as u64,
                b'_' => {
                    if empty && base == 10 {
                        return IResult::Error;
                    }
                    len += 1;
                    continue;
                }
                _ => break,
            };
            if digit >= base {
                return IResult::Error;
            }
            value = match value.checked_mul(base) {
                Some(value) => value,
                None => return IResult::Error,
            };
            value = match value.checked_add(digit) {
                Some(value) => value,
                None => return IResult::Error,
            };
            len += 1;
            empty = false;
        }
        if empty {
            IResult::Error
        } else {
            IResult::Done(&input[len..], value)
        }
    }
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};
    use std::{ascii, iter};
    use std::fmt::{self, Display};
    use std::str;

    impl ToTokens for Lit {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                Lit::Str(ref s, StrStyle::Cooked) => s.to_tokens(tokens),
                Lit::Str(ref s, StrStyle::Raw(n)) => {
                    tokens.append(&format!("r{delim}\"{string}\"{delim}",
                        delim = iter::repeat("#").take(n).collect::<String>(),
                        string = s));
                }
                Lit::ByteStr(ref v, StrStyle::Cooked) => {
                    let mut escaped = "b\"".to_string();
                    for &ch in v.iter() {
                        match ch {
                            0 => escaped.push_str(r"\0"),
                            b'\'' => escaped.push('\''),
                            _ => escaped.extend(ascii::escape_default(ch).map(|c| c as char)),
                        }
                    }
                    escaped.push('"');
                    tokens.append(&escaped);
                }
                Lit::ByteStr(ref vec, StrStyle::Raw(n)) => {
                    tokens.append(&format!("br{delim}\"{string}\"{delim}",
                        delim = iter::repeat("#").take(n).collect::<String>(),
                        string = str::from_utf8(vec).unwrap()));
                }
                Lit::Byte(b) => {
                    match b {
                        0 => tokens.append(r"b'\0'"),
                        b'\"' => tokens.append("b'\"'"),
                        _ => {
                            let mut escaped = "b'".to_string();
                            escaped.extend(ascii::escape_default(b).map(|c| c as char));
                            escaped.push('\'');
                            tokens.append(&escaped);
                        }
                    }
                }
                Lit::Char(ch) => ch.to_tokens(tokens),
                Lit::Int(value, ty) => tokens.append(&format!("{}{}", value, ty)),
                Lit::Float(ref value, ty) => tokens.append(&format!("{}{}", value, ty)),
                Lit::Bool(true) => tokens.append("true"),
                Lit::Bool(false) => tokens.append("false"),
            }
        }
    }

    impl Display for IntTy {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            match *self {
                IntTy::Isize => formatter.write_str("isize"),
                IntTy::I8 => formatter.write_str("i8"),
                IntTy::I16 => formatter.write_str("i16"),
                IntTy::I32 => formatter.write_str("i32"),
                IntTy::I64 => formatter.write_str("i64"),
                IntTy::Usize => formatter.write_str("usize"),
                IntTy::U8 => formatter.write_str("u8"),
                IntTy::U16 => formatter.write_str("u16"),
                IntTy::U32 => formatter.write_str("u32"),
                IntTy::U64 => formatter.write_str("u64"),
                IntTy::Unsuffixed => Ok(()),
            }
        }
    }

    impl Display for FloatTy {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
            match *self {
                FloatTy::F32 => formatter.write_str("f32"),
                FloatTy::F64 => formatter.write_str("f64"),
                FloatTy::Unsuffixed => Ok(()),
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::types::{get_value_from_input_event, Msg};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct CommandLine {
    valid_entries: Vec<Entry>,
    // Among valid (filtered) entries.
    selected_command_index: usize,
    value: String,
}

#[derive(PartialEq, Clone, Properties, Debug)]
pub struct CommandLineProperties {
    pub value: String,
    pub entries: Vec<Entry>,
    pub enabled: bool,
    pub input_node_ref: NodeRef,
    #[prop_or_default]
    pub oninput: Callback<String>,
    #[prop_or_default]
    pub onselect: Callback<Msg>,
    #[prop_or_default]
    pub onenter: Callback<()>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Entry {
    pub label: String,
    pub description: String,
    pub action: Msg,
    pub valid_classes: Vec<String>,
}

#[derive(Debug)]
pub enum CommandLineMsg {
    Noop,
    Click,
    Key(KeyboardEvent),
    Input(String),
}

impl Component for CommandLine {
    type Message = CommandLineMsg;
    type Properties = CommandLineProperties;

    fn create(ctx: &Context<Self>) -> Self {
        Self {
            valid_entries: ctx.props().entries.clone(),
            selected_command_index: 0,
            value: ctx.props().value.clone(),
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let props = ctx.props();
        let value = &self.value;
        let enabled = props.enabled;
        let valid_entries = &self.valid_entries;
        let selected_entry = valid_entries.get(self.selected_command_index);
        let selected_entry_suffix = selected_entry
            .cloned()
            .map(|v| v.label.clone())
            .unwrap_or_default()
            .strip_prefix(value)
            .map(|v| v.to_string())
            .unwrap_or_default();
        let selected = true;
        // TODO: Fuzzy search.
        let entries: Vec<_> = if selected {
            valid_entries
                .iter()
                .enumerate()
                .map(|(i, v)| {
                    let value_string = v.label.clone();
                    let value_suffix = value_string.strip_prefix(value).unwrap_or_default();

                    // let node = v.to_node();
                    let action = v.action.clone();
                    let onselect = props.onselect.clone();
                    let onclick = ctx.link().callback(move |_e: MouseEvent| {
                        onselect.emit(action.clone());
                        CommandLineMsg::Noop
                    });
                    let mut classes_item = vec!["block", "border"];
                    if i == self.selected_command_index {
                        classes_item.push("selected");
                    }
                    // Avoid re-selecting the node, we want to move to next.
                    html! {

                          { value.clone() }
                          { value_suffix }

                    }
                })
                .collect()
        } else {
            vec![]
        };
        let classes_dropdown = vec!["absolute", "z-10", "bg-white"];
        // let id = view::command_input_id(&path);
        let style = if value.len() > 0 {
            format!("width: {}ch;", value.len())
        } else {
            "width: 0.1ch;".to_string()
        };
        let rows = value.split('\n').count();
        // XXX: Chrome inspector CSS color editor.
        let placeholder = "";
        let placeholder = if placeholder.is_empty() {
            None
        } else {
            Some(html! {

{ placeholder }

            })
        };
        let suffix = if selected {
            selected_entry_suffix
        } else {
            "".to_string()
        };
        let mut class = vec![
            "inline-block",
            "w-full",
            "bg-transparent",
            "resize-none",
            "overflow-hidden",
        ];
        // let errors = vec![];
        // if !errors.is_empty() {
        //     class.push("error");
        // }

        // let editing = if model.mode == crate::types::Mode::Edit {
        let dropdown = if enabled && selected {
            html! {

                    { for entries }

            }
        } else {
            html! {}
        };
        let suffix = if enabled && selected {
            html! {
                { suffix }
            }
        } else {
            html! {}
        };
        let classes = if enabled && selected {
            match selected_entry {
                Some(entry) => entry.valid_classes.clone().join(" "),
                None => "".to_string(),
            }
        } else {
            "".to_string()
        };

        let oninput = props.oninput.clone();
        let oninput = ctx.link().callback(move |e: InputEvent| {
            let v = get_value_from_input_event(e);
            oninput.emit(v.clone());
            CommandLineMsg::Input(v)
        });

        let onclick = ctx.link().callback(|_| CommandLineMsg::Click);
        let onfocus = ctx.link().callback(|_| CommandLineMsg::Click);
        let onkeydown = ctx
            .link()
            .callback(move |e: KeyboardEvent| CommandLineMsg::Key(e));

        html! {

                { for placeholder }

                        <textarea
                        rows={ format!("{}", rows) }
                        ref={ ctx.props().input_node_ref.clone() }
                        //   id={ id }
                        class={ class }
                        type="text"
                        oninput={ oninput }
                        onkeydown={ onkeydown }
                        //   onfocus={ onfocus }
                        value={ value.to_string() }
                        style={ style }
                        //   disabled={ model.mode != crate::types::Mode::Edit }
                        autocomplete="off"
                        >
                        </textarea>
                        { suffix }

                    { dropdown }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            CommandLineMsg::Noop => false,
            CommandLineMsg::Click => {
                let input_node = ctx.props().input_node_ref.clone();
                input_node
                    .cast::<HtmlInputElement>()
                    .unwrap()
                    .focus()
                    .unwrap();
                true
            }
            CommandLineMsg::Input(value) => {
                self.value = value.clone();
                self.valid_entries = ctx
                    .props()
                    .entries
                    .clone()
                    .into_iter()
                    .filter(|v| v.label.starts_with(&value))
                    .collect();
                true
            }
            CommandLineMsg::Key(e) => {
                let props = ctx.props();
                let entries = &self.valid_entries;
                let selected_command_index = self.selected_command_index;
                if e.shift_key() {
                    return false;
                }
                match e.key().as_ref() {
                    "ArrowUp" => {
                        if entries.len() > 0 {
                            self.selected_command_index = if selected_command_index > 0 {
                                selected_command_index - 1
                            } else {
                                entries.len() - 1
                            }
                        }
                    }
                    "ArrowDown" => {
                        if entries.len() > 0 {
                            self.selected_command_index =
                                if selected_command_index < entries.len() - 1 {
                                    selected_command_index + 1
                                } else {
                                    0
                                }
                        }
                    }
                    "Enter" => {
                        let selected_entry = entries.get(selected_command_index).cloned();
                        if let Some(selected_entry) = selected_entry {
                            let action = selected_entry.action.clone();
                            props.onselect.emit(action);
                        } else {
                            props.onenter.emit(());
                        }
                    }
                    _ => {}
                };
                true
            }
        }
    }
}
<commit_msg>Enter<commit_after>use crate::types::{get_value_from_input_event, Msg};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct CommandLine {
    valid_entries: Vec<Entry>,
    // Among valid (filtered) entries.
    selected_command_index: usize,
    value: String,
}

#[derive(PartialEq, Clone, Properties, Debug)]
pub struct CommandLineProperties {
    pub value: String,
    pub entries: Vec<Entry>,
    pub enabled: bool,
    pub input_node_ref: NodeRef,
    #[prop_or_default]
    pub oninput: Callback<String>,
    #[prop_or_default]
    pub onselect: Callback<Msg>,
    #[prop_or_default]
    pub onenter: Callback<()>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Entry {
    pub label: String,
    pub description: String,
    pub action: Msg,
    pub valid_classes: Vec<String>,
}

#[derive(Debug)]
pub enum CommandLineMsg {
    Noop,
    Click,
    Key(KeyboardEvent),
    Input(String),
}

impl Component for CommandLine {
    type Message = CommandLineMsg;
    type Properties = CommandLineProperties;

    fn create(ctx: &Context<Self>) -> Self {
        Self {
            valid_entries: ctx.props().entries.clone(),
            selected_command_index: 0,
            value: ctx.props().value.clone(),
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let props = ctx.props();
        let value = &self.value;
        let enabled = props.enabled;
        let valid_entries = &self.valid_entries;
        let selected_entry = valid_entries.get(self.selected_command_index);
        let selected_entry_suffix = selected_entry
            .cloned()
            .map(|v| v.label.clone())
            .unwrap_or_default()
            .strip_prefix(value)
            .map(|v| v.to_string())
            .unwrap_or_default();
        let selected = true;
        // TODO: Fuzzy search.
        let entries: Vec<_> = if selected {
            valid_entries
                .iter()
                .enumerate()
                .map(|(i, v)| {
                    let value_string = v.label.clone();
                    let value_suffix = value_string.strip_prefix(value).unwrap_or_default();

                    // let node = v.to_node();
                    let action = v.action.clone();
                    let onselect = props.onselect.clone();
                    let onclick = ctx.link().callback(move |_e: MouseEvent| {
                        onselect.emit(action.clone());
                        CommandLineMsg::Noop
                    });
                    let mut classes_item = vec!["block", "border"];
                    if i == self.selected_command_index {
                        classes_item.push("selected");
                    }
                    // Avoid re-selecting the node, we want to move to next.
                    html! {

                          { value.clone() }
                          { value_suffix }

                    }
                })
                .collect()
        } else {
            vec![]
        };
        let classes_dropdown = vec!["absolute", "z-10", "bg-white"];
        // let id = view::command_input_id(&path);
        let style = if value.len() > 0 {
            format!("width: {}ch;", value.len())
        } else {
            "width: 0.1ch;".to_string()
        };
        let rows = value.split('\n').count();
        // XXX: Chrome inspector CSS color editor.
        let placeholder = "";
        let placeholder = if placeholder.is_empty() {
            None
        } else {
            Some(html! {

{ placeholder }

            })
        };
        let suffix = if selected {
            selected_entry_suffix
        } else {
            "".to_string()
        };
        let mut class = vec![
            "inline-block",
            "w-full",
            "bg-transparent",
            "resize-none",
            "overflow-hidden",
        ];
        // let errors = vec![];
        // if !errors.is_empty() {
        //     class.push("error");
        // }

        // let editing = if model.mode == crate::types::Mode::Edit {
        let dropdown = if enabled && selected {
            html! {

                    { for entries }

            }
        } else {
            html! {}
        };
        let suffix = if enabled && selected {
            html! {
                { suffix }
            }
        } else {
            html! {}
        };
        let classes = if enabled && selected {
            match selected_entry {
                Some(entry) => entry.valid_classes.clone().join(" "),
                None => "".to_string(),
            }
        } else {
            "".to_string()
        };

        let oninput = props.oninput.clone();
        let oninput = ctx.link().callback(move |e: InputEvent| {
            let v = get_value_from_input_event(e);
            oninput.emit(v.clone());
            CommandLineMsg::Input(v)
        });

        let onclick = ctx.link().callback(|_| CommandLineMsg::Click);
        let onfocus = ctx.link().callback(|_| CommandLineMsg::Click);
        let onkeydown = ctx
            .link()
            .callback(move |e: KeyboardEvent| CommandLineMsg::Key(e));

        html! {

                { for placeholder }

                        <textarea
                        rows={ format!("{}", rows) }
                        ref={ ctx.props().input_node_ref.clone() }
                        //   id={ id }
                        class={ class }
                        type="text"
                        oninput={ oninput }
                        onkeydown={ onkeydown }
                        //   onfocus={ onfocus }
                        value={ value.to_string() }
                        style={ style }
                        //   disabled={ model.mode != crate::types::Mode::Edit }
                        autocomplete="off"
                        >
                        </textarea>
                        { suffix }

                    { dropdown }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            CommandLineMsg::Noop => false,
            CommandLineMsg::Click => {
                let input_node = ctx.props().input_node_ref.clone();
                input_node
                    .cast::<HtmlInputElement>()
                    .unwrap()
                    .focus()
                    .unwrap();
                true
            }
            CommandLineMsg::Input(value) => {
                self.value = value.clone();
                self.valid_entries = ctx
                    .props()
                    .entries
                    .clone()
                    .into_iter()
                    .filter(|v| v.label.starts_with(&value))
                    .collect();
                true
            }
            CommandLineMsg::Key(e) => {
                let props = ctx.props();
                let entries = &self.valid_entries;
                let selected_command_index = self.selected_command_index;
                if e.shift_key() {
                    return false;
                }
                match e.key().as_ref() {
                    "ArrowUp" => {
                        if entries.len() > 0 {
                            self.selected_command_index = if selected_command_index > 0 {
                                selected_command_index - 1
                            } else {
                                entries.len() - 1
                            }
                        }
                    }
                    "ArrowDown" => {
                        if entries.len() > 0 {
                            self.selected_command_index =
                                if selected_command_index < entries.len() - 1 {
                                    selected_command_index + 1
                                } else {
                                    0
                                }
                        }
                    }
                    "Enter" => {
                        e.prevent_default();
                        let selected_entry = entries.get(selected_command_index).cloned();
                        if let Some(selected_entry) = selected_entry {
                            let action = selected_entry.action.clone();
                            props.onselect.emit(action);
                        } else {
                            props.onenter.emit(());
                        }
                    }
                    _ => {}
                };
                true
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::error::Error;
use std::panic;

macro_rules! fetch_tag {
  ($tiff:expr, $tag:expr) => (try!($tiff.find_entry($tag).ok_or(format!("Couldn't find tag {}",stringify!($tag)).to_string())););
}

macro_rules! fetch_ifd {
  ($tiff:expr, $tag:expr) => (try!($tiff.find_first_ifd($tag).ok_or(format!("Couldn't find ifd with tag {}",stringify!($tag)).to_string())););
}

extern crate toml;
mod basics;
mod cfa;
mod tiff;
mod mrw;
mod arw;
mod mef;
mod orf;
mod srw;
mod erf;
mod kdc;
mod dcs;
mod rw2;
mod raf;
mod dcr;
use self::tiff::*;

pub static CAMERAS_TOML: &'static str = include_str!("../../data/cameras/all.toml");

pub trait Decoder {
  fn image(&self) -> Result<Image, String>;
}

#[derive(Debug, Clone)]
pub struct Buffer {
  buf: Vec<u8>,
}

impl Buffer {
  pub fn new(reader: &mut Read) -> Result<Buffer, String> {
    let mut buffer = Vec::new();
    if let Err(err) = reader.read_to_end(&mut buffer) {
      return Err(format!("IOError: {}", err).to_string())
    }
    buffer.extend([0;16].iter().cloned());
    Ok(Buffer {
      buf: buffer,
    })
  }
}

#[derive(Debug, Clone)]
pub struct Image {
  pub make: String,
  pub model: String,
  pub canonical_make: String,
  pub canonical_model: String,
  pub width: usize,
  pub height: usize,
  pub wb_coeffs: [f32;4],
  pub data: Box<[u16]>,
  pub whitelevels: [i64;4],
  pub blacklevels: [i64;4],
  pub color_matrix: [i64;12],
  pub cfa: cfa::CFA,
  pub crops: [i64;4],
}

#[derive(Debug, Clone)]
pub struct Camera {
  pub make: String,
  pub model: String,
  pub mode: String,
  pub canonical_make: String,
  pub canonical_model: String,
  whitelevels: [i64;4],
  blacklevels: [i64;4],
  color_matrix: [i64;12],
  cfa: cfa::CFA,
  crops: [i64;4],
  bps: u32,
  hints: Vec<String>,
}

impl Camera {
  pub fn find_hint(&self, hint: &str) -> bool {
    self.hints.contains(&(hint.to_string()))
  }

  pub fn update_from_toml(&mut self, ct: &toml::Table) {
    for (name, val) in ct {
      match name.as_ref() {
        "make" => {self.make = val.as_str().unwrap().to_string().clone();},
        "model" => {self.model = val.as_str().unwrap().to_string().clone();},
        "mode" => {self.mode = val.as_str().unwrap().to_string().clone();},
        "canonical_name" => {self.canonical_make = val.as_str().unwrap().to_string().clone();},
        "canonical_model" => {self.canonical_model = val.as_str().unwrap().to_string().clone();},
        "whitepoint" => {let white = val.as_integer().unwrap(); self.whitelevels = [white, white, white, white];},
        "blackpoint" => {let black = val.as_integer().unwrap(); self.blacklevels = [black, black, black, black];},
        "color_matrix" => {
          let matrix = val.as_slice().unwrap();
          for (i, val) in matrix.into_iter().enumerate() {
            self.color_matrix[i] = val.as_integer().unwrap();
          }
        },
        "crops" => {
          let crop_vals = val.as_slice().unwrap();
          for (i, val) in crop_vals.into_iter().enumerate() {
            self.crops[i] = val.as_integer().unwrap();
          }
        },
        "color_pattern" => {self.cfa = cfa::CFA::new(&val.as_str().unwrap().to_string());},
        "bps" => {self.bps = val.as_integer().unwrap() as u32;},
        "hints" => {
          self.hints = Vec::new();
          for hint in val.as_slice().unwrap() {
            self.hints.push(hint.as_str().unwrap().to_string());
          }
        },
        _ => {},
      }
    }
  }

  pub fn new() -> Camera {
    Camera {
      make: "".to_string(),
      model: "".to_string(),
      mode: "".to_string(),
      canonical_make: "".to_string(),
      canonical_model: "".to_string(),
      whitelevels: [0;4],
      blacklevels: [0;4],
      color_matrix : [0;12],
      cfa: cfa::CFA::new(""),
      crops: [0,0,0,0],
      bps: 0,
      hints: Vec::new(),
    }
  }
}

pub fn ok_image(camera: &Camera, width: u32, height: u32, wb_coeffs: [f32;4], image: Vec<u16>) -> Result<Image,String> {
  Ok(Image {
    make: camera.make.clone(),
    model: camera.model.clone(),
    canonical_make: camera.canonical_make.clone(),
    canonical_model: camera.canonical_model.clone(),
    width: width as usize,
    height: height as usize,
    wb_coeffs: wb_coeffs,
    data: image.into_boxed_slice(),
    blacklevels: camera.blacklevels,
    whitelevels: camera.whitelevels,
    color_matrix: camera.color_matrix,
    cfa: camera.cfa.clone(),
    crops: camera.crops,
  })
}

//pub fn ok_image_with_levels(camera: &Camera, width: u32, height: u32, wb_coeffs: [f32;4], whites: [i64;4], blacks: [i64;4], image: Vec<u16>) -> Result<Image,String> {
//  Ok(Image {
//    make: camera.make.clone(),
//    model: camera.model.clone(),
//    canonical_make: camera.canonical_make.clone(),
//    canonical_model: camera.canonical_model.clone(),
//    width: width,
//    height: height,
//    wb_coeffs: wb_coeffs,
//    data: image.into_boxed_slice(),
//    blacklevels: blacks,
//    whitelevels: whites,
//    color_matrix: camera.color_matrix,
//    dcraw_filters: camera.dcraw_filters,
//    crops: camera.crops,
//  })
//}

#[derive(Debug, Clone)]
pub struct RawLoader {
  pub cameras: HashMap<(String,String,String),Camera>,
}

impl RawLoader {
  pub fn new() -> RawLoader {
    let mut parser = toml::Parser::new(&CAMERAS_TOML);
    let toml = match parser.parse() {
      Some(val) => val,
      None => panic!(format!("Error parsing all.toml: {:?}", parser.errors)),
    };
    let mut cams = Vec::new();
    for camera in toml.get("cameras").unwrap().as_slice().unwrap() {
      let ct = camera.as_table().unwrap();
      let mut cam = Camera::new();
      cam.update_from_toml(ct);
      let basecam = cam.clone();
      cams.push(cam);

      match ct.get("modes") {
        Some(val) => {
          for mode in val.as_slice().unwrap() {
            let cmt = mode.as_table().unwrap();
            let mut mcam = basecam.clone();
            mcam.update_from_toml(cmt);
            cams.push(mcam);
          }
        },
        None => {},
      }
    }

    let mut map = HashMap::new();
    for cam in cams {
      map.insert((cam.make.clone(),cam.model.clone(),cam.mode.clone()), cam);
    }

    RawLoader{
      cameras: map,
    }
  }

  pub fn get_decoder<'b>(&'b self, buf: &'b Buffer) -> Result<Box<Decoder+'b>, String> {
    let buffer = &buf.buf;

    if mrw::is_mrw(buffer) {
      let dec = Box::new(mrw::MrwDecoder::new(buffer, &self));
      return Ok(dec as Box<Decoder>);
    }

    let tiff = try!(TiffIFD::new_file(buffer));

    macro_rules! use_decoder {
        ($dec:ty, $buf:ident, $tiff:ident, $rawdec:ident) => (Ok(Box::new(<$dec>::new($buf, $tiff, $rawdec)) as Box<Decoder>));
    }

    match fetch_tag!(tiff, Tag::Make).get_str().to_string().as_ref() {
      "SONY"                    => use_decoder!(arw::ArwDecoder, buffer, tiff, self),
      "Mamiya-OP Co.,Ltd."      => use_decoder!(mef::MefDecoder, buffer, tiff, self),
      "OLYMPUS IMAGING CORP."   => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "OLYMPUS CORPORATION"     => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "OLYMPUS OPTICAL CO.,LTD" => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "SAMSUNG"                 => use_decoder!(srw::SrwDecoder, buffer, tiff, self),
      "SEIKO EPSON CORP."       => use_decoder!(erf::ErfDecoder, buffer, tiff, self),
      "EASTMAN KODAK COMPANY"   => use_decoder!(kdc::KdcDecoder, buffer, tiff, self),
      "KODAK"                   => use_decoder!(dcs::DcsDecoder, buffer, tiff, self),
      "Kodak"                   => use_decoder!(dcr::DcrDecoder, buffer, tiff, self),
      "Panasonic"               => use_decoder!(rw2::Rw2Decoder, buffer, tiff, self),
      "FUJIFILM"                => use_decoder!(raf::RafDecoder, buffer, tiff, self),
      make => Err(format!("Couldn't find a decoder for make \"{}\"", make).to_string()),
    }
  }

  pub fn check_supported_with_mode<'a>(&'a self, tiff: &'a TiffIFD, mode: &str) -> Result<&Camera, String> {
    let make = fetch_tag!(tiff, Tag::Make).get_str();
    let model = fetch_tag!(tiff, Tag::Model).get_str();

    match self.cameras.get(&(make.to_string(),model.to_string(),mode.to_string())) {
      Some(cam) => Ok(cam),
      None => Err(format!("Couldn't find camera \"{}\" \"{}\" mode \"{}\"", make, model, mode)),
    }
  }

  pub fn check_supported<'a>(&'a self, tiff: &'a TiffIFD) -> Result<&Camera, String> {
    self.check_supported_with_mode(tiff, "")
  }

  pub fn decode(&self, reader: &mut Read) -> Result<Image, String> {
    let buffer = try!(Buffer::new(reader));
    let decoder = try!(self.get_decoder(&buffer));
    decoder.image()
  }

  pub fn decode_safe(&self, path: &str) -> Result<Image, String> {
    match panic::catch_unwind(|| {
      let mut f = match File::open(path) {
        Ok(val) => val,
        Err(e) => {return Err(e.description().to_string())},
      };
      self.decode(&mut f)
    }) {
      Ok(val) => val,
      Err(_) => Err("Caught a panic while decoding, please file a bug and attach a sample file".to_string()),
    }
  }
}
<commit_msg>Reformat fetch_tag! and fetch_ifd! macros<commit_after>use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::error::Error;
use std::panic;

macro_rules! fetch_tag {
  ($tiff:expr, $tag:expr) => (
    try!(
      $tiff.find_entry($tag).ok_or(
        format!("Couldn't find tag {}",stringify!($tag)).to_string()
      )
    );
  );
}

macro_rules! fetch_ifd {
  ($tiff:expr, $tag:expr) => (
    try!(
      $tiff.find_first_ifd($tag).ok_or(
        format!("Couldn't find ifd with tag {}",stringify!($tag)).to_string()
      )
    );
  );
}

extern crate toml;
mod basics;
mod cfa;
mod tiff;
mod mrw;
mod arw;
mod mef;
mod orf;
mod srw;
mod erf;
mod kdc;
mod dcs;
mod rw2;
mod raf;
mod dcr;
use self::tiff::*;

pub static CAMERAS_TOML: &'static str = include_str!("../../data/cameras/all.toml");

pub trait Decoder {
  fn image(&self) -> Result<Image, String>;
}

#[derive(Debug, Clone)]
pub struct Buffer {
  buf: Vec<u8>,
}

impl Buffer {
  pub fn new(reader: &mut Read) -> Result<Buffer, String> {
    let mut buffer = Vec::new();
    if let Err(err) = reader.read_to_end(&mut buffer) {
      return Err(format!("IOError: {}", err).to_string())
    }
    buffer.extend([0;16].iter().cloned());
    Ok(Buffer {
      buf: buffer,
    })
  }
}

#[derive(Debug, Clone)]
pub struct Image {
  pub make: String,
  pub model: String,
  pub canonical_make: String,
  pub canonical_model: String,
  pub width: usize,
  pub height: usize,
  pub wb_coeffs: [f32;4],
  pub data: Box<[u16]>,
  pub whitelevels: [i64;4],
  pub blacklevels: [i64;4],
  pub color_matrix: [i64;12],
  pub cfa: cfa::CFA,
  pub crops: [i64;4],
}

#[derive(Debug, Clone)]
pub struct Camera {
  pub make: String,
  pub model: String,
  pub mode: String,
  pub canonical_make: String,
  pub canonical_model: String,
  whitelevels: [i64;4],
  blacklevels: [i64;4],
  color_matrix: [i64;12],
  cfa: cfa::CFA,
  crops: [i64;4],
  bps: u32,
  hints: Vec<String>,
}

impl Camera {
  pub fn find_hint(&self, hint: &str) -> bool {
    self.hints.contains(&(hint.to_string()))
  }

  pub fn update_from_toml(&mut self, ct: &toml::Table) {
    for (name, val) in ct {
      match name.as_ref() {
        "make" => {self.make = val.as_str().unwrap().to_string().clone();},
        "model" => {self.model = val.as_str().unwrap().to_string().clone();},
        "mode" => {self.mode = val.as_str().unwrap().to_string().clone();},
        "canonical_name" => {self.canonical_make = val.as_str().unwrap().to_string().clone();},
        "canonical_model" => {self.canonical_model = val.as_str().unwrap().to_string().clone();},
        "whitepoint" => {let white = val.as_integer().unwrap(); self.whitelevels = [white, white, white, white];},
        "blackpoint" => {let black = val.as_integer().unwrap(); self.blacklevels = [black, black, black, black];},
        "color_matrix" => {
          let matrix = val.as_slice().unwrap();
          for (i, val) in matrix.into_iter().enumerate() {
            self.color_matrix[i] = val.as_integer().unwrap();
          }
        },
        "crops" => {
          let crop_vals = val.as_slice().unwrap();
          for (i, val) in crop_vals.into_iter().enumerate() {
            self.crops[i] = val.as_integer().unwrap();
          }
        },
        "color_pattern" => {self.cfa = cfa::CFA::new(&val.as_str().unwrap().to_string());},
        "bps" => {self.bps = val.as_integer().unwrap() as u32;},
        "hints" => {
          self.hints = Vec::new();
          for hint in val.as_slice().unwrap() {
            self.hints.push(hint.as_str().unwrap().to_string());
          }
        },
        _ => {},
      }
    }
  }

  pub fn new() -> Camera {
    Camera {
      make: "".to_string(),
      model: "".to_string(),
      mode: "".to_string(),
      canonical_make: "".to_string(),
      canonical_model: "".to_string(),
      whitelevels: [0;4],
      blacklevels: [0;4],
      color_matrix : [0;12],
      cfa: cfa::CFA::new(""),
      crops: [0,0,0,0],
      bps: 0,
      hints: Vec::new(),
    }
  }
}

pub fn ok_image(camera: &Camera, width: u32, height: u32, wb_coeffs: [f32;4], image: Vec<u16>) -> Result<Image,String> {
  Ok(Image {
    make: camera.make.clone(),
    model: camera.model.clone(),
    canonical_make: camera.canonical_make.clone(),
    canonical_model: camera.canonical_model.clone(),
    width: width as usize,
    height: height as usize,
    wb_coeffs: wb_coeffs,
    data: image.into_boxed_slice(),
    blacklevels: camera.blacklevels,
    whitelevels: camera.whitelevels,
    color_matrix: camera.color_matrix,
    cfa: camera.cfa.clone(),
    crops: camera.crops,
  })
}

//pub fn ok_image_with_levels(camera: &Camera, width: u32, height: u32, wb_coeffs: [f32;4], whites: [i64;4], blacks: [i64;4], image: Vec<u16>) -> Result<Image,String> {
//  Ok(Image {
//    make: camera.make.clone(),
//    model: camera.model.clone(),
//    canonical_make: camera.canonical_make.clone(),
//    canonical_model: camera.canonical_model.clone(),
//    width: width,
//    height: height,
//    wb_coeffs: wb_coeffs,
//    data: image.into_boxed_slice(),
//    blacklevels: blacks,
//    whitelevels: whites,
//    color_matrix: camera.color_matrix,
//    dcraw_filters: camera.dcraw_filters,
//    crops: camera.crops,
//  })
//}

#[derive(Debug, Clone)]
pub struct RawLoader {
  pub cameras: HashMap<(String,String,String),Camera>,
}

impl RawLoader {
  pub fn new() -> RawLoader {
    let mut parser = toml::Parser::new(&CAMERAS_TOML);
    let toml = match parser.parse() {
      Some(val) => val,
      None => panic!(format!("Error parsing all.toml: {:?}", parser.errors)),
    };
    let mut cams = Vec::new();
    for camera in toml.get("cameras").unwrap().as_slice().unwrap() {
      let ct = camera.as_table().unwrap();
      let mut cam = Camera::new();
      cam.update_from_toml(ct);
      let basecam = cam.clone();
      cams.push(cam);

      match ct.get("modes") {
        Some(val) => {
          for mode in val.as_slice().unwrap() {
            let cmt = mode.as_table().unwrap();
            let mut mcam = basecam.clone();
            mcam.update_from_toml(cmt);
            cams.push(mcam);
          }
        },
        None => {},
      }
    }

    let mut map = HashMap::new();
    for cam in cams {
      map.insert((cam.make.clone(),cam.model.clone(),cam.mode.clone()), cam);
    }

    RawLoader{
      cameras: map,
    }
  }

  pub fn get_decoder<'b>(&'b self, buf: &'b Buffer) -> Result<Box<Decoder+'b>, String> {
    let buffer = &buf.buf;

    if mrw::is_mrw(buffer) {
      let dec = Box::new(mrw::MrwDecoder::new(buffer, &self));
      return Ok(dec as Box<Decoder>);
    }

    let tiff = try!(TiffIFD::new_file(buffer));

    macro_rules! use_decoder {
        ($dec:ty, $buf:ident, $tiff:ident, $rawdec:ident) => (Ok(Box::new(<$dec>::new($buf, $tiff, $rawdec)) as Box<Decoder>));
    }

    match fetch_tag!(tiff, Tag::Make).get_str().to_string().as_ref() {
      "SONY"                    => use_decoder!(arw::ArwDecoder, buffer, tiff, self),
      "Mamiya-OP Co.,Ltd."      => use_decoder!(mef::MefDecoder, buffer, tiff, self),
      "OLYMPUS IMAGING CORP."   => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "OLYMPUS CORPORATION"     => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "OLYMPUS OPTICAL CO.,LTD" => use_decoder!(orf::OrfDecoder, buffer, tiff, self),
      "SAMSUNG"                 => use_decoder!(srw::SrwDecoder, buffer, tiff, self),
      "SEIKO EPSON CORP."       => use_decoder!(erf::ErfDecoder, buffer, tiff, self),
      "EASTMAN KODAK COMPANY"   => use_decoder!(kdc::KdcDecoder, buffer, tiff, self),
      "KODAK"                   => use_decoder!(dcs::DcsDecoder, buffer, tiff, self),
      "Kodak"                   => use_decoder!(dcr::DcrDecoder, buffer, tiff, self),
      "Panasonic"               => use_decoder!(rw2::Rw2Decoder, buffer, tiff, self),
      "FUJIFILM"                => use_decoder!(raf::RafDecoder, buffer, tiff, self),
      make => Err(format!("Couldn't find a decoder for make \"{}\"", make).to_string()),
    }
  }

  pub fn check_supported_with_mode<'a>(&'a self, tiff: &'a TiffIFD, mode: &str) -> Result<&Camera, String> {
    let make = fetch_tag!(tiff, Tag::Make).get_str();
    let model = fetch_tag!(tiff, Tag::Model).get_str();

    match self.cameras.get(&(make.to_string(),model.to_string(),mode.to_string())) {
      Some(cam) => Ok(cam),
      None => Err(format!("Couldn't find camera \"{}\" \"{}\" mode \"{}\"", make, model, mode)),
    }
  }

  pub fn check_supported<'a>(&'a self, tiff: &'a TiffIFD) -> Result<&Camera, String> {
    self.check_supported_with_mode(tiff, "")
  }

  pub fn decode(&self, reader: &mut Read) -> Result<Image, String> {
    let buffer = try!(Buffer::new(reader));
    let decoder = try!(self.get_decoder(&buffer));
    decoder.image()
  }

  pub fn decode_safe(&self, path: &str) -> Result<Image, String> {
    match panic::catch_unwind(|| {
      let mut f = match File::open(path) {
        Ok(val) => val,
        Err(e) => {return Err(e.description().to_string())},
      };
      self.decode(&mut f)
    }) {
      Ok(val) => val,
      Err(_) => Err("Caught a panic while decoding, please file a bug and attach a sample file".to_string()),
    }
  }
}
<|endoftext|>


<commit_before>use std::ops::Fn;
use std::error::FromError;
use std::sync::mpsc;

use std::path::Path as StdPath;

// @todo, change to std::io once stable.
use std::old_io as io;

//================================================================================
// Error:
//================================================================================

pub enum Error {
	Io( io::IoError ),
	NotifyError( String ),
	PathNotFound,
	NotImplemented,
}

impl FromError<io::IoError> for Error {
	fn from_error( from: io::IoError ) -> Error {
		Error::Io( from )
	}
}

//================================================================================
// Misc typedefs:
//================================================================================

pub type Path = StdPath;
pub type NotifyResult<T> = Result<T, Error>;
pub type R = NotifyResult<()>;

//================================================================================
// Configuration:
//================================================================================

pub type RecursionFilter<'a> = Option<&'a (Fn( &Path ) -> bool + 'a)>;
pub type RecursionLimit = Option<usize>;
pub struct Configuration<'a> {
	subscribe: Operations,
	follow_symlinks: bool,
	recursion_limit: RecursionLimit,
	recursion_filter: RecursionFilter<'a>,
}

//================================================================================
// Events:
//================================================================================

mod operations;
use self::operations::Operations;

pub struct Event<'a> {
	pub path: Option<&'a Path>,
	pub op: NotifyResult<Operations>,
}

pub type EventSender<'a> = mpsc::Sender<Event<'a>>;

//================================================================================
// Notifier trait:
//================================================================================

pub trait FsNotifier<'a> : Drop {
	fn new( sender: EventSender, config: Configuration<'a> ) -> NotifyResult<Self>;

	fn add( &self, path: &Path ) -> R;

	fn remove( &self, path: &Path ) -> R;

	fn start( &self ) -> R;

	fn stop( &self ) -> R;
}<commit_msg>fsnotify/mod.rs, added documentation.<commit_after>use std::ops::Fn;
use std::error::FromError;
use std::sync::mpsc;

use std::path::Path as StdPath;

// @todo, change to std::io once stable.
use std::old_io as io;

//================================================================================
// Error:
//================================================================================

/**
 * `Error` contains all the possible errors for `fsnotify`.
 */
pub enum Error {
	Io( io::IoError ),
	NotifyError( String ),
	PathNotFound,
	NotImplemented,
}

impl FromError<io::IoError> for Error {
	fn from_error( from: io::IoError ) -> Error {
		Error::Io( from )
	}
}

//================================================================================
// Misc typedefs:
//================================================================================

pub type Path = StdPath;

/**
 * The `Result` of an operation, with either `T` (success), `Error` (failure).
 */
pub type NotifyResult<T> = Result<T, Error>;

/**
 * `R = NotifyResult<()`
 * Indicates either success or failure of an operation.
 */
pub type R = NotifyResult<()>;

//================================================================================
// Configuration:
//================================================================================

/**
 * `RecursionFilter`, a predicate function that, when recursion is enabled,
 * tells the `FsNotifier` if it should subscribe to a sub-directory.
 *
 * If the value returned is true, it will subscribe, otherwise, it will not.
 */
pub type RecursionFilter<'a> = Option<&'a (Fn( &Path ) -> bool + 'a)>;

/**
 * `RecursionLimit` denoted what the maximum recursion depth is starting
 * from a tree point that was explicitly added to the watch list.
 *
 * `None` means that there is no limit, it will do recursion forever.
 * `Some(0)` means that no recursion will happen at all.
 */
pub type RecursionLimit = Option<usize>;

/**
 * `Configuration` provides configuration for a `FsNotifier`.
 *
 * It configures what operations to subcribe to if applicable for the platform.
 * This lets you avoid tracking events of no interest.
 *
 * It also configures if symlinks should be followed or not (`follow_symlinks`).
 *
 * Lastly, it configures the `RecursionLimit`, and `RecursionFilter`.
 */
pub struct Configuration<'a> {
	subscribe: Operations,
	follow_symlinks: bool,
	recursion_limit: RecursionLimit,
	recursion_filter: RecursionFilter<'a>,
}

//================================================================================
// Events:
//================================================================================

mod operations;
use self::operations::Operations;

/**
 * Event:s are passed to the `EventSender`.
 * It has information about the path that the operations that happened on it.
 */
pub struct Event<'a> {
	pub path: Option<&'a Path>,
	pub op: NotifyResult<Operations>,
}

/**
 * `EventSender`, a `Sender` for an `Event`.
 */
pub type EventSender<'a> = mpsc::Sender<Event<'a>>;

//================================================================================
// Notifier trait:
//================================================================================

/**
 * FsNotifier, a trait, is the generic interface for
 * subscribing to file system change events.
 *
 * It provides 4 basic operations:
 * 	+ `.add( &Path )`
 * 	+ `.remove( &Path )`
 * 	+ `.start()`
 * 	+ `.stop()`
 *
 * As this trait deals with I/O interaction with the operating system, things can fail.
 * Thus, all methods return a `NotifyResult<T> = Result<T, Error>`.
 * All methods but the first, `.new(...)` return `R = NotifyResult<()>`,
 * indicating either success or failure of the operation.
 */
pub trait FsNotifier<'a> : Drop {
	/**
	 * Constructs a `FsNotifier`.
	 *
	 * The sender is an `EventSender` which the notifier will send events through.
	 * The config: `Configuration` contains information about how to handle recursion,
	 * symlinks and what file system operations that should be tracked.
	 *
	 * The returned value is a the notifier, wrapped in a `NotifyResult`.
	 * This is done because there might be I/O failures depending on the system.
	 */
	fn new( sender: EventSender, config: Configuration<'a> ) -> NotifyResult<Self>;

	/**
	 * Adds a path to track to the notifier.
	 *
	 * Returned is a Result<()>, that indicates either failure or success.
	 */
	fn add( &self, path: &Path ) -> R;

	/**
	 * Tells the notifier to stop tracking a path.
	 *
	 * Returned is a Result<()>, that indicates either failure or success.
	 */
	fn remove( &self, path: &Path ) -> R;

	/**
	 * Tells the notifier to start the tracking.
	 * This operation is blocking, therefore it should be wrapped in a thread.
	 *
	 * Returned is a Result<()>, that indicates either failure or success.
	 */
	fn start( &self ) -> R;

	/**
	 * Tells the notifier to stop the tracking.
	 *
	 * Returned is a Result<()>, that indicates either failure or success.
	 */
	fn stop( &self ) -> R;
}<|endoftext|>


<commit_before>//! Memory backing.

use ffi;
use traits::Zero;
use libc;
use std::{mem, ptr, raw};
use std::ops::{Deref, DerefMut};

struct RawVec<T> {
    dat: *mut T,
    len: usize
}
impl<T> RawVec<T> {
    unsafe fn uninit(n: usize) -> RawVec<T> {
        let size = n.checked_mul(mem::size_of::<T>()).expect("FftwVec::uninit: size overflow");
        let dat = ffi::fftw_malloc(size as libc::size_t);

        if dat.is_null() {
            panic!("FftwVec::uninit: fftw_malloc failed");
        }

        RawVec { dat: dat as *mut T, len: n }
    }
}

impl<T> Deref for RawVec<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        unsafe {mem::transmute(raw::Slice { data: self.dat as *const T, len: self.len })}
    }
}
impl<T> DerefMut for RawVec<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        unsafe {mem::transmute(raw::Slice { data: self.dat as *const T, len: self.len })}
    }
}

impl<T> Drop for RawVec<T> {
    fn drop(&mut self) {
        unsafe {ffi::fftw_free(self.dat as *mut libc::c_void)}
    }
}

/// A non-resizable vector allocated using FFTWs allocator.
///
/// This implements `Deref<[T]>` and `DerefMut<[T]>` and so can be
/// used nearly-interchangeably with slices.
pub struct FftwVec<T> {
    dat: RawVec<T>
}

impl<T> FftwVec<T> {
    /// Allocate a `FftwVec` without initialising the elements at all.
    pub unsafe fn uninit(n: usize) -> FftwVec<T> {
        FftwVec { dat: RawVec::uninit(n) }
    }
}
impl<T> Deref for FftwVec<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        &*self.dat
    }
}
impl<T> DerefMut for FftwVec<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        &mut *self.dat
    }
}

impl<T> Drop for FftwVec<T> {
    fn drop(&mut self) {
        // free everything
        for p in self.iter() {
            unsafe {ptr::read(p);}
        }
    }
}

struct PartialVec<T> {
    dat: RawVec<T>,
    idx: usize
}

impl<T: Zero> FftwVec<T> {
    /// Allocate a `FftwVec` of length `n` containing zeros.
    pub fn zeros(n: usize) -> FftwVec<T> {
        let mut v = PartialVec {
            dat: unsafe {RawVec::uninit(n)},
            idx: 0,
        };

        while v.idx < n {
            unsafe {
                ptr::write(v.dat.dat.offset(v.idx as isize), Zero::zero());
            }
            v.idx += 1
        }

        unsafe {
            let ret = FftwVec {
                dat: ptr::read(&v.dat)
            };
            mem::forget(v);
            ret
        }
    }
}

impl<T> Drop for PartialVec<T> {
    fn drop(&mut self) {
        for p in self.dat[..self.idx].iter() {
            unsafe {ptr::read(p);} // FIXME: overflow
        }
    }
}

#[cfg(test)]
mod tests {
    use mem::FftwVec;

    #[test]
    fn fftw_vec() {
        let mut v = FftwVec::<usize>::zeros(100);
        for (i, x) in v.iter_mut().enumerate() {
            *x = i;
        }
        let mut i = 0;
        for x in v.iter() {
            assert_eq!(*x, i);
            i += 1;
        }
        assert_eq!(i, 100);
    }
}
<commit_msg>fix typo in mem.rs<commit_after>//! Memory backing.

use ffi;
use traits::Zero;
use libc;
use std::{mem, ptr, raw};
use std::ops::{Deref, DerefMut};

struct RawVec<T> {
    dat: *mut T,
    len: usize
}
impl<T> RawVec<T> {
    unsafe fn uninit(n: usize) -> RawVec<T> {
        let size = n.checked_mul(mem::size_of::<T>()).expect("FftwVec::uninit: size overflow");
        let dat = ffi::fftw_malloc(size as libc::size_t);

        if dat.is_null() {
            panic!("FftwVec::uninit: fftw_malloc failed");
        }

        RawVec { dat: dat as *mut T, len: n }
    }
}

impl<T> Deref for RawVec<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        unsafe {mem::transmute(raw::Slice { data: self.dat as *const T, len: self.len })}
    }
}
impl<T> DerefMut for RawVec<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        unsafe {mem::transmute(raw::Slice { data: self.dat as *const T, len: self.len })}
    }
}

impl<T> Drop for RawVec<T> {
    fn drop(&mut self) {
        unsafe {ffi::fftw_free(self.dat as *mut libc::c_void)}
    }
}

/// A non-resizable vector allocated using FFTWs allocator.
///
/// This implements `Deref<[T]>` and `DerefMut<[T]>` and so can be
/// used nearly-interchangeably with slices.
pub struct FftwVec<T> {
    dat: RawVec<T>
}

impl<T> FftwVec<T> {
    /// Allocate a `FftwVec` without initialising the elements at all.
    pub unsafe fn uninit(n: usize) -> FftwVec<T> {
        FftwVec { dat: RawVec::uninit(n) }
    }
}
impl<T> Deref for FftwVec<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        &*self.dat
    }
}
impl<T> DerefMut for FftwVec<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        &mut *self.dat
    }
}

impl<T> Drop for FftwVec<T> {
    fn drop(&mut self) {
        // free everything
        for p in self.iter() {
            unsafe {ptr::read(p);}
        }
    }
}

struct PartialVec<T> {
    dat: RawVec<T>,
    idx: usize
}

impl<T: Zero> FftwVec<T> {
    /// Allocate a `FftwVec` of length `n` containing zeros.
    pub fn zeros(n: usize) -> FftwVec<T> {
        let mut v = PartialVec {
            dat: unsafe {RawVec::uninit(n)},
            idx: 0,
        };

        while v.idx < n {
            unsafe {
                ptr::write(v.dat.dat.offset(v.idx as isize), Zero::zero());
            }
            v.idx += 1
        }

        unsafe {
            let ret = FftwVec {
                dat: ptr::read(&v.dat)
            };
            mem::forget(v);
            ret
        }
    }
}

impl<T> Drop for PartialVec<T> {
    fn drop(&mut self) {
        for p in self.dat[..self.idx].iter() {
            unsafe {ptr::read(p);}
        }
    }
}

#[cfg(test)]
mod tests {
    use mem::FftwVec;

    #[test]
    fn fftw_vec() {
        let mut v = FftwVec::<usize>::zeros(100);
        for (i, x) in v.iter_mut().enumerate() {
            *x = i;
        }
        let mut i = 0;
        for x in v.iter() {
            assert_eq!(*x, i);
            i += 1;
        }
        assert_eq!(i, 100);
    }
}
<|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]

const cobalt_yml: &'static [u8] = b"name: cobalt blog
source: \".\"
dest: \"build\"
ignore:
  - .git/*
  - build/*
";

const default_liquid: &'static [u8] = b"<!DOCTYPE html>

        <meta charset=\"utf-8\">
        {% if is_post %}
          <title>{{ title }}</title>
        {% else %}
       	  <title>Cobalt.rs Blog</title>
        {% endif %}

      {% if is_post %}
        {% include '_layouts/post.liquid' %}
      {% else %}
        {{ content }}
      {% endif %}

";

const post_liquid: &'static [u8] = b"

{{ title }}

    {{content}}

";

const post_1_md: &'static [u8] = b"extends: default.liquid

title: First Post
date: 14 January 2016 21:00:30 -0500
---

# This is our first Post!

Welcome to the first post ever on cobalt.rs!
";

const index_liquid: &'static [u8] = b"extends: default.liquid
---

Blog!

    {% for post in posts %}

{{post.title}}

{{ post.title }}

    {% endfor %}

";

use std::path::Path;
use std::fs::{DirBuilder, OpenOptions};
use std::io::Write;
use error::Result;
use config::Config;

pub fn create_new_project<P: AsRef<Path>>(dest: P) -> Result<()> {
    let dest = dest.as_ref();

    try!(create_folder(&dest));

    try!(create_file(&dest.join(".cobalt.yml"), cobalt_yml));
    try!(create_file(&dest.join("index.liquid"), index_liquid));

    try!(create_folder(&dest.join("_layouts")));
    try!(create_file(&dest.join("_layouts/default.liquid"), default_liquid));
    try!(create_file(&dest.join("_layouts/post.liquid"), post_liquid));

    try!(create_folder(&dest.join("posts")));
    try!(create_file(&dest.join("posts/post-1.md"), post_1_md));

    Ok(())
}

pub fn create_new_document(doc_type: &str, name: &str, config: &Config) -> Result<()> {
    let path = Path::new(&config.source);
    let full_path = &path.join(&config.posts).join(name);

    match doc_type {
        "page" => create_file(name, index_liquid)?,
        "post" => create_file(full_path, post_1_md)?,
        _ => bail!("Unsupported document type {}", doc_type),
    }

    Ok(())
}

fn create_folder<P: AsRef<Path>>(path: P) -> Result<()> {
    trace!("Creating folder {:?}", &path.as_ref());

    try!(DirBuilder::new().recursive(true).create(path));

    Ok(())
}

fn create_file<P: AsRef<Path>>(name: P, content: &[u8]) -> Result<()> {
    trace!("Creating file {:?}", &name.as_ref());

    let mut file = try!(OpenOptions::new()
                            .write(true)
                            .create_new(true)
                            .open(name));

    file.write_all(content)?;

    Ok(())
}
<commit_msg>refactor: Clean up projet init code<commit_after>use std::path::Path;
use std::fs::{DirBuilder, OpenOptions};
use std::io::Write;
use error::Result;
use config::Config;

const COBALT_YML: &'static [u8] = b"name: cobalt blog
source: \".\"
dest: \"build\"
ignore:
  - .git/*
  - build/*
";

const DEFAULT_LAYOUT: &'static [u8] = b"<!DOCTYPE html>

        <meta charset=\"utf-8\">
        {% if is_post %}
          <title>{{ title }}</title>
        {% else %}
          <title>Cobalt.rs Blog</title>
        {% endif %}

      {% if is_post %}
        {% include '_layouts/post.liquid' %}
      {% else %}
        {{ content }}
      {% endif %}

";

const POST_LAYOUT: &'static [u8] = b"

{{ title }}

    {{content}}

";

const POST_MD: &'static [u8] = b"extends: default.liquid

title: First Post
date: 14 January 2016 21:00:30 -0500
---

# This is our first Post!

Welcome to the first post ever on cobalt.rs!
";

const INDEX_LIQUID: &'static [u8] = b"extends: default.liquid
---

Blog!

    {% for post in posts %}

{{post.title}}

{{ post.title }}

    {% endfor %}

";

pub fn create_new_project<P: AsRef<Path>>(dest: P) -> Result<()> {
    let dest = dest.as_ref();

    create_folder(&dest)?;

    create_file(&dest.join(".cobalt.yml"), COBALT_YML)?;
    create_file(&dest.join("index.liquid"), INDEX_LIQUID)?;

    create_folder(&dest.join("_layouts"))?;
    create_file(&dest.join("_layouts/default.liquid"), DEFAULT_LAYOUT)?;
    create_file(&dest.join("_layouts/post.liquid"), POST_LAYOUT)?;

    create_folder(&dest.join("posts"))?;
    create_file(&dest.join("posts/post-1.md"), POST_MD)?;

    Ok(())
}

pub fn create_new_document(doc_type: &str, name: &str, config: &Config) -> Result<()> {
    let path = Path::new(&config.source);
    let full_path = &path.join(&config.posts).join(name);

    match doc_type {
        "page" => create_file(name, INDEX_LIQUID)?,
        "post" => create_file(full_path, POST_MD)?,
        _ => bail!("Unsupported document type {}", doc_type),
    }

    Ok(())
}

fn create_folder<P: AsRef<Path>>(path: P) -> Result<()> {
    trace!("Creating folder {:?}", &path.as_ref());

    DirBuilder::new().recursive(true).create(path)?;

    Ok(())
}

fn create_file<P: AsRef<Path>>(name: P, content: &[u8]) -> Result<()> {
    trace!("Creating file {:?}", &name.as_ref());

    let mut file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(name)?;

    file.write_all(content)?;

    Ok(())
}
<|endoftext|>


<commit_before>use crate::agents::*;
use crate::utils::{agent::*, LimitConfig};
use crate::web::Session;
use std::collections::hash_map::{Entry, HashMap};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Mutex;
use url::Url;

struct Expiring<T> {
    inner: T,
    expires: Instant,
}

impl<T> Expiring<T> {
    fn from_duration(inner: T, duration: Duration) -> Self {
        let expires = Instant::now() + duration;
        Expiring { inner, expires }
    }

    fn is_alive(&self) -> bool {
        self.expires > Instant::now()
    }
}

/// Message sent at an interval to collect garbage.
struct Gc;
impl Message for Gc {
    type Reply = ();
}

/// A cache slot in the cache `HashMap`.
///
/// We want to lock these individually while a fetch is in progress, so multiple requests for the
/// same URL result in only one fetch. Therefore, we use an `Arc<Mutex<_>>` to carry slots around,
/// and within an `Option` which indicates whether cache is actually present (despite the hash map
/// entry existing or not, which does not indicate anything).
type CacheSlot = Arc<Mutex<Option<Expiring<String>>>>;

/// Store implementation using memory.
pub struct MemoryStore {
    /// TTL of session keys
    expire_sessions: Duration,
    /// TTL of cache keys
    expire_cache: Duration,
    /// Configuration for per-email rate limiting.
    limit_per_email_config: LimitConfig,
    /// The agent used for fetching on cache miss.
    fetcher: Addr<FetchAgent>,
    /// Session storage.
    sessions: HashMap<String, Expiring<Session>>,
    /// Cache storage.
    cache: HashMap<Url, CacheSlot>,
    /// Rate limit storage.
    limits: HashMap<IncrAndTestLimit, Expiring<usize>>,
}

impl MemoryStore {
    pub fn new(
        expire_sessions: Duration,
        expire_cache: Duration,
        limit_per_email_config: LimitConfig,
        fetcher: Addr<FetchAgent>,
    ) -> Self {
        log::warn!("Storing sessions in memory.");
        log::warn!("Note that sessions will be lost on restart!");

        MemoryStore {
            expire_sessions,
            expire_cache,
            limit_per_email_config,
            fetcher,
            sessions: HashMap::new(),
            cache: HashMap::new(),
            limits: HashMap::new(),
        }
    }
}

impl Agent for MemoryStore {
    fn started(addr: &Addr<Self>) {
        // Start the garbage collection loop.
        let addr = addr.clone();
        tokio::spawn(async move {
            let mut interval = tokio::time::interval(Duration::from_secs(60));
            loop {
                interval.tick().await;
                addr.send(Gc).await;
            }
        });
    }
}

impl Handler<Gc> for MemoryStore {
    fn handle(&mut self, _message: Gc, reply: ReplySender<Gc>) {
        // TODO
        reply.send(())
    }
}

impl Handler<SaveSession> for MemoryStore {
    fn handle(&mut self, message: SaveSession, reply: ReplySender<SaveSession>) {
        self.sessions.insert(
            message.session_id,
            Expiring::from_duration(message.data, self.expire_sessions),
        );
        reply.send(Ok(()))
    }
}

impl Handler<GetSession> for MemoryStore {
    fn handle(&mut self, message: GetSession, reply: ReplySender<GetSession>) {
        let data = self
            .sessions
            .get(&message.session_id)
            .filter(|entry| entry.is_alive())
            .map(|entry| entry.inner.clone());
        reply.send(Ok(data))
    }
}

impl Handler<DeleteSession> for MemoryStore {
    fn handle(&mut self, message: DeleteSession, reply: ReplySender<DeleteSession>) {
        self.sessions.remove(&message.session_id);
        reply.send(Ok(()))
    }
}

impl Handler<CachedFetch> for MemoryStore {
    fn handle(&mut self, message: CachedFetch, reply: ReplySender<CachedFetch>) {
        let fetcher = self.fetcher.clone();
        let slot = self.cache.entry(message.url.clone()).or_default().clone();
        let expire_cache = self.expire_cache;
        reply.later(move || async move {
            let mut slot = slot.lock().await;
            if let Some(entry) = slot.as_ref().filter(|entry| entry.is_alive()) {
                return Ok(entry.inner.clone());
            }
            let result = fetcher.send(FetchUrl { url: message.url }).await?;
            let ttl = std::cmp::max(expire_cache, result.max_age);
            *slot = Some(Expiring::from_duration(result.data.clone(), ttl));
            Ok(result.data)
        });
    }
}

impl Handler<IncrAndTestLimit> for MemoryStore {
    fn handle(&mut self, message: IncrAndTestLimit, reply: ReplySender<IncrAndTestLimit>) {
        let config = match message {
            IncrAndTestLimit::PerEmail { .. } => self.limit_per_email_config,
        };
        let count: usize = match self.limits.entry(message) {
            Entry::Occupied(mut entry) => {
                let mut expiring = entry.get_mut();
                if expiring.expires <= Instant::now() {
                    *expiring = Expiring::from_duration(1, config.duration);
                    1
                } else {
                    expiring.inner += 1;
                    expiring.inner
                }
            }
            Entry::Vacant(entry) => {
                entry.insert(Expiring::from_duration(1, config.duration));
                1
            }
        };
        reply.send(Ok(count <= config.max_count));
    }
}

impl StoreSender for Addr<MemoryStore> {}
<commit_msg>Implement memory store garbage collection<commit_after>use crate::agents::*;
use crate::utils::{agent::*, LimitConfig};
use crate::web::Session;
use std::collections::hash_map::{Entry, HashMap};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::Mutex;
use url::Url;

struct Expiring<T> {
    inner: T,
    expires: Instant,
}

impl<T> Expiring<T> {
    fn from_duration(inner: T, duration: Duration) -> Self {
        let expires = Instant::now() + duration;
        Expiring { inner, expires }
    }

    fn is_alive(&self) -> bool {
        self.expires > Instant::now()
    }
}

/// Message sent at an interval to collect garbage.
struct Gc;
impl Message for Gc {
    type Reply = ();
}

/// A cache slot in the cache `HashMap`.
///
/// We want to lock these individually while a fetch is in progress, so multiple requests for the
/// same URL result in only one fetch. Therefore, we use an `Arc<Mutex<_>>` to carry slots around,
/// and within an `Option` which indicates whether cache is actually present (despite the hash map
/// entry existing or not, which does not indicate anything).
type CacheSlot = Arc<Mutex<Option<Expiring<String>>>>;

/// Store implementation using memory.
pub struct MemoryStore {
    /// TTL of session keys
    expire_sessions: Duration,
    /// TTL of cache keys
    expire_cache: Duration,
    /// Configuration for per-email rate limiting.
    limit_per_email_config: LimitConfig,
    /// The agent used for fetching on cache miss.
    fetcher: Addr<FetchAgent>,
    /// Session storage.
    sessions: HashMap<String, Expiring<Session>>,
    /// Cache storage.
    cache: HashMap<Url, CacheSlot>,
    /// Rate limit storage.
    limits: HashMap<IncrAndTestLimit, Expiring<usize>>,
}

impl MemoryStore {
    pub fn new(
        expire_sessions: Duration,
        expire_cache: Duration,
        limit_per_email_config: LimitConfig,
        fetcher: Addr<FetchAgent>,
    ) -> Self {
        log::warn!("Storing sessions in memory.");
        log::warn!("Note that sessions will be lost on restart!");

        MemoryStore {
            expire_sessions,
            expire_cache,
            limit_per_email_config,
            fetcher,
            sessions: HashMap::new(),
            cache: HashMap::new(),
            limits: HashMap::new(),
        }
    }
}

impl Agent for MemoryStore {
    fn started(addr: &Addr<Self>) {
        // Start the garbage collection loop.
        let addr = addr.clone();
        tokio::spawn(async move {
            let mut interval = tokio::time::interval(Duration::from_secs(60));
            loop {
                interval.tick().await;
                addr.send(Gc).await;
            }
        });
    }
}

impl Handler<Gc> for MemoryStore {
    fn handle(&mut self, _message: Gc, reply: ReplySender<Gc>) {
        self.sessions = self
            .sessions
            .drain()
            .filter(|(_, ref entry)| entry.is_alive())
            .collect();
        self.cache = self
            .cache
            .drain()
            .filter(|(_, ref slot)| {
                // For cache items, only remove if we have the sole reference. Otherwise, a fetch
                // might still be in progress. Locking should be immediate if true.
                if let Ok(maybe_entry) = slot.try_lock() {
                    Arc::strong_count(slot) > 1
                        || maybe_entry
                            .as_ref()
                            .filter(|entry| entry.is_alive())
                            .is_some()
                } else {
                    true
                }
            })
            .collect();
        self.limits = self
            .limits
            .drain()
            .filter(|(_, ref entry)| entry.is_alive())
            .collect();
        reply.send(())
    }
}

impl Handler<SaveSession> for MemoryStore {
    fn handle(&mut self, message: SaveSession, reply: ReplySender<SaveSession>) {
        self.sessions.insert(
            message.session_id,
            Expiring::from_duration(message.data, self.expire_sessions),
        );
        reply.send(Ok(()))
    }
}

impl Handler<GetSession> for MemoryStore {
    fn handle(&mut self, message: GetSession, reply: ReplySender<GetSession>) {
        let data = self
            .sessions
            .get(&message.session_id)
            .filter(|entry| entry.is_alive())
            .map(|entry| entry.inner.clone());
        reply.send(Ok(data))
    }
}

impl Handler<DeleteSession> for MemoryStore {
    fn handle(&mut self, message: DeleteSession, reply: ReplySender<DeleteSession>) {
        self.sessions.remove(&message.session_id);
        reply.send(Ok(()))
    }
}

impl Handler<CachedFetch> for MemoryStore {
    fn handle(&mut self, message: CachedFetch, reply: ReplySender<CachedFetch>) {
        let fetcher = self.fetcher.clone();
        let slot = self.cache.entry(message.url.clone()).or_default().clone();
        let expire_cache = self.expire_cache;
        reply.later(move || async move {
            let mut slot = slot.lock().await;
            if let Some(entry) = slot.as_ref().filter(|entry| entry.is_alive()) {
                return Ok(entry.inner.clone());
            }
            let result = fetcher.send(FetchUrl { url: message.url }).await?;
            let ttl = std::cmp::max(expire_cache, result.max_age);
            *slot = Some(Expiring::from_duration(result.data.clone(), ttl));
            Ok(result.data)
        });
    }
}

impl Handler<IncrAndTestLimit> for MemoryStore {
    fn handle(&mut self, message: IncrAndTestLimit, reply: ReplySender<IncrAndTestLimit>) {
        let config = match message {
            IncrAndTestLimit::PerEmail { .. } => self.limit_per_email_config,
        };
        let count: usize = match self.limits.entry(message) {
            Entry::Occupied(mut entry) => {
                let mut expiring = entry.get_mut();
                if expiring.expires <= Instant::now() {
                    *expiring = Expiring::from_duration(1, config.duration);
                    1
                } else {
                    expiring.inner += 1;
                    expiring.inner
                }
            }
            Entry::Vacant(entry) => {
                entry.insert(Expiring::from_duration(1, config.duration));
                1
            }
        };
        reply.send(Ok(count <= config.max_count));
    }
}

impl StoreSender for Addr<MemoryStore> {}
<|endoftext|>


<commit_before>use vm::{OpCode, XAdd};
use ast::{Ast,NumberExprAst};
use lexer::{TokInt};

pub fn gen_add(a: &Ast, b: &Ast) -> OpCode {
    match *a {
        NumberExprAst(i) => match *b {
            NumberExprAst(ii) => XAdd(i, ii),
            _ => fail!("[gen_add] expected an integer.")
        },
        _ => fail!("[gen_add] expected an integer.")
    }
}<commit_msg>more tests<commit_after>use vm::{OpCode, XAdd};
use ast::{Ast,NumberExprAst};
use lexer::{TokInt};

pub fn gen_add(a: &Ast, b: &Ast) -> OpCode {
    match *a {
        NumberExprAst(i) => match *b {
            NumberExprAst(ii) => XAdd(i, ii),
            _ => fail!("[gen_add] expected an integer.")
        },
        _ => fail!("[gen_add] expected an integer.")
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use ast::{NumberExprAst};
    use vm::{XAdd};

    #[test]
    fn test_gen_add() {
        let op = gen_add(&NumberExprAst(5), &NumberExprAst(2));
        match op {
            XAdd(a, b) => {
                assert_eq!(a, 5);
                assert_eq!(b, 2);
            },
            _ => fail!("Failed")
        }
    }
}<|endoftext|>