<commit_before>#![allow(
    clippy::cast_lossless,
    clippy::cast_possible_wrap,
    clippy::derive_partial_eq_without_eq
)]

use indoc::indoc;
use serde_derive::Deserialize;
use serde_yaml::Value;
use std::collections::BTreeMap;
use std::fmt::Debug;

fn test_de<'de, T>(yaml: &'de str, expected: &T)
where
    T: serde::de::Deserialize<'de> + PartialEq + Debug,
{
    let deserialized: T = serde_yaml::from_str(yaml).unwrap();
    assert_eq!(*expected, deserialized);

    serde_yaml::from_str::<serde_yaml::Value>(yaml).unwrap();
    serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

fn test_de_seed<'de, T, S>(yaml: &'de str, seed: S, expected: &T)
where
    T: PartialEq + Debug,
    S: serde::de::DeserializeSeed<'de, Value = T>,
{
    let deserialized: T = serde_yaml::seed::from_str_seed(yaml, seed).unwrap();
    assert_eq!(*expected, deserialized);

    serde_yaml::from_str::<serde_yaml::Value>(yaml).unwrap();
    serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

#[test]
fn test_borrowed() {
    let yaml = indoc! {"
        - first
        - second
        - third
    "};
    let expected = vec!["first", "second", "third"];
    test_de(yaml, &expected);
}

#[test]
fn test_alias() {
    let yaml = indoc! {"
        first:
          &alias
          1
        second:
          *alias
        third: 3
    "};
    let mut expected = BTreeMap::new();
    expected.insert(String::from("first"), 1);
    expected.insert(String::from("second"), 1);
    expected.insert(String::from("third"), 3);
    test_de(yaml, &expected);
}

#[test]
fn test_option() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: Option<f64>,
        b: Option<String>,
        c: Option<bool>,
    }
    let yaml = indoc! {"
        b:
        c: true
    "};
    let expected = Data {
        a: None,
        b: None,
        c: Some(true),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_option_alias() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: Option<f64>,
        b: Option<String>,
        c: Option<bool>,
        d: Option<f64>,
        e: Option<String>,
        f: Option<bool>,
    }
    let yaml = indoc! {"
        none_f:
          &none_f
          ~
        none_s:
          &none_s
          ~
        none_b:
          &none_b
          ~

        some_f:
          &some_f
          1.0
        some_s:
          &some_s
          x
        some_b:
          &some_b
          true

        a: *none_f
        b: *none_s
        c: *none_b
        d: *some_f
        e: *some_s
        f: *some_b
    "};
    let expected = Data {
        a: None,
        b: None,
        c: None,
        d: Some(1.0),
        e: Some("x".to_owned()),
        f: Some(true),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_enum_alias() {
    #[derive(Deserialize, PartialEq, Debug)]
    enum E {
        A,
        B(u8, u8),
    }
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: E,
        b: E,
    }
    let yaml = indoc! {"
        aref:
          &aref
          A
        bref:
          &bref
          B:
            - 1
            - 2

        a: *aref
        b: *bref
    "};
    let expected = Data {
        a: E::A,
        b: E::B(1, 2),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_enum_tag() {
    #[derive(Deserialize, PartialEq, Debug)]
    enum E {
        A(String),
        B(String),
    }
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: E,
        b: E,
    }
    let yaml = indoc! {"
        a: !A foo
        b: !B bar
    "};
    let expected = Data {
        a: E::A("foo".into()),
        b: E::B("bar".into()),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_number_as_string() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Num {
        value: String,
    }
    let yaml = indoc! {"
        # Cannot be represented as u128
        value: 340282366920938463463374607431768211457
    "};
    let expected = Num {
        value: "340282366920938463463374607431768211457".to_owned(),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_i128_big() {
    let expected: i128 = i64::MIN as i128 - 1;
    let yaml = indoc! {"
        -9223372036854775809
    "};
    assert_eq!(expected, serde_yaml::from_str::<i128>(yaml).unwrap());

    let octal = indoc! {"
        -0o1000000000000000000001
    "};
    assert_eq!(expected, serde_yaml::from_str::<i128>(octal).unwrap());
}

#[test]
fn test_u128_big() {
    let expected: u128 = u64::MAX as u128 + 1;
    let yaml = indoc! {"
        18446744073709551616
    "};
    assert_eq!(expected, serde_yaml::from_str::<u128>(yaml).unwrap());

    let octal = indoc! {"
        0o2000000000000000000000
    "};
    assert_eq!(expected, serde_yaml::from_str::<u128>(octal).unwrap());
}

#[test]
fn test_number_alias_as_string() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Num {
        version: String,
        value: String,
    }
    let yaml = indoc! {"
        version: &a 1.10
        value: *a
    "};
    let expected = Num {
        version: "1.10".to_owned(),
        value: "1.10".to_owned(),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_de_mapping() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct Data {
        pub substructure: serde_yaml::Mapping,
    }
    let yaml = indoc! {"
        substructure:
          a: 'foo'
          b: 'bar'
    "};

    let mut expected = Data {
        substructure: serde_yaml::Mapping::new(),
    };
    expected.substructure.insert(
        serde_yaml::Value::String("a".to_owned()),
        serde_yaml::Value::String("foo".to_owned()),
    );
    expected.substructure.insert(
        serde_yaml::Value::String("b".to_owned()),
        serde_yaml::Value::String("bar".to_owned()),
    );

    test_de(yaml, &expected);
}

#[test]
fn test_bomb() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct Data {
        expected: String,
    }

    // This would deserialize an astronomical number of elements if we were
    // vulnerable.
    let yaml = indoc! {"
        a: &a ~
        b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
        c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
        d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
        e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]
        f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]
        g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]
        h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]
        i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]
        j: &j [*i,*i,*i,*i,*i,*i,*i,*i,*i]
        k: &k [*j,*j,*j,*j,*j,*j,*j,*j,*j]
        l: &l [*k,*k,*k,*k,*k,*k,*k,*k,*k]
        m: &m [*l,*l,*l,*l,*l,*l,*l,*l,*l]
        n: &n [*m,*m,*m,*m,*m,*m,*m,*m,*m]
        o: &o [*n,*n,*n,*n,*n,*n,*n,*n,*n]
        p: &p [*o,*o,*o,*o,*o,*o,*o,*o,*o]
        q: &q [*p,*p,*p,*p,*p,*p,*p,*p,*p]
        r: &r [*q,*q,*q,*q,*q,*q,*q,*q,*q]
        s: &s [*r,*r,*r,*r,*r,*r,*r,*r,*r]
        t: &t [*s,*s,*s,*s,*s,*s,*s,*s,*s]
        u: &u [*t,*t,*t,*t,*t,*t,*t,*t,*t]
        v: &v [*u,*u,*u,*u,*u,*u,*u,*u,*u]
        w: &w [*v,*v,*v,*v,*v,*v,*v,*v,*v]
        x: &x [*w,*w,*w,*w,*w,*w,*w,*w,*w]
        y: &y [*x,*x,*x,*x,*x,*x,*x,*x,*x]
        z: &z [*y,*y,*y,*y,*y,*y,*y,*y,*y]
        expected: string
    "};

    let expected = Data {
        expected: "string".to_owned(),
    };

    assert_eq!(expected, serde_yaml::from_str::<Data>(yaml).unwrap());
}

#[test]
fn test_numbers() {
    let cases = [
        ("0xF0", "240"),
        ("+0xF0", "240"),
        ("-0xF0", "-240"),
        ("0o70", "56"),
        ("+0o70", "56"),
        ("-0o70", "-56"),
        ("0b10", "2"),
        ("+0b10", "2"),
        ("-0b10", "-2"),
        ("127", "127"),
        ("+127", "127"),
        ("-127", "-127"),
        (".inf", ".inf"),
        (".Inf", ".inf"),
        (".INF", ".inf"),
        ("-.inf", "-.inf"),
        ("-.Inf", "-.inf"),
        ("-.INF", "-.inf"),
        (".nan", ".nan"),
        (".NaN", ".nan"),
        (".NAN", ".nan"),
        ("0.1", "0.1"),
    ];
    for &(yaml, expected) in &cases {
        let value = serde_yaml::from_str::<Value>(yaml).unwrap();
        match value {
            Value::Number(number) => assert_eq!(number.to_string(), expected),
            _ => panic!("expected number. input={:?}, result={:?}", yaml, value),
        }
    }

    // NOT numbers.
    let cases = ["0127", "+0127", "-0127"];
    for yaml in &cases {
        let value = serde_yaml::from_str::<Value>(yaml).unwrap();
        match value {
            Value::String(string) => assert_eq!(string, *yaml),
            _ => panic!("expected string. input={:?}, result={:?}", yaml, value),
        }
    }
}

#[test]
fn test_stateful() {
    struct Seed(i64);

    impl<'de> serde::de::DeserializeSeed<'de> for Seed {
        type Value = i64;
        fn deserialize<D>(self, deserializer: D) -> Result<i64, D::Error>
        where
            D: serde::de::Deserializer<'de>,
        {
            struct Visitor(i64);
            impl<'de> serde::de::Visitor<'de> for Visitor {
                type Value = i64;

                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    write!(formatter, "an integer")
                }

                fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<i64, E> {
                    Ok(v * self.0)
                }

                fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<i64, E> {
                    Ok(v as i64 * self.0)
                }
            }

            deserializer.deserialize_any(Visitor(self.0))
        }
    }

    let cases = [("3", 5, 15), ("6", 7, 42), ("-5", 9, -45)];
    for &(yaml, seed, expected) in &cases {
        test_de_seed(yaml, Seed(seed), &expected);
    }
}
<commit_msg>Test borrowing quoted strings<commit_after>#![allow(
    clippy::cast_lossless,
    clippy::cast_possible_wrap,
    clippy::derive_partial_eq_without_eq
)]

use indoc::indoc;
use serde_derive::Deserialize;
use serde_yaml::Value;
use std::collections::BTreeMap;
use std::fmt::Debug;

fn test_de<'de, T>(yaml: &'de str, expected: &T)
where
    T: serde::de::Deserialize<'de> + PartialEq + Debug,
{
    let deserialized: T = serde_yaml::from_str(yaml).unwrap();
    assert_eq!(*expected, deserialized);

    serde_yaml::from_str::<serde_yaml::Value>(yaml).unwrap();
    serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

fn test_de_seed<'de, T, S>(yaml: &'de str, seed: S, expected: &T)
where
    T: PartialEq + Debug,
    S: serde::de::DeserializeSeed<'de, Value = T>,
{
    let deserialized: T = serde_yaml::seed::from_str_seed(yaml, seed).unwrap();
    assert_eq!(*expected, deserialized);

    serde_yaml::from_str::<serde_yaml::Value>(yaml).unwrap();
    serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

#[test]
fn test_borrowed() {
    let yaml = indoc! {"
        - plain
        - 'single quoted'
        - \"double quoted\"
    "};
    let expected = vec!["plain", "single quoted", "double quoted"];
    test_de(yaml, &expected);
}

#[test]
fn test_alias() {
    let yaml = indoc! {"
        first:
          &alias
          1
        second:
          *alias
        third: 3
    "};
    let mut expected = BTreeMap::new();
    expected.insert(String::from("first"), 1);
    expected.insert(String::from("second"), 1);
    expected.insert(String::from("third"), 3);
    test_de(yaml, &expected);
}

#[test]
fn test_option() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: Option<f64>,
        b: Option<String>,
        c: Option<bool>,
    }
    let yaml = indoc! {"
        b:
        c: true
    "};
    let expected = Data {
        a: None,
        b: None,
        c: Some(true),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_option_alias() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: Option<f64>,
        b: Option<String>,
        c: Option<bool>,
        d: Option<f64>,
        e: Option<String>,
        f: Option<bool>,
    }
    let yaml = indoc! {"
        none_f:
          &none_f
          ~
        none_s:
          &none_s
          ~
        none_b:
          &none_b
          ~

        some_f:
          &some_f
          1.0
        some_s:
          &some_s
          x
        some_b:
          &some_b
          true

        a: *none_f
        b: *none_s
        c: *none_b
        d: *some_f
        e: *some_s
        f: *some_b
    "};
    let expected = Data {
        a: None,
        b: None,
        c: None,
        d: Some(1.0),
        e: Some("x".to_owned()),
        f: Some(true),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_enum_alias() {
    #[derive(Deserialize, PartialEq, Debug)]
    enum E {
        A,
        B(u8, u8),
    }
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: E,
        b: E,
    }
    let yaml = indoc! {"
        aref:
          &aref
          A
        bref:
          &bref
          B:
            - 1
            - 2

        a: *aref
        b: *bref
    "};
    let expected = Data {
        a: E::A,
        b: E::B(1, 2),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_enum_tag() {
    #[derive(Deserialize, PartialEq, Debug)]
    enum E {
        A(String),
        B(String),
    }
    #[derive(Deserialize, PartialEq, Debug)]
    struct Data {
        a: E,
        b: E,
    }
    let yaml = indoc! {"
        a: !A foo
        b: !B bar
    "};
    let expected = Data {
        a: E::A("foo".into()),
        b: E::B("bar".into()),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_number_as_string() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Num {
        value: String,
    }
    let yaml = indoc! {"
        # Cannot be represented as u128
        value: 340282366920938463463374607431768211457
    "};
    let expected = Num {
        value: "340282366920938463463374607431768211457".to_owned(),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_i128_big() {
    let expected: i128 = i64::MIN as i128 - 1;
    let yaml = indoc! {"
        -9223372036854775809
    "};
    assert_eq!(expected, serde_yaml::from_str::<i128>(yaml).unwrap());

    let octal = indoc! {"
        -0o1000000000000000000001
    "};
    assert_eq!(expected, serde_yaml::from_str::<i128>(octal).unwrap());
}

#[test]
fn test_u128_big() {
    let expected: u128 = u64::MAX as u128 + 1;
    let yaml = indoc! {"
        18446744073709551616
    "};
    assert_eq!(expected, serde_yaml::from_str::<u128>(yaml).unwrap());

    let octal = indoc! {"
        0o2000000000000000000000
    "};
    assert_eq!(expected, serde_yaml::from_str::<u128>(octal).unwrap());
}

#[test]
fn test_number_alias_as_string() {
    #[derive(Deserialize, PartialEq, Debug)]
    struct Num {
        version: String,
        value: String,
    }
    let yaml = indoc! {"
        version: &a 1.10
        value: *a
    "};
    let expected = Num {
        version: "1.10".to_owned(),
        value: "1.10".to_owned(),
    };
    test_de(yaml, &expected);
}

#[test]
fn test_de_mapping() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct Data {
        pub substructure: serde_yaml::Mapping,
    }
    let yaml = indoc! {"
        substructure:
          a: 'foo'
          b: 'bar'
    "};

    let mut expected = Data {
        substructure: serde_yaml::Mapping::new(),
    };
    expected.substructure.insert(
        serde_yaml::Value::String("a".to_owned()),
        serde_yaml::Value::String("foo".to_owned()),
    );
    expected.substructure.insert(
        serde_yaml::Value::String("b".to_owned()),
        serde_yaml::Value::String("bar".to_owned()),
    );

    test_de(yaml, &expected);
}

#[test]
fn test_bomb() {
    #[derive(Debug, Deserialize, PartialEq)]
    struct Data {
        expected: String,
    }

    // This would deserialize an astronomical number of elements if we were
    // vulnerable.
    let yaml = indoc! {"
        a: &a ~
        b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
        c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
        d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
        e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]
        f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]
        g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f]
        h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g]
        i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h]
        j: &j [*i,*i,*i,*i,*i,*i,*i,*i,*i]
        k: &k [*j,*j,*j,*j,*j,*j,*j,*j,*j]
        l: &l [*k,*k,*k,*k,*k,*k,*k,*k,*k]
        m: &m [*l,*l,*l,*l,*l,*l,*l,*l,*l]
        n: &n [*m,*m,*m,*m,*m,*m,*m,*m,*m]
        o: &o [*n,*n,*n,*n,*n,*n,*n,*n,*n]
        p: &p [*o,*o,*o,*o,*o,*o,*o,*o,*o]
        q: &q [*p,*p,*p,*p,*p,*p,*p,*p,*p]
        r: &r [*q,*q,*q,*q,*q,*q,*q,*q,*q]
        s: &s [*r,*r,*r,*r,*r,*r,*r,*r,*r]
        t: &t [*s,*s,*s,*s,*s,*s,*s,*s,*s]
        u: &u [*t,*t,*t,*t,*t,*t,*t,*t,*t]
        v: &v [*u,*u,*u,*u,*u,*u,*u,*u,*u]
        w: &w [*v,*v,*v,*v,*v,*v,*v,*v,*v]
        x: &x [*w,*w,*w,*w,*w,*w,*w,*w,*w]
        y: &y [*x,*x,*x,*x,*x,*x,*x,*x,*x]
        z: &z [*y,*y,*y,*y,*y,*y,*y,*y,*y]
        expected: string
    "};

    let expected = Data {
        expected: "string".to_owned(),
    };

    assert_eq!(expected, serde_yaml::from_str::<Data>(yaml).unwrap());
}

#[test]
fn test_numbers() {
    let cases = [
        ("0xF0", "240"),
        ("+0xF0", "240"),
        ("-0xF0", "-240"),
        ("0o70", "56"),
        ("+0o70", "56"),
        ("-0o70", "-56"),
        ("0b10", "2"),
        ("+0b10", "2"),
        ("-0b10", "-2"),
        ("127", "127"),
        ("+127", "127"),
        ("-127", "-127"),
        (".inf", ".inf"),
        (".Inf", ".inf"),
        (".INF", ".inf"),
        ("-.inf", "-.inf"),
        ("-.Inf", "-.inf"),
        ("-.INF", "-.inf"),
        (".nan", ".nan"),
        (".NaN", ".nan"),
        (".NAN", ".nan"),
        ("0.1", "0.1"),
    ];
    for &(yaml, expected) in &cases {
        let value = serde_yaml::from_str::<Value>(yaml).unwrap();
        match value {
            Value::Number(number) => assert_eq!(number.to_string(), expected),
            _ => panic!("expected number. input={:?}, result={:?}", yaml, value),
        }
    }

    // NOT numbers.
    let cases = ["0127", "+0127", "-0127"];
    for yaml in &cases {
        let value = serde_yaml::from_str::<Value>(yaml).unwrap();
        match value {
            Value::String(string) => assert_eq!(string, *yaml),
            _ => panic!("expected string. input={:?}, result={:?}", yaml, value),
        }
    }
}

#[test]
fn test_stateful() {
    struct Seed(i64);

    impl<'de> serde::de::DeserializeSeed<'de> for Seed {
        type Value = i64;
        fn deserialize<D>(self, deserializer: D) -> Result<i64, D::Error>
        where
            D: serde::de::Deserializer<'de>,
        {
            struct Visitor(i64);
            impl<'de> serde::de::Visitor<'de> for Visitor {
                type Value = i64;

                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    write!(formatter, "an integer")
                }

                fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<i64, E> {
                    Ok(v * self.0)
                }

                fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<i64, E> {
                    Ok(v as i64 * self.0)
                }
            }

            deserializer.deserialize_any(Visitor(self.0))
        }
    }

    let cases = [("3", 5, 15), ("6", 7, 42), ("-5", 9, -45)];
    for &(yaml, seed, expected) in &cases {
        test_de_seed(yaml, Seed(seed), &expected);
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate syn;

use token::*;

const TAG_OPENER: &'static str = "{{";
const TAG_CLOSER: &'static str = "}}";
const UNESCAPED_TAG_CLOSER: &'static str = "}}}";

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    Mismatch
}

fn consume<'a>(input: &'a str, expected: &str) -> Result<&'a str, Error> {
    match input.starts_with(expected) {
        true => Ok(&input[expected.len()..]),
        false => Err(Error::Mismatch),
    }
}

fn not_dot(ch: char) -> bool {
    ch != '.'
}

fn name<'a>(input: &'a str) -> Result<(&'a str, Name<'a>), Error> {
    let input = input.trim();

    let leading_dots = input.find(not_dot).unwrap_or(input.len());
    let input = &input[leading_dots..].trim_left();

    if leading_dots == 0 && input.len() == 0 {
        return Err(Error::Mismatch);
    }

    let mut segments = vec![];

    if input.len() > 0 {
        for segment in input.split('.') {
            syn::parse_ident(segment).map_err(|_| Error::Mismatch)?;
            segments.push(segment);
        }
    }

    Ok((&input[0..0], Name {
        leading_dots: num::cast::cast(leading_dots).unwrap(),
        segments: segments,
    }))
}

fn at_end(input: &str) -> Result<(), Error> {
    match input.len() {
        0 => Ok(()),
        _ => Err(Error::Mismatch),
    }
}

fn interpolation<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::Interpolation(name))
}

fn unescaped_interpolation<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let input = consume(input, "{")?;
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::UnescapedInterpolation(name))
}

fn section_opener<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    enum Head { Positive, Negative };
    enum Tail { Conditional, Scope, None };

    let input = input.trim();

    let head = match input.chars().next() {
        Some('#') => Ok(Head::Positive),
        Some('^') => Ok(Head::Negative),
        _ => Err(Error::Mismatch),
    }?;
    let input = &input[1..];

    let (input, tail) = if input.ends_with('?') {
        (&input[..input.len()-1], Tail::Conditional)
    } else if input.ends_with('.') {
        (&input[..input.len()-1], Tail::Scope)
    } else {
        (input, Tail::None)
    };

    let (rest, name) = name(input)?;

    at_end(rest)?;

    let section_type = match (head, tail) {
        (Head::Positive, Tail::None) => Ok(SectionType::Iteration),
        (Head::Negative, Tail::None) => Ok(SectionType::NegativeIteration),
        (Head::Positive, Tail::Conditional) => Ok(SectionType::Conditional),
        (Head::Negative, Tail::Conditional) => Ok(SectionType::NegativeConditional),
        (Head::Positive, Tail::Scope) => Ok(SectionType::Scope),
        _ => Err(Error::Mismatch),
    }?;

    Ok(Token::SectionOpener(section_type, name))
}

fn section_closer<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let input = consume(input, "/")?;
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::SectionCloser(name))
}

fn bart_tag<'a>(input: &'a str) -> Result<(&'a str, Token<'a>), Error> {
    let input = consume(input, TAG_OPENER)?;

    let peek = input.chars().next();
    let tag_closer = match peek {
        Some('{') => UNESCAPED_TAG_CLOSER,
        _ => TAG_CLOSER,
    };

    let end = input.find(tag_closer).ok_or(Error::Mismatch)?;
    let tag_meat = &input[..end];
    let rest = &input[end + tag_closer.len()..];

    let tag = match peek {
        Some('#') => section_opener(tag_meat)?,
        Some('^') => section_opener(tag_meat)?,
        Some('/') => section_closer(tag_meat)?,
        Some('{') => unescaped_interpolation(tag_meat)?,
        Some(_) => interpolation(tag_meat)?,
        None => return Err(Error::Mismatch),
    };

    Ok((rest, tag))
}

fn literal_text<'a>(input: &'a str) -> Result<(&'a str, Option<Token<'a>>), Error> {
    match input.find(TAG_OPENER) {
        Some(0) => Ok((input, None)),
        Some(index) => Ok((
            &input[index..],
            Some(Token::Literal(&input[0..index]))
        )),
        None => Ok((
            "",
            match input.len() {
                0 => None,
                _ => Some(Token::Literal(input))
            }
        ))
    }
}

pub fn sequence<'a>(mut input: &'a str) -> Result<Vec<Token<'a>>, Error> {
    let mut seq = vec![];

    loop {
        let (rest, literal_opt) = literal_text(input)?;

        if let Some(literal) = literal_opt {
            seq.push(literal);
        }

        if rest.is_empty() {
            break;
        }

        let (rest, tag) = bart_tag(rest)?;
        seq.push(tag);

        input = rest;
    }

    Ok(seq)
}

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

    #[test]
    fn consume_matches() {
        assert_eq!(Ok("ape}}"), consume("{{ape}}", "{{"));
    }

    #[test]
    fn consume_mismatches() {
        assert_eq!(Err(Error::Mismatch), consume("{{ape}}", "{a"));
    }

    #[test]
    fn bart_tag_matches() {
        assert_eq!(
            Ok(("tail", Token::Interpolation(simple_name("ape")))),
            bart_tag("{{ape}}tail")
        );
    }

    #[test]
    fn bart_tag_matches_name_with_underscore() {
        assert_eq!(
            Ok(("tail", Token::Interpolation(simple_name("ape_katt")))),
            bart_tag("{{ape_katt}}tail")
        );
    }

    #[test]
    fn bart_tag_mismatches() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("head{{ape}}")
        );
    }

    #[test]
    fn bart_tag_must_be_closed() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("{{ape")
        );
    }

    #[test]
    fn bart_tag_matches_iteration_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Iteration, simple_name("ape")))),
            bart_tag("{{#ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_negative_iteration_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::NegativeIteration, simple_name("ape")))),
            bart_tag("{{^ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_conditional_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Conditional, simple_name("ape")))),
            bart_tag("{{#ape?}}")
        );
    }

    #[test]
    fn bart_tag_matches_negative_conditional_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::NegativeConditional, simple_name("ape")))),
            bart_tag("{{^ape?}}")
        );
    }

    #[test]
    fn bart_tag_matches_scope_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Scope, simple_name("ape")))),
            bart_tag("{{#ape.}}")
        );
    }

    #[test]
    fn bart_tag_matches_section_closer() {
        assert_eq!(
            Ok(("", Token::SectionCloser(simple_name("ape")))),
            bart_tag("{{/ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_unescaped_interpolation() {
        assert_eq!(
            Ok(("", Token::UnescapedInterpolation(simple_name("ape")))),
            bart_tag("{{{ape}}}")
        );
    }

    #[test]
    fn error_on_invalid_tag() {
        let res = bart_tag("{{+ape}}");
        assert!(res.is_err());
    }

    #[test]
    fn literal_reads_until_tag() {
        assert_eq!(
            Ok(("{{ape}}", Some(Token::Literal("head")))),
            literal_text("head{{ape}}")
        );
    }

    #[test]
    fn literal_reads_until_end() {
        assert_eq!(
            Ok(("", Some(Token::Literal("head{ape}")))),
            literal_text("head{ape}")
        );
    }

    #[test]
    fn literal_returns_none_at_tag() {
        assert_eq!(
            Ok(("{{ape}}", None)),
            literal_text("{{ape}}")
        );
    }

    #[test]
    fn literal_returns_none_at_end() {
        assert_eq!(
            Ok(("", None)),
            literal_text("")
        );
    }

    #[test]
    fn template_with_tightly_packed_tags() {
        let parsed = sequence("{{a}}{{b}}{{c}}").unwrap();
        assert_eq!(vec![
            Interpolation(simple_name("a")),
            Interpolation(simple_name("b")),
            Interpolation(simple_name("c")),
        ], parsed);
    }

    #[test]
    fn template_with_mixed_content() {
        let parsed = sequence("Hello {{name}}! {{#list}}Welcome{{/list}}").unwrap();
        assert_eq!(vec![
            Literal("Hello "),
            Interpolation(simple_name("name")),
            Literal("! "),
            SectionOpener(SectionType::Iteration, simple_name("list")),
            Literal("Welcome"),
            SectionCloser(simple_name("list")),
        ], parsed);
    }

    #[test]
    fn tags_with_leading_dots() {
        let parsed = sequence("{{.a}}{{..b}}{{...c}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 1, segments: vec!["a"] }),
            Interpolation(Name { leading_dots: 2, segments: vec!["b"] }),
            Interpolation(Name { leading_dots: 3, segments: vec!["c"] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmented_names() {
        let parsed = sequence("{{a.b.c}}{{..b.c.d}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 0, segments: vec!["a", "b", "c"] }),
            Interpolation(Name { leading_dots: 2, segments: vec!["b", "c", "d"] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmentless_name() {
        let parsed = sequence("{{.}}{{..}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 1, segments: vec![] }),
            Interpolation(Name { leading_dots: 2, segments: vec![] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmentless_name_missing_dots() {
        match sequence("{{}}") {
            Ok(_) => panic!(),
            Err(_) => (),
        }
    }

    #[test]
    fn simple_name_parses() {
        assert_eq!(Ok(("", simple_name("ape"))), name("ape"));
    }

    #[test]
    fn name_with_whitespace() {
        assert_eq!(Ok(("", simple_name("ape"))), name("  ape  "));
    }

    #[test]
    fn name_with_leading_dots() {
        assert_eq!(Ok(("", Name { leading_dots: 1, segments: vec!["ape"] })), name(".ape"));
    }

    #[test]
    fn name_with_multiple_segments() {
        assert_eq!(Ok(("", Name { leading_dots: 0, segments: vec!["ape", "skrekk"] })), name("ape.skrekk"));
    }

    #[test]
    fn name_without_any_segments() {
        assert_eq!(Ok(("", Name { leading_dots: 1, segments: vec![] })), name("."));
    }
}
<commit_msg>Add extra test to check that invalid tags are parse errors. This fixes #8<commit_after>extern crate num;
extern crate syn;

use token::*;

const TAG_OPENER: &'static str = "{{";
const TAG_CLOSER: &'static str = "}}";
const UNESCAPED_TAG_CLOSER: &'static str = "}}}";

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    Mismatch
}

fn consume<'a>(input: &'a str, expected: &str) -> Result<&'a str, Error> {
    match input.starts_with(expected) {
        true => Ok(&input[expected.len()..]),
        false => Err(Error::Mismatch),
    }
}

fn not_dot(ch: char) -> bool {
    ch != '.'
}

fn name<'a>(input: &'a str) -> Result<(&'a str, Name<'a>), Error> {
    let input = input.trim();

    let leading_dots = input.find(not_dot).unwrap_or(input.len());
    let input = &input[leading_dots..].trim_left();

    if leading_dots == 0 && input.len() == 0 {
        return Err(Error::Mismatch);
    }

    let mut segments = vec![];

    if input.len() > 0 {
        for segment in input.split('.') {
            syn::parse_ident(segment).map_err(|_| Error::Mismatch)?;
            segments.push(segment);
        }
    }

    Ok((&input[0..0], Name {
        leading_dots: num::cast::cast(leading_dots).unwrap(),
        segments: segments,
    }))
}

fn at_end(input: &str) -> Result<(), Error> {
    match input.len() {
        0 => Ok(()),
        _ => Err(Error::Mismatch),
    }
}

fn interpolation<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::Interpolation(name))
}

fn unescaped_interpolation<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let input = consume(input, "{")?;
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::UnescapedInterpolation(name))
}

fn section_opener<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    enum Head { Positive, Negative };
    enum Tail { Conditional, Scope, None };

    let input = input.trim();

    let head = match input.chars().next() {
        Some('#') => Ok(Head::Positive),
        Some('^') => Ok(Head::Negative),
        _ => Err(Error::Mismatch),
    }?;
    let input = &input[1..];

    let (input, tail) = if input.ends_with('?') {
        (&input[..input.len()-1], Tail::Conditional)
    } else if input.ends_with('.') {
        (&input[..input.len()-1], Tail::Scope)
    } else {
        (input, Tail::None)
    };

    let (rest, name) = name(input)?;

    at_end(rest)?;

    let section_type = match (head, tail) {
        (Head::Positive, Tail::None) => Ok(SectionType::Iteration),
        (Head::Negative, Tail::None) => Ok(SectionType::NegativeIteration),
        (Head::Positive, Tail::Conditional) => Ok(SectionType::Conditional),
        (Head::Negative, Tail::Conditional) => Ok(SectionType::NegativeConditional),
        (Head::Positive, Tail::Scope) => Ok(SectionType::Scope),
        _ => Err(Error::Mismatch),
    }?;

    Ok(Token::SectionOpener(section_type, name))
}

fn section_closer<'a>(input: &'a str) -> Result<Token<'a>, Error> {
    let input = consume(input, "/")?;
    let (rest, name) = name(input)?;
    at_end(rest)?;
    Ok(Token::SectionCloser(name))
}

fn bart_tag<'a>(input: &'a str) -> Result<(&'a str, Token<'a>), Error> {
    let input = consume(input, TAG_OPENER)?;

    let peek = input.chars().next();
    let tag_closer = match peek {
        Some('{') => UNESCAPED_TAG_CLOSER,
        _ => TAG_CLOSER,
    };

    let end = input.find(tag_closer).ok_or(Error::Mismatch)?;
    let tag_meat = &input[..end];
    let rest = &input[end + tag_closer.len()..];

    let tag = match peek {
        Some('#') => section_opener(tag_meat)?,
        Some('^') => section_opener(tag_meat)?,
        Some('/') => section_closer(tag_meat)?,
        Some('{') => unescaped_interpolation(tag_meat)?,
        Some(_) => interpolation(tag_meat)?,
        None => return Err(Error::Mismatch),
    };

    Ok((rest, tag))
}

fn literal_text<'a>(input: &'a str) -> Result<(&'a str, Option<Token<'a>>), Error> {
    match input.find(TAG_OPENER) {
        Some(0) => Ok((input, None)),
        Some(index) => Ok((
            &input[index..],
            Some(Token::Literal(&input[0..index]))
        )),
        None => Ok((
            "",
            match input.len() {
                0 => None,
                _ => Some(Token::Literal(input))
            }
        ))
    }
}

pub fn sequence<'a>(mut input: &'a str) -> Result<Vec<Token<'a>>, Error> {
    let mut seq = vec![];

    loop {
        let (rest, literal_opt) = literal_text(input)?;

        if let Some(literal) = literal_opt {
            seq.push(literal);
        }

        if rest.is_empty() {
            break;
        }

        let (rest, tag) = bart_tag(rest)?;
        seq.push(tag);

        input = rest;
    }

    Ok(seq)
}

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

    #[test]
    fn consume_matches() {
        assert_eq!(Ok("ape}}"), consume("{{ape}}", "{{"));
    }

    #[test]
    fn consume_mismatches() {
        assert_eq!(Err(Error::Mismatch), consume("{{ape}}", "{a"));
    }

    #[test]
    fn bart_tag_matches() {
        assert_eq!(
            Ok(("tail", Token::Interpolation(simple_name("ape")))),
            bart_tag("{{ape}}tail")
        );
    }

    #[test]
    fn bart_tag_matches_name_with_underscore() {
        assert_eq!(
            Ok(("tail", Token::Interpolation(simple_name("ape_katt")))),
            bart_tag("{{ape_katt}}tail")
        );
    }

    #[test]
    fn bart_tag_mismatches() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("head{{ape}}")
        );
    }

    #[test]
    fn bart_tag_must_be_closed() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("{{ape")
        );
    }

    #[test]
    fn bart_tag_matches_iteration_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Iteration, simple_name("ape")))),
            bart_tag("{{#ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_negative_iteration_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::NegativeIteration, simple_name("ape")))),
            bart_tag("{{^ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_conditional_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Conditional, simple_name("ape")))),
            bart_tag("{{#ape?}}")
        );
    }

    #[test]
    fn bart_tag_matches_negative_conditional_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::NegativeConditional, simple_name("ape")))),
            bart_tag("{{^ape?}}")
        );
    }

    #[test]
    fn bart_tag_matches_scope_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener(SectionType::Scope, simple_name("ape")))),
            bart_tag("{{#ape.}}")
        );
    }

    #[test]
    fn bart_tag_matches_section_closer() {
        assert_eq!(
            Ok(("", Token::SectionCloser(simple_name("ape")))),
            bart_tag("{{/ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_unescaped_interpolation() {
        assert_eq!(
            Ok(("", Token::UnescapedInterpolation(simple_name("ape")))),
            bart_tag("{{{ape}}}")
        );
    }

    #[test]
    fn error_on_invalid_tag() {
        let res = bart_tag("{{+ape}}");
        assert!(res.is_err());
    }

    #[test]
    fn error_on_invalid_tag_2() {
        let res = bart_tag("{{ape-skrekk}}");
        assert!(res.is_err());
    }

    #[test]
    fn literal_reads_until_tag() {
        assert_eq!(
            Ok(("{{ape}}", Some(Token::Literal("head")))),
            literal_text("head{{ape}}")
        );
    }

    #[test]
    fn literal_reads_until_end() {
        assert_eq!(
            Ok(("", Some(Token::Literal("head{ape}")))),
            literal_text("head{ape}")
        );
    }

    #[test]
    fn literal_returns_none_at_tag() {
        assert_eq!(
            Ok(("{{ape}}", None)),
            literal_text("{{ape}}")
        );
    }

    #[test]
    fn literal_returns_none_at_end() {
        assert_eq!(
            Ok(("", None)),
            literal_text("")
        );
    }

    #[test]
    fn template_with_tightly_packed_tags() {
        let parsed = sequence("{{a}}{{b}}{{c}}").unwrap();
        assert_eq!(vec![
            Interpolation(simple_name("a")),
            Interpolation(simple_name("b")),
            Interpolation(simple_name("c")),
        ], parsed);
    }

    #[test]
    fn template_with_mixed_content() {
        let parsed = sequence("Hello {{name}}! {{#list}}Welcome{{/list}}").unwrap();
        assert_eq!(vec![
            Literal("Hello "),
            Interpolation(simple_name("name")),
            Literal("! "),
            SectionOpener(SectionType::Iteration, simple_name("list")),
            Literal("Welcome"),
            SectionCloser(simple_name("list")),
        ], parsed);
    }

    #[test]
    fn tags_with_leading_dots() {
        let parsed = sequence("{{.a}}{{..b}}{{...c}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 1, segments: vec!["a"] }),
            Interpolation(Name { leading_dots: 2, segments: vec!["b"] }),
            Interpolation(Name { leading_dots: 3, segments: vec!["c"] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmented_names() {
        let parsed = sequence("{{a.b.c}}{{..b.c.d}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 0, segments: vec!["a", "b", "c"] }),
            Interpolation(Name { leading_dots: 2, segments: vec!["b", "c", "d"] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmentless_name() {
        let parsed = sequence("{{.}}{{..}}").unwrap();
        assert_eq!(vec![
            Interpolation(Name { leading_dots: 1, segments: vec![] }),
            Interpolation(Name { leading_dots: 2, segments: vec![] }),
        ], parsed);
    }

    #[test]
    fn tags_with_segmentless_name_missing_dots() {
        match sequence("{{}}") {
            Ok(_) => panic!(),
            Err(_) => (),
        }
    }

    #[test]
    fn simple_name_parses() {
        assert_eq!(Ok(("", simple_name("ape"))), name("ape"));
    }

    #[test]
    fn name_with_whitespace() {
        assert_eq!(Ok(("", simple_name("ape"))), name("  ape  "));
    }

    #[test]
    fn name_with_leading_dots() {
        assert_eq!(Ok(("", Name { leading_dots: 1, segments: vec!["ape"] })), name(".ape"));
    }

    #[test]
    fn name_with_multiple_segments() {
        assert_eq!(Ok(("", Name { leading_dots: 0, segments: vec!["ape", "skrekk"] })), name("ape.skrekk"));
    }

    #[test]
    fn name_without_any_segments() {
        assert_eq!(Ok(("", Name { leading_dots: 1, segments: vec![] })), name("."));
    }
}
<|endoftext|>


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

use std::env;
use std::fs::File;
use std::io::{self, Error, ErrorKind, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};

use super::common;

/// Returns the ELF class from the ELF header in the supplied file.
fn parse_elf_header(path: &Path) -> io::Result<u8> {
    let mut file = File::open(path)?;
    let mut buffer = [0; 5];
    file.read_exact(&mut buffer)?;
    if buffer[..4] == [127, 69, 76, 70] {
        Ok(buffer[4])
    } else {
        Err(Error::new(ErrorKind::InvalidData, "invalid ELF header"))
    }
}

/// Returns the magic number from the PE header in the supplied file.
fn parse_pe_header(path: &Path) -> io::Result<u16> {
    let mut file = File::open(path)?;

    // Determine the header offset.
    let mut buffer = [0; 4];
    let start = SeekFrom::Start(0x3C);
    file.seek(start)?;
    file.read_exact(&mut buffer)?;
    let offset = i32::from_le_bytes(buffer);

    // Determine the validity of the header.
    file.seek(SeekFrom::Start(offset as u64))?;
    file.read_exact(&mut buffer)?;
    if buffer != [80, 69, 0, 0] {
        return Err(Error::new(ErrorKind::InvalidData, "invalid PE header"));
    }

    // Find the magic number.
    let mut buffer = [0; 2];
    file.seek(SeekFrom::Current(20))?;
    file.read_exact(&mut buffer)?;
    Ok(u16::from_le_bytes(buffer))
}

/// Validates the header for the supplied `libclang` shared library.
fn validate_header(path: &Path) -> Result<(), String> {
    if cfg!(any(target_os = "freebsd", target_os = "linux")) {
        let class = parse_elf_header(path).map_err(|e| e.to_string())?;

        if cfg!(target_pointer_width = "32") && class != 1 {
            return Err("invalid ELF class (64-bit)".into());
        }

        if cfg!(target_pointer_width = "64") && class != 2 {
            return Err("invalid ELF class (32-bit)".into());
        }

        Ok(())
    } else if cfg!(target_os = "windows") {
        let magic = parse_pe_header(path).map_err(|e| e.to_string())?;

        if cfg!(target_pointer_width = "32") && magic != 267 {
            return Err("invalid DLL (64-bit)".into());
        }

        if cfg!(target_pointer_width = "64") && magic != 523 {
            return Err("invalid DLL (32-bit)".into());
        }

        Ok(())
    } else {
        Ok(())
    }
}

/// Returns the components of the version in the supplied `libclang` shared
// library filename.
fn parse_version(filename: &str) -> Vec<u32> {
    let version = if filename.starts_with("libclang.so.") {
        &filename[12..]
    } else if filename.starts_with("libclang-") {
        &filename[9..filename.len() - 3]
    } else {
        return vec![];
    };

    version.split('.').map(|s| s.parse().unwrap_or(0)).collect()
}

/// Returns the paths to, the filenames, and the versions of the `libclang`
// shared libraries.
fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> {
    let mut files = vec![format!(
        "{}clang{}",
        env::consts::DLL_PREFIX,
        env::consts::DLL_SUFFIX
    )];

    if cfg!(target_os = "linux") {
        // Some Linux distributions don't create a `libclang.so` symlink, so we
        // need to look for versioned files (e.g., `libclang-3.9.so`).
        files.push("libclang-*.so".into());

        // Some Linux distributions don't create a `libclang.so` symlink and
        // don't have versioned files as described above, so we need to look for
        // suffix versioned files (e.g., `libclang.so.1`). However, `ld` cannot
        // link to these files, so this will only be included when linking at
        // runtime.
        if runtime {
            files.push("libclang.so.*".into());
        }
    }

    if cfg!(any(
        target_os = "openbsd",
        target_os = "freebsd",
        target_os = "netbsd"
    )) {
        // Some BSD distributions don't create a `libclang.so` symlink either,
        // but use a different naming scheme for versioned files (e.g.,
        // `libclang.so.7.0`).
        files.push("libclang.so.*".into());
    }

    if cfg!(target_os = "windows") {
        // The official LLVM build uses `libclang.dll` on Windows instead of
        // `clang.dll`. However, unofficial builds such as MinGW use `clang.dll`.
        files.push("libclang.dll".into());
    }

    // Validate the `libclang` shared libraries and collect the versions.
    let mut valid = vec![];
    let mut invalid = vec![];
    for (directory, filename) in common::search_libclang_directories(&files, "LIBCLANG_PATH") {
        let path = directory.join(&filename);
        match validate_header(&path) {
            Ok(()) => {
                let version = parse_version(&filename);
                valid.push((directory, filename, version))
            }
            Err(message) => invalid.push(format!("({}: {})", path.display(), message)),
        }
    }

    if !valid.is_empty() {
        return Ok(valid);
    }

    let message = format!(
        "couldn't find any valid shared libraries matching: [{}], set the \
         `LIBCLANG_PATH` environment variable to a path where one of these files \
         can be found (invalid: [{}])",
        files
            .iter()
            .map(|f| format!("'{}'", f))
            .collect::<Vec<_>>()
            .join(", "),
        invalid.join(", "),
    );

    Err(message)
}

/// Returns the directory and filename of the "best" available `libclang` shared
/// library.
pub fn find(runtime: bool) -> Result<(PathBuf, String), String> {
    search_libclang_directories(runtime)?
        .iter()
        .max_by_key(|f| &f.2)
        .cloned()
        .map(|(path, filename, _)| (path, filename))
        .ok_or_else(|| "unreachable".into())
}

/// Find and link to `libclang` dynamically.
#[cfg(not(feature = "runtime"))]
pub fn link() {
    use std::fs;

    let (directory, filename) = find(false).unwrap();
    println!("cargo:rustc-link-search={}", directory.display());

    if cfg!(all(target_os = "windows", target_env = "msvc")) {
        // Find the `libclang` stub static library required for the MSVC
        // toolchain.
        let lib = if !directory.ends_with("bin") {
            directory
        } else {
            directory.parent().unwrap().join("lib")
        };

        if lib.join("libclang.lib").exists() {
            println!("cargo:rustc-link-search={}", lib.display());
        } else if lib.join("libclang.dll.a").exists() {
            // MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`.
            // It is linkable with the MSVC linker, but Rust doesn't recognize
            // the `.a` suffix, so we need to copy it with a different name.
            //
            // FIXME: Maybe we can just hardlink or symlink it?
            let out = env::var("OUT_DIR").unwrap();
            fs::copy(
                lib.join("libclang.dll.a"),
                Path::new(&out).join("libclang.lib"),
            )
            .unwrap();
            println!("cargo:rustc-link-search=native={}", out);
        } else {
            panic!(
                "using '{}', so 'libclang.lib' or 'libclang.dll.a' must be \
                 available in {}",
                filename,
                lib.display(),
            );
        }

        println!("cargo:rustc-link-lib=dylib=libclang");
    } else {
        let name = filename.trim_start_matches("lib");

        // Strip extensions and trailing version numbers (e.g., the `.so.7.0` in
        // `libclang.so.7.0`).
        let name = match name.find(".dylib").or_else(|| name.find(".so")) {
            Some(index) => &name[0..index],
            None => &name,
        };

        println!("cargo:rustc-link-lib=dylib={}", name);
    }
}
<commit_msg>Add additional libclang pattern (#102)<commit_after>// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::env;
use std::fs::File;
use std::io::{self, Error, ErrorKind, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};

use super::common;

/// Returns the ELF class from the ELF header in the supplied file.
fn parse_elf_header(path: &Path) -> io::Result<u8> {
    let mut file = File::open(path)?;
    let mut buffer = [0; 5];
    file.read_exact(&mut buffer)?;
    if buffer[..4] == [127, 69, 76, 70] {
        Ok(buffer[4])
    } else {
        Err(Error::new(ErrorKind::InvalidData, "invalid ELF header"))
    }
}

/// Returns the magic number from the PE header in the supplied file.
fn parse_pe_header(path: &Path) -> io::Result<u16> {
    let mut file = File::open(path)?;

    // Determine the header offset.
    let mut buffer = [0; 4];
    let start = SeekFrom::Start(0x3C);
    file.seek(start)?;
    file.read_exact(&mut buffer)?;
    let offset = i32::from_le_bytes(buffer);

    // Determine the validity of the header.
    file.seek(SeekFrom::Start(offset as u64))?;
    file.read_exact(&mut buffer)?;
    if buffer != [80, 69, 0, 0] {
        return Err(Error::new(ErrorKind::InvalidData, "invalid PE header"));
    }

    // Find the magic number.
    let mut buffer = [0; 2];
    file.seek(SeekFrom::Current(20))?;
    file.read_exact(&mut buffer)?;
    Ok(u16::from_le_bytes(buffer))
}

/// Validates the header for the supplied `libclang` shared library.
fn validate_header(path: &Path) -> Result<(), String> {
    if cfg!(any(target_os = "freebsd", target_os = "linux")) {
        let class = parse_elf_header(path).map_err(|e| e.to_string())?;

        if cfg!(target_pointer_width = "32") && class != 1 {
            return Err("invalid ELF class (64-bit)".into());
        }

        if cfg!(target_pointer_width = "64") && class != 2 {
            return Err("invalid ELF class (32-bit)".into());
        }

        Ok(())
    } else if cfg!(target_os = "windows") {
        let magic = parse_pe_header(path).map_err(|e| e.to_string())?;

        if cfg!(target_pointer_width = "32") && magic != 267 {
            return Err("invalid DLL (64-bit)".into());
        }

        if cfg!(target_pointer_width = "64") && magic != 523 {
            return Err("invalid DLL (32-bit)".into());
        }

        Ok(())
    } else {
        Ok(())
    }
}

/// Returns the components of the version in the supplied `libclang` shared
// library filename.
fn parse_version(filename: &str) -> Vec<u32> {
    let version = if filename.starts_with("libclang.so.") {
        &filename[12..]
    } else if filename.starts_with("libclang-") {
        &filename[9..filename.len() - 3]
    } else {
        return vec![];
    };

    version.split('.').map(|s| s.parse().unwrap_or(0)).collect()
}

/// Returns the paths to, the filenames, and the versions of the `libclang`
// shared libraries.
fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> {
    let mut files = vec![format!(
        "{}clang{}",
        env::consts::DLL_PREFIX,
        env::consts::DLL_SUFFIX
    )];

    if cfg!(target_os = "linux") {
        // Some Linux distributions don't create a `libclang.so` symlink, so we
        // need to look for versioned files (e.g., `libclang-3.9.so`).
        files.push("libclang-*.so".into());

        // Some Linux distributions don't create a `libclang.so` symlink and
        // don't have versioned files as described above, so we need to look for
        // suffix versioned files (e.g., `libclang.so.1`). However, `ld` cannot
        // link to these files, so this will only be included when linking at
        // runtime.
        if runtime {
            files.push("libclang.so.*".into());
            files.push("libclang-*.so.*".into());
        }
    }

    if cfg!(any(
        target_os = "openbsd",
        target_os = "freebsd",
        target_os = "netbsd"
    )) {
        // Some BSD distributions don't create a `libclang.so` symlink either,
        // but use a different naming scheme for versioned files (e.g.,
        // `libclang.so.7.0`).
        files.push("libclang.so.*".into());
    }

    if cfg!(target_os = "windows") {
        // The official LLVM build uses `libclang.dll` on Windows instead of
        // `clang.dll`. However, unofficial builds such as MinGW use `clang.dll`.
        files.push("libclang.dll".into());
    }

    // Validate the `libclang` shared libraries and collect the versions.
    let mut valid = vec![];
    let mut invalid = vec![];
    for (directory, filename) in common::search_libclang_directories(&files, "LIBCLANG_PATH") {
        let path = directory.join(&filename);
        match validate_header(&path) {
            Ok(()) => {
                let version = parse_version(&filename);
                valid.push((directory, filename, version))
            }
            Err(message) => invalid.push(format!("({}: {})", path.display(), message)),
        }
    }

    if !valid.is_empty() {
        return Ok(valid);
    }

    let message = format!(
        "couldn't find any valid shared libraries matching: [{}], set the \
         `LIBCLANG_PATH` environment variable to a path where one of these files \
         can be found (invalid: [{}])",
        files
            .iter()
            .map(|f| format!("'{}'", f))
            .collect::<Vec<_>>()
            .join(", "),
        invalid.join(", "),
    );

    Err(message)
}

/// Returns the directory and filename of the "best" available `libclang` shared
/// library.
pub fn find(runtime: bool) -> Result<(PathBuf, String), String> {
    search_libclang_directories(runtime)?
        .iter()
        .max_by_key(|f| &f.2)
        .cloned()
        .map(|(path, filename, _)| (path, filename))
        .ok_or_else(|| "unreachable".into())
}

/// Find and link to `libclang` dynamically.
#[cfg(not(feature = "runtime"))]
pub fn link() {
    use std::fs;

    let (directory, filename) = find(false).unwrap();
    println!("cargo:rustc-link-search={}", directory.display());

    if cfg!(all(target_os = "windows", target_env = "msvc")) {
        // Find the `libclang` stub static library required for the MSVC
        // toolchain.
        let lib = if !directory.ends_with("bin") {
            directory
        } else {
            directory.parent().unwrap().join("lib")
        };

        if lib.join("libclang.lib").exists() {
            println!("cargo:rustc-link-search={}", lib.display());
        } else if lib.join("libclang.dll.a").exists() {
            // MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`.
            // It is linkable with the MSVC linker, but Rust doesn't recognize
            // the `.a` suffix, so we need to copy it with a different name.
            //
            // FIXME: Maybe we can just hardlink or symlink it?
            let out = env::var("OUT_DIR").unwrap();
            fs::copy(
                lib.join("libclang.dll.a"),
                Path::new(&out).join("libclang.lib"),
            )
            .unwrap();
            println!("cargo:rustc-link-search=native={}", out);
        } else {
            panic!(
                "using '{}', so 'libclang.lib' or 'libclang.dll.a' must be \
                 available in {}",
                filename,
                lib.display(),
            );
        }

        println!("cargo:rustc-link-lib=dylib=libclang");
    } else {
        let name = filename.trim_start_matches("lib");

        // Strip extensions and trailing version numbers (e.g., the `.so.7.0` in
        // `libclang.so.7.0`).
        let name = match name.find(".dylib").or_else(|| name.find(".so")) {
            Some(index) => &name[0..index],
            None => &name,
        };

        println!("cargo:rustc-link-lib=dylib={}", name);
    }
}
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::*;
use crate::syntax::{Derive, ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type, Var};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::Token;

impl ToTokens for Type {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Type::Ident(ident) => {
                if ident == CxxString {
                    let span = ident.span();
                    tokens.extend(quote_spanned!(span=> ::cxx::));
                }
                ident.to_tokens(tokens);
            }
            Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
                ty.to_tokens(tokens)
            }
            Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
            Type::Slice(s) => s.to_tokens(tokens),
            Type::Fn(f) => f.to_tokens(tokens),
            Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
        }
    }
}

impl ToTokens for Var {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.ident.to_tokens(tokens);
        Token![:](self.ident.span()).to_tokens(tokens);
        self.ty.to_tokens(tokens);
    }
}

impl ToTokens for Ty1 {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        if let "UniquePtr" | "RustVec" | "CxxVector" = self.name.to_string().as_str() {
            let span = self.name.span();
            tokens.extend(quote_spanned!(span=> ::cxx::));
        }
        self.name.to_tokens(tokens);
        self.langle.to_tokens(tokens);
        self.inner.to_tokens(tokens);
        self.rangle.to_tokens(tokens);
    }
}

impl ToTokens for Ref {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.ampersand.to_tokens(tokens);
        self.lifetime.to_tokens(tokens);
        self.mutability.to_tokens(tokens);
        self.inner.to_tokens(tokens);
    }
}

impl ToTokens for Slice {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.bracket.surround(tokens, |tokens| {
            self.inner.to_tokens(tokens);
        });
    }
}

impl ToTokens for Derive {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let name = match self {
            Derive::Clone => "Clone",
            Derive::Copy => "Copy",
        };
        Ident::new(name, Span::call_site()).to_tokens(tokens);
    }
}

impl ToTokens for ExternFn {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        // Notional token range for error reporting purposes.
        self.sig.fn_token.to_tokens(tokens);
        self.semi_token.to_tokens(tokens);
    }
}

impl ToTokens for Signature {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.fn_token.to_tokens(tokens);
        self.paren_token.surround(tokens, |tokens| {
            self.args.to_tokens(tokens);
        });
        if let Some(ret) = &self.ret {
            Token![->](self.paren_token.span).to_tokens(tokens);
            if let Some((result, langle, rangle)) = self.throws_tokens {
                result.to_tokens(tokens);
                langle.to_tokens(tokens);
                ret.to_tokens(tokens);
                rangle.to_tokens(tokens);
            } else {
                ret.to_tokens(tokens);
            }
        }
    }
}

pub struct ReceiverType<'a>(&'a Receiver);

impl Receiver {
    // &TheType
    pub fn ty(&self) -> ReceiverType {
        ReceiverType(self)
    }
}

impl ToTokens for ReceiverType<'_> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.0.ampersand.to_tokens(tokens);
        self.0.lifetime.to_tokens(tokens);
        self.0.mutability.to_tokens(tokens);
        self.0.ty.to_tokens(tokens);
    }
}
<commit_msg>Fix module path of Vec in generated Rust code<commit_after>use crate::syntax::atom::Atom::*;
use crate::syntax::{Derive, ExternFn, Receiver, Ref, Signature, Slice, Ty1, Type, Var};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote_spanned, ToTokens};
use syn::Token;

impl ToTokens for Type {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Type::Ident(ident) => {
                if ident == CxxString {
                    let span = ident.span();
                    tokens.extend(quote_spanned!(span=> ::cxx::));
                }
                ident.to_tokens(tokens);
            }
            Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) | Type::RustVec(ty) => {
                ty.to_tokens(tokens)
            }
            Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
            Type::Slice(s) => s.to_tokens(tokens),
            Type::Fn(f) => f.to_tokens(tokens),
            Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
        }
    }
}

impl ToTokens for Var {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.ident.to_tokens(tokens);
        Token![:](self.ident.span()).to_tokens(tokens);
        self.ty.to_tokens(tokens);
    }
}

impl ToTokens for Ty1 {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let span = self.name.span();
        let name = self.name.to_string();
        if let "UniquePtr" | "CxxVector" = name.as_str() {
            tokens.extend(quote_spanned!(span=> ::cxx::));
        } else if name == "Vec" {
            tokens.extend(quote_spanned!(span=> ::std::vec::));
        }
        self.name.to_tokens(tokens);
        self.langle.to_tokens(tokens);
        self.inner.to_tokens(tokens);
        self.rangle.to_tokens(tokens);
    }
}

impl ToTokens for Ref {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.ampersand.to_tokens(tokens);
        self.lifetime.to_tokens(tokens);
        self.mutability.to_tokens(tokens);
        self.inner.to_tokens(tokens);
    }
}

impl ToTokens for Slice {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.bracket.surround(tokens, |tokens| {
            self.inner.to_tokens(tokens);
        });
    }
}

impl ToTokens for Derive {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let name = match self {
            Derive::Clone => "Clone",
            Derive::Copy => "Copy",
        };
        Ident::new(name, Span::call_site()).to_tokens(tokens);
    }
}

impl ToTokens for ExternFn {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        // Notional token range for error reporting purposes.
        self.sig.fn_token.to_tokens(tokens);
        self.semi_token.to_tokens(tokens);
    }
}

impl ToTokens for Signature {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.fn_token.to_tokens(tokens);
        self.paren_token.surround(tokens, |tokens| {
            self.args.to_tokens(tokens);
        });
        if let Some(ret) = &self.ret {
            Token![->](self.paren_token.span).to_tokens(tokens);
            if let Some((result, langle, rangle)) = self.throws_tokens {
                result.to_tokens(tokens);
                langle.to_tokens(tokens);
                ret.to_tokens(tokens);
                rangle.to_tokens(tokens);
            } else {
                ret.to_tokens(tokens);
            }
        }
    }
}

pub struct ReceiverType<'a>(&'a Receiver);

impl Receiver {
    // &TheType
    pub fn ty(&self) -> ReceiverType {
        ReceiverType(self)
    }
}

impl ToTokens for ReceiverType<'_> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        self.0.ampersand.to_tokens(tokens);
        self.0.lifetime.to_tokens(tokens);
        self.0.mutability.to_tokens(tokens);
        self.0.ty.to_tokens(tokens);
    }
}
<|endoftext|>


<commit_before>use std::iter::{Iterator, Chain};
use std::option::{Item};
use std::mem;
use num::Integer;
use calx::{V2};
use dir6::Dir6;

pub struct Fov<'a> {
    /// Predicate for whether a given point will block the field of view.
    is_opaque: |V2<int>|: 'a -> bool,
    range: uint,
    stack: Vec<Sector>,
}

struct Sector {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// Currently iterating through a sequence of opaque cells.
    group_opaque: bool,
}

/// An iterator that will yield the field of view around the origin V2(0, 0)
/// up to hex grid distance range, with cells for which is_opaque returns true
/// blocking visibility further away in their direction.
impl<'a> Fov<'a> {
    pub fn new<'a>(is_opaque: |V2<int>|: 'a -> bool, range: uint) -> Chain<Item<V2<int>>, Fov<'a>> {
        // The origin position V2(0, 0) is a special case for the traversal
        // algorithm, but it's also always present, so instead of adding ugly
        // branches to the actual iterator, we just chain it in right here.
        let init_group = is_opaque(Dir6::from_int(0).to_v2());
        Some(V2(0i, 0i)).into_iter().chain(Fov {
            is_opaque: is_opaque,
            range: range,
            stack: vec![Sector {
                begin: PolarPoint::new(0.0, 1),
                pt: PolarPoint::new(0.0, 1),
                end: PolarPoint::new(6.0, 1),
                group_opaque: init_group,
            }],
        })
    }
}

impl<'a> Iterator<V2<int>> for Fov<'a> {
    fn next(&mut self) -> Option<V2<int>> {
        if let Some(mut current) = self.stack.pop() {
            if current.pt.is_below(current.end) {
                let pos = current.pt.to_v2();
                let current_opaque = (self.is_opaque)(pos);

                // Terrain opacity changed, branch out.
                if current_opaque != current.group_opaque {
                    // Add the rest of this sector with the new opacity.
                    self.stack.push(Sector {
                        begin: current.pt,
                        pt: current.pt,
                        end: current.end,
                        group_opaque: current_opaque,
                    });

                    // If this was a visible sector and we're below range, branch
                    // out further.
                    if !current.group_opaque && current.begin.radius < self.range {
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                        });
                    }
                    return self.next();
                }

                // Otherwise proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some(pos);
            } else {
                // Hit the end of the sector.

                // If this was a visible sector and we're below range, branch
                // out further.
                if !current.group_opaque && current.begin.radius < self.range {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

/// Points on a hex circle expressed in polar coordinates.
#[deriving(PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: uint
}

impl PolarPoint {
    pub fn new(pos: f32, radius: uint) -> PolarPoint { PolarPoint { pos: pos, radius: radius } }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> int { (self.pos + 0.5).floor() as int }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }
    fn end_index(self) -> int { (self.pos + 0.5).ceil() as int }

    pub fn to_v2(self) -> V2<int> {
        if self.radius == 0 { return V2(0, 0); }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as int * 6)) / self.radius as int;
        let offset = index.mod_floor(&(self.radius as int)) as int;
        let rod = Dir6::from_int(sector).to_v2() * (self.radius as int);
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2() * offset;
        rod + tangent
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return a tuple of the point outside of the circle between the
    /// two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<V2<int>> {
        let next = self.next();
        let V2(x1, y1) = self.to_v2();
        let V2(x2, y2) = next.to_v2();

        if x2 == x1 + 1 && y2 == y1 + 1 {
            // Going down the right rim.
            Some(V2(x1 + 1, y1))
        } else if x2 == x1 - 1 && y2 == y1 - 1 {
            // Going up the left rim.
            Some(V2(x1 - 1, y1))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}
<commit_msg>Acute corner visibility hack in FOV<commit_after>use std::iter::{Iterator, Chain};
use std::option::{Item};
use std::mem;
use num::Integer;
use calx::{V2};
use dir6::Dir6;

pub struct Fov<'a> {
    /// Predicate for whether a given point will block the field of view.
    is_opaque: |V2<int>|: 'a -> bool,
    range: uint,
    stack: Vec<Sector>,
    /// Extra values generated by special cases.
    side_channel: Vec<V2<int>>,
}

struct Sector {
    /// Start point of current sector.
    begin: PolarPoint,
    /// Point currently being processed.
    pt: PolarPoint,
    /// End point of current sector.
    end: PolarPoint,
    /// Currently iterating through a sequence of opaque cells.
    group_opaque: bool,
}

/// An iterator that will yield the field of view around the origin V2(0, 0)
/// up to hex grid distance range, with cells for which is_opaque returns true
/// blocking visibility further away in their direction.
impl<'a> Fov<'a> {
    pub fn new<'a>(is_opaque: |V2<int>|: 'a -> bool, range: uint) -> Chain<Item<V2<int>>, Fov<'a>> {
        // The origin position V2(0, 0) is a special case for the traversal
        // algorithm, but it's also always present, so instead of adding ugly
        // branches to the actual iterator, we just chain it in right here.
        let init_group = is_opaque(Dir6::from_int(0).to_v2());
        Some(V2(0i, 0i)).into_iter().chain(Fov {
            is_opaque: is_opaque,
            range: range,
            stack: vec![Sector {
                begin: PolarPoint::new(0.0, 1),
                pt: PolarPoint::new(0.0, 1),
                end: PolarPoint::new(6.0, 1),
                group_opaque: init_group,
            }],
            side_channel: vec![],
        })
    }
}

impl<'a> Iterator<V2<int>> for Fov<'a> {
    fn next(&mut self) -> Option<V2<int>> {
        if let Some(ret) = self.side_channel.pop() {
            return Some(ret);
        }

        if let Some(mut current) = self.stack.pop() {
            if current.pt.is_below(current.end) {
                let pos = current.pt.to_v2();
                let current_opaque = (self.is_opaque)(pos);

                // Terrain opacity changed, branch out.
                if current_opaque != current.group_opaque {
                    // Add the rest of this sector with the new opacity.
                    self.stack.push(Sector {
                        begin: current.pt,
                        pt: current.pt,
                        end: current.end,
                        group_opaque: current_opaque,
                    });

                    // If this was a visible sector and we're below range, branch
                    // out further.
                    if !current.group_opaque && current.begin.radius < self.range {
                        self.stack.push(Sector {
                            begin: current.begin.further(),
                            pt: current.begin.further(),
                            end: current.pt.further(),
                            group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                        });
                    }
                    return self.next();
                }

                // Hack for making acute corner tiles of fake-isometric rooms
                // visible.
                if let Some(side_pt) = current.pt.side_point() {
                    // Only do this if both the front tiles and the target
                    // tile are opaque.
                    let next = current.pt.next();
                    if next.is_below(current.end)
                        && current.group_opaque
                        && (self.is_opaque)(next.to_v2())
                        && (self.is_opaque)(side_pt)
                        && current.begin.radius < self.range {
                            self.side_channel.push(side_pt);
                    }
                }

                // Proceed along the current sector.
                current.pt = current.pt.next();
                self.stack.push(current);
                return Some(pos);
            } else {
                // Hit the end of the sector.

                // If this was a visible sector and we're below range, branch
                // out further.
                if !current.group_opaque && current.begin.radius < self.range {
                    self.stack.push(Sector {
                        begin: current.begin.further(),
                        pt: current.begin.further(),
                        end: current.end.further(),
                        group_opaque: (self.is_opaque)(current.begin.further().to_v2()),
                    });
                }

                self.next()
            }
        } else {
            None
        }
    }
}

/// Points on a hex circle expressed in polar coordinates.
#[deriving(PartialEq)]
struct PolarPoint {
    pos: f32,
    radius: uint
}

impl PolarPoint {
    pub fn new(pos: f32, radius: uint) -> PolarPoint { PolarPoint { pos: pos, radius: radius } }
    /// Index of the discrete hex cell along the circle that corresponds to this point.
    fn winding_index(self) -> int { (self.pos + 0.5).floor() as int }

    pub fn is_below(self, other: PolarPoint) -> bool { self.winding_index() < other.end_index() }
    fn end_index(self) -> int { (self.pos + 0.5).ceil() as int }

    pub fn to_v2(self) -> V2<int> {
        if self.radius == 0 { return V2(0, 0); }
        let index = self.winding_index();
        let sector = index.mod_floor(&(self.radius as int * 6)) / self.radius as int;
        let offset = index.mod_floor(&(self.radius as int)) as int;
        let rod = Dir6::from_int(sector).to_v2() * (self.radius as int);
        let tangent = Dir6::from_int((sector + 2) % 6).to_v2() * offset;
        rod + tangent
    }

    /// If this point and the next point are adjacent vertically (along the xy
    /// axis), return a tuple of the point outside of the circle between the
    /// two points.
    ///
    /// This is a helper function for the FOV special case where acute corners
    /// of fake isometric rooms are marked visible even though strict hex FOV
    /// logic would keep them unseen.
    pub fn side_point(self) -> Option<V2<int>> {
        let next = self.next();
        let V2(x1, y1) = self.to_v2();
        let V2(x2, y2) = next.to_v2();

        if x2 == x1 + 1 && y2 == y1 + 1 {
            // Going down the right rim.
            Some(V2(x1 + 1, y1))
        } else if x2 == x1 - 1 && y2 == y1 - 1 {
            // Going up the left rim.
            Some(V2(x1 - 1, y1))
        } else {
            None
        }
    }

    /// The point corresponding to this one on the hex circle with radius +1.
    pub fn further(self) -> PolarPoint {
        PolarPoint::new(
            self.pos * (self.radius + 1) as f32 / self.radius as f32,
            self.radius + 1)
    }

    /// The point next to this one along the hex circle.
    pub fn next(self) -> PolarPoint {
        PolarPoint::new((self.pos + 0.5).floor() + 0.5, self.radius)
    }
}
<|endoftext|>