<commit_before>use errors::*;
use nom::IResult;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Targeting {
    target: String,
    #[serde(skip_serializing_if = "Option::is_none")] pub segment: Option<String>,
}

impl Targeting {
    fn new(targeting: &str, segment: Option<&str>) -> Self {
        Targeting {
            target: targeting.to_string(),
            segment: match segment {
                Some(s) => Some(s.to_string()),
                None => None,
            },
        }
    }
}

impl<'a> From<TargetingParsed<'a>> for Targeting {
    fn from(targeting: TargetingParsed<'a>) -> Self {
        match targeting {
            TargetingParsed::Gender(s) => Targeting::new("Gender", Some(s)),
            TargetingParsed::City(s) => Targeting::new("City", Some(s)),
            TargetingParsed::State(s) => Targeting::new("State", Some(s)),
            TargetingParsed::Region(s) => Targeting::new("Region", Some(s)),
            TargetingParsed::Age(s) => Targeting::new("Age", Some(s)),
            TargetingParsed::Interest(s) => Targeting::new("Interest", Some(s)),
            TargetingParsed::Segment(s) => Targeting::new("Segment", Some(s)),
            TargetingParsed::Retargeting(s) => Targeting::new("Retargeting", Some(s)),
            TargetingParsed::Agency(s) => Targeting::new("Agency", Some(s)),
            TargetingParsed::Website(s) => Targeting::new("Website", Some(s)),
            TargetingParsed::Language(s) => Targeting::new("Language", Some(s)),
            TargetingParsed::Employer(s) => Targeting::new("Employer", Some(s)),
            TargetingParsed::School(s) => Targeting::new("School", Some(s)),
            TargetingParsed::Like => Targeting::new("Like", None),
            TargetingParsed::List => Targeting::new("List", None),
            TargetingParsed::Advertiser(_) => {
                panic!("Advertiser is not allowed as a targeting field")
            }
        }
    }
}

// https://github.com/Geal/nom/issues/613
trait DbgFakeHex {
    fn to_hex(&self, chunk_size: usize) -> String;
}

impl DbgFakeHex for str {
    fn to_hex(&self, chunk_size: usize) -> String {
        use nom::HexDisplay;
        self.as_bytes().to_hex(chunk_size)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum TargetingParsed<'a> {
    Gender(&'a str),
    City(&'a str),
    State(&'a str),
    Region(&'a str),
    Age(&'a str),
    Interest(&'a str),
    Segment(&'a str),
    Retargeting(&'a str),
    Agency(&'a str),
    Website(&'a str),
    Language(&'a str),
    Employer(&'a str),
    School(&'a str),
    Like,
    List,
    // This is a special field that allows us to tie a page to a name
    Advertiser(&'a str),
}

named!(until_b(&str) -> &str, take_until!(""));

named!(list(&str) -> TargetingParsed,
   do_parse!(
       take_until!("added you to a list of people they want to reach on Facebook.") >>
       (TargetingParsed::List)
   )
);

named!(like(&str) -> TargetingParsed,
   do_parse!(
       alt!(
           take_until!("who like their")
           | take_until!("whose friends like their")
           | take_until!("Personen erreichen möchte, denen deren Seite gefällt.")
       ) >>
       (TargetingParsed::Like)
   )
);

named!(school(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("som har angivet skolen ") >>
        res: take_until!(" på") >>
        (TargetingParsed::Employer(res))
    )
);

named!(employer(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("Personen erreichen möchte, die ") >>
        res: take_until!(" als Arbeitgeber") >>
        (TargetingParsed::Employer(res))
    )
);

named!(provider(&str) -> TargetingParsed,
    do_parse!(
        alt!(
            take_until!("based on data provided by") |
            take_until!("wir basierend auf Daten von")
        ) >>
        take_until_and_consume!("") >>
        agency: until_b >> (TargetingParsed::Agency(agency))
    )
);

named!(advertiser_wants(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("including that ") >>
        advertiser: take_until!(" wants to reach") >>
        (TargetingParsed::Advertiser(advertiser))
    )
);

named!(advertiser_b(&str) -> TargetingParsed,
    do_parse!(
        alt!(
            take_until_and_consume!("is that ") |
            take_until_and_consume!("")
        ) >>
        advertiser: until_b >>
        (TargetingParsed::Advertiser(advertiser))
    )
);

named!(interest(&str) -> TargetingParsed,
   do_parse!(
        take_until_and_consume!("") >>
        interest: until_b >>
        (TargetingParsed::Interest(interest))
    )
);

named!(language(&str) -> TargetingParsed,
   alt!(
       do_parse!(
           take_until_and_consume!("die ") >>
           language: take_until!(" sprechen") >>
           (TargetingParsed::Language(language))
       )
       | do_parse!(
           take_until_and_consume!("who speak ") >>
           opt!(tag!("\"")) >>
           language: alt!(take_until_and_consume!("\"") | until_b) >>
           (TargetingParsed::Language(language))
       )
   )
);

named!(segment(&str) -> TargetingParsed,
    alt!(
        do_parse!(
            alt!(
                take_until_and_consume!("„") |
                take_until_and_consume!("\"") |
                take_until_and_consume!("„")
            ) >>
            segment: alt!(
                take_until!("\"") |
                take_until!("“") |
                take_until!("“")
            ) >>
            (TargetingParsed::Segment(segment))
        )
        | do_parse!(
            take_until_and_consume!("an audience called '")  >>
            segment: take_until!("'") >>
            (TargetingParsed::Segment(segment))
        )
        | do_parse!(
            take_until_and_consume!("an audience called \"")  >>
            segment: take_until!("\"") >>
            (TargetingParsed::Segment(segment))
        )

    )
);

named!(gender(&str) -> Option<TargetingParsed>,
    do_parse!(
        take_until_and_consume!("") >>
        result: alt!(
            alt!(
                tag!("men")
                | tag!("Männer")
                | tag!("mænd")
                | tag!("gli uomini")
                | tag!("män")
            ) => {|m| Some(TargetingParsed::Gender(m)) }
            | alt!(
                tag!("women")
                | tag!("Frauen")
                | tag!("kvinder")
            ) => {|f| Some(TargetingParsed::Gender(f)) }
            | alt!(
                tag!("people")
                | tag!("Personen")
                | tag!("personer")
                | tag!("le persone")
            ) => {|_| None }
        ) >>
        (result)
    )
);

named!(website(&str) -> TargetingParsed,
   do_parse!(
       take_until_and_consume!("wants to reach ") >>
       res: tag!("people who have visited their website or used one of their apps") >>
       (TargetingParsed::Website(res))
   )
);

named!(retargeting(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("") >>
        res: alt!(
            tag!("people who may be similar to their customers")
            | tag!("Personen, die deren Kunden ähneln")
            | tag!("personer, som minder om deres kunder")
            | tag!("i nærheden af deres virksomhed for nylig")
            | tag!("kürzlich in der Nähe des Unternehmens")
            | tag!("nyss varit i närheten av företaget")
            | tag!("recently near their business")
        ) >>
        (TargetingParsed::Retargeting(res))
    )
);

named!(age(&str) -> TargetingParsed,
    alt!(
        do_parse!(
          ws!(alt!(
              tag!("zwischen")
              | tag!("im Alter von")
              | tag!("i alderen")
              | tag!("i åldern")
              | tag!("på")
              | tag!("di età")
          )) >>
          complete: ws!(alt!(
              take_until_and_consume!("derover")
              | take_until_and_consume!("älter")
              | take_until_and_consume!("die in")
              | take_until_and_consume!("che vivono")
              | take_until_and_consume!(",")
          )) >>
          (TargetingParsed::Age(complete))
        ) |
        do_parse!(
            ws!(tag!("age")) >>
            ws!(opt!(tag!("s"))) >>
            ws!(opt!(tag!("d"))) >>
            complete: ws!(take_until!(" who ")) >>
            (TargetingParsed::Age(complete))
        )
    )
);

named!(region(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
         tag!("live")>>
         ws!(take_until_and_consume!("in")) >>
         country: until_b >>
         (vec![Some(TargetingParsed::Region(country))])
     )
);

named!(city_state(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
        ws!(alt!(
            take_until_and_consume!("near") |
            take_until_and_consume!("in") |
            take_until_and_consume!("af")
        )) >>
        city: ws!(take_until_and_consume!(",")) >>
        state: alt!(take_until!("wohnen oder") | until_b) >>
        (vec![Some(TargetingParsed::City(city)), Some(TargetingParsed::State(state))])
    )
);

named!(age_and_location(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
        a: opt!(ws!(age)) >>
        l: ws!(alt!(city_state | region)) >>
        (vec![&vec![a][..], &l[..]].concat())
    )
);

named!(get_targeting(&str) -> Vec<TargetingParsed>,
    do_parse!(
        advertiser_first: opt!(advertiser_b) >>
        sources_and_interests: opt!(alt!(
            interest
            | retargeting
            | employer
            | school
            | website
            | provider
            | language
            | segment
            | like
            | list
        )) >>
        advertiser_second: opt!(advertiser_wants) >>
        gender: gender >>
        location: age_and_location >>
        (vec![&vec![advertiser_first,
                    sources_and_interests,
                    advertiser_second,
                    gender][..], &location[..]]
              .concat().iter().filter_map(|x| x.clone()).collect())
    )
);

pub fn collect_targeting(thing: &str) -> Result<Vec<Targeting>> {
    parse_targeting(thing).map(|result| {
        result
            .into_iter()
            .filter(|t| match *t {
                TargetingParsed::Advertiser(_) => false,
                _ => true,
            })
            .map(Targeting::from)
            .collect()
    })
}

pub fn collect_advertiser(thing: &str) -> Option<String> {
    match parse_targeting(thing) {
        Ok(result) => result
            .into_iter()
            .filter(|t| match *t {
                TargetingParsed::Advertiser(_) => true,
                _ => false,
            })
            .nth(0)
            .map(|t| match t {
                TargetingParsed::Advertiser(a) => a.to_string(),
                _ => panic!("Somehow got something other than an Advertiser"),
            }),
        _ => None,
    }
}

pub fn parse_targeting(thing: &str) -> Result<Vec<TargetingParsed>> {
    match get_targeting(thing) {
        IResult::Done(_, result) => Ok(result),
        IResult::Error(e) => Err(e.into()),
        IResult::Incomplete(e) => Err(ErrorKind::TargetingIncomplete(e).into()),
    }
}

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

    #[test]
    fn test_targeting() {
        assert_eq!(
            segment("\"Generation X\""),
            IResult::Done("\"", TargetingParsed::Segment("Generation X"))
        );
        let g = "Google";
        assert_eq!(
            advertiser_b(&format!("{}", g)),
            IResult::Done("", TargetingParsed::Advertiser(g))
        );
        let i = "American Federation of State, County and Municipal Employees";
        assert_eq!(
            interest(&format!("{}", i)),
            IResult::Done("", TargetingParsed::Interest(i))
        );
        assert_eq!(
            gender("men"),
            IResult::Done("", Some(TargetingParsed::Gender("men")))
        );
        assert_eq!(
            gender("women"),
            IResult::Done("", Some(TargetingParsed::Gender("women")))
        );
        assert_eq!(gender("people"), IResult::Done("", None));
        assert_eq!(
            age(" ages 26 to 62 who "),
            IResult::Done("who ", TargetingParsed::Age("26 to 62"))
        );
        assert_eq!(
            region("live in United States"),
            IResult::Done("", vec![Some(TargetingParsed::Region("United States"))])
        );
        assert_eq!(
            city_state("near Burlington, North Carolina"),
            IResult::Done(
                "",
                vec![
                    Some(TargetingParsed::City("Burlington")),
                    Some(TargetingParsed::State("North Carolina")),
                ],
            )
        );
        assert_eq!(
            age_and_location(
                "ages 18 and older who live or were recently near San Francisco, California",
            ),
            IResult::Done(
                "",
                vec![
                    Some(TargetingParsed::Age("18 and older")),
                    Some(TargetingParsed::City("San Francisco")),
                    Some(TargetingParsed::State("California")),
                ],
            )
        );
    }
}
<commit_msg>adds a test to make sure we're parsing audiences right<commit_after>use errors::*;
use nom::IResult;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Targeting {
    target: String,
    #[serde(skip_serializing_if = "Option::is_none")] pub segment: Option<String>,
}

impl Targeting {
    fn new(targeting: &str, segment: Option<&str>) -> Self {
        Targeting {
            target: targeting.to_string(),
            segment: match segment {
                Some(s) => Some(s.to_string()),
                None => None,
            },
        }
    }
}

impl<'a> From<TargetingParsed<'a>> for Targeting {
    fn from(targeting: TargetingParsed<'a>) -> Self {
        match targeting {
            TargetingParsed::Gender(s) => Targeting::new("Gender", Some(s)),
            TargetingParsed::City(s) => Targeting::new("City", Some(s)),
            TargetingParsed::State(s) => Targeting::new("State", Some(s)),
            TargetingParsed::Region(s) => Targeting::new("Region", Some(s)),
            TargetingParsed::Age(s) => Targeting::new("Age", Some(s)),
            TargetingParsed::Interest(s) => Targeting::new("Interest", Some(s)),
            TargetingParsed::Segment(s) => Targeting::new("Segment", Some(s)),
            TargetingParsed::Retargeting(s) => Targeting::new("Retargeting", Some(s)),
            TargetingParsed::Agency(s) => Targeting::new("Agency", Some(s)),
            TargetingParsed::Website(s) => Targeting::new("Website", Some(s)),
            TargetingParsed::Language(s) => Targeting::new("Language", Some(s)),
            TargetingParsed::Employer(s) => Targeting::new("Employer", Some(s)),
            TargetingParsed::School(s) => Targeting::new("School", Some(s)),
            TargetingParsed::Like => Targeting::new("Like", None),
            TargetingParsed::List => Targeting::new("List", None),
            TargetingParsed::Advertiser(_) => {
                panic!("Advertiser is not allowed as a targeting field")
            }
        }
    }
}

// https://github.com/Geal/nom/issues/613
trait DbgFakeHex {
    fn to_hex(&self, chunk_size: usize) -> String;
}

impl DbgFakeHex for str {
    fn to_hex(&self, chunk_size: usize) -> String {
        use nom::HexDisplay;
        self.as_bytes().to_hex(chunk_size)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum TargetingParsed<'a> {
    Gender(&'a str),
    City(&'a str),
    State(&'a str),
    Region(&'a str),
    Age(&'a str),
    Interest(&'a str),
    Segment(&'a str),
    Retargeting(&'a str),
    Agency(&'a str),
    Website(&'a str),
    Language(&'a str),
    Employer(&'a str),
    School(&'a str),
    Like,
    List,
    // This is a special field that allows us to tie a page to a name
    Advertiser(&'a str),
}

named!(until_b(&str) -> &str, take_until!(""));

named!(list(&str) -> TargetingParsed,
   do_parse!(
       take_until!("added you to a list of people they want to reach on Facebook.") >>
       (TargetingParsed::List)
   )
);

named!(like(&str) -> TargetingParsed,
   do_parse!(
       alt!(
           take_until!("who like their")
           | take_until!("whose friends like their")
           | take_until!("Personen erreichen möchte, denen deren Seite gefällt.")
       ) >>
       (TargetingParsed::Like)
   )
);

named!(school(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("som har angivet skolen ") >>
        res: take_until!(" på") >>
        (TargetingParsed::Employer(res))
    )
);

named!(employer(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("Personen erreichen möchte, die ") >>
        res: take_until!(" als Arbeitgeber") >>
        (TargetingParsed::Employer(res))
    )
);

named!(provider(&str) -> TargetingParsed,
    do_parse!(
        alt!(
            take_until!("based on data provided by") |
            take_until!("wir basierend auf Daten von")
        ) >>
        take_until_and_consume!("") >>
        agency: until_b >> (TargetingParsed::Agency(agency))
    )
);

named!(advertiser_wants(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("including that ") >>
        advertiser: take_until!(" wants to reach") >>
        (TargetingParsed::Advertiser(advertiser))
    )
);

named!(advertiser_b(&str) -> TargetingParsed,
    do_parse!(
        alt!(
            take_until_and_consume!("is that ") |
            take_until_and_consume!("")
        ) >>
        advertiser: until_b >>
        (TargetingParsed::Advertiser(advertiser))
    )
);

named!(interest(&str) -> TargetingParsed,
   do_parse!(
        take_until_and_consume!("") >>
        interest: until_b >>
        (TargetingParsed::Interest(interest))
    )
);

named!(language(&str) -> TargetingParsed,
   alt!(
       do_parse!(
           take_until_and_consume!("die ") >>
           language: take_until!(" sprechen") >>
           (TargetingParsed::Language(language))
       )
       | do_parse!(
           take_until_and_consume!("who speak ") >>
           opt!(tag!("\"")) >>
           language: alt!(take_until_and_consume!("\"") | until_b) >>
           (TargetingParsed::Language(language))
       )
   )
);

named!(segment(&str) -> TargetingParsed,
    alt!(
        do_parse!(
            alt!(
                take_until_and_consume!("„") |
                take_until_and_consume!("\"") |
                take_until_and_consume!("„")
            ) >>
            segment: alt!(
                take_until!("\"") |
                take_until!("“") |
                take_until!("“")
            ) >>
            (TargetingParsed::Segment(segment))
        )
        | do_parse!(
            take_until_and_consume!("an audience called '")  >>
            segment: take_until!("'") >>
            (TargetingParsed::Segment(segment))
        )
        | do_parse!(
            take_until_and_consume!("an audience called \"")  >>
            segment: take_until!("\"") >>
            (TargetingParsed::Segment(segment))
        )

    )
);

named!(gender(&str) -> Option<TargetingParsed>,
    do_parse!(
        take_until_and_consume!("") >>
        result: alt!(
            alt!(
                tag!("men")
                | tag!("Männer")
                | tag!("mænd")
                | tag!("gli uomini")
                | tag!("män")
            ) => {|m| Some(TargetingParsed::Gender(m)) }
            | alt!(
                tag!("women")
                | tag!("Frauen")
                | tag!("kvinder")
            ) => {|f| Some(TargetingParsed::Gender(f)) }
            | alt!(
                tag!("people")
                | tag!("Personen")
                | tag!("personer")
                | tag!("le persone")
            ) => {|_| None }
        ) >>
        (result)
    )
);

named!(website(&str) -> TargetingParsed,
   do_parse!(
       take_until_and_consume!("wants to reach ") >>
       res: tag!("people who have visited their website or used one of their apps") >>
       (TargetingParsed::Website(res))
   )
);

named!(retargeting(&str) -> TargetingParsed,
    do_parse!(
        take_until_and_consume!("") >>
        res: alt!(
            tag!("people who may be similar to their customers")
            | tag!("Personen, die deren Kunden ähneln")
            | tag!("personer, som minder om deres kunder")
            | tag!("i nærheden af deres virksomhed for nylig")
            | tag!("kürzlich in der Nähe des Unternehmens")
            | tag!("nyss varit i närheten av företaget")
            | tag!("recently near their business")
        ) >>
        (TargetingParsed::Retargeting(res))
    )
);

named!(age(&str) -> TargetingParsed,
    alt!(
        do_parse!(
          ws!(alt!(
              tag!("zwischen")
              | tag!("im Alter von")
              | tag!("i alderen")
              | tag!("i åldern")
              | tag!("på")
              | tag!("di età")
          )) >>
          complete: ws!(alt!(
              take_until_and_consume!("derover")
              | take_until_and_consume!("älter")
              | take_until_and_consume!("die in")
              | take_until_and_consume!("che vivono")
              | take_until_and_consume!(",")
          )) >>
          (TargetingParsed::Age(complete))
        ) |
        do_parse!(
            ws!(tag!("age")) >>
            ws!(opt!(tag!("s"))) >>
            ws!(opt!(tag!("d"))) >>
            complete: ws!(take_until!(" who ")) >>
            (TargetingParsed::Age(complete))
        )
    )
);

named!(region(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
         tag!("live")>>
         ws!(take_until_and_consume!("in")) >>
         country: until_b >>
         (vec![Some(TargetingParsed::Region(country))])
     )
);

named!(city_state(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
        ws!(alt!(
            take_until_and_consume!("near") |
            take_until_and_consume!("in") |
            take_until_and_consume!("af")
        )) >>
        city: ws!(take_until_and_consume!(",")) >>
        state: alt!(take_until!("wohnen oder") | until_b) >>
        (vec![Some(TargetingParsed::City(city)), Some(TargetingParsed::State(state))])
    )
);

named!(age_and_location(&str) -> Vec<Option<TargetingParsed>>,
    do_parse!(
        a: opt!(ws!(age)) >>
        l: ws!(alt!(city_state | region)) >>
        (vec![&vec![a][..], &l[..]].concat())
    )
);

named!(get_targeting(&str) -> Vec<TargetingParsed>,
    do_parse!(
        advertiser_first: opt!(advertiser_b) >>
        sources_and_interests: opt!(alt!(
            interest
            | retargeting
            | employer
            | school
            | website
            | provider
            | language
            | segment
            | like
            | list
        )) >>
        advertiser_second: opt!(advertiser_wants) >>
        gender: gender >>
        location: age_and_location >>
        (vec![&vec![advertiser_first,
                    sources_and_interests,
                    advertiser_second,
                    gender][..], &location[..]]
              .concat().iter().filter_map(|x| x.clone()).collect())
    )
);

pub fn collect_targeting(thing: &str) -> Result<Vec<Targeting>> {
    parse_targeting(thing).map(|result| {
        result
            .into_iter()
            .filter(|t| match *t {
                TargetingParsed::Advertiser(_) => false,
                _ => true,
            })
            .map(Targeting::from)
            .collect()
    })
}

pub fn collect_advertiser(thing: &str) -> Option<String> {
    match parse_targeting(thing) {
        Ok(result) => result
            .into_iter()
            .filter(|t| match *t {
                TargetingParsed::Advertiser(_) => true,
                _ => false,
            })
            .nth(0)
            .map(|t| match t {
                TargetingParsed::Advertiser(a) => a.to_string(),
                _ => panic!("Somehow got something other than an Advertiser"),
            }),
        _ => None,
    }
}

pub fn parse_targeting(thing: &str) -> Result<Vec<TargetingParsed>> {
    match get_targeting(thing) {
        IResult::Done(_, result) => Ok(result),
        IResult::Error(e) => Err(e.into()),
        IResult::Incomplete(e) => Err(ErrorKind::TargetingIncomplete(e).into()),
    }
}

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

    #[test]
    fn test_targeting() {
        assert_eq!(
            segment("\"Generation X\""),
            IResult::Done("\"", TargetingParsed::Segment("Generation X"))
        );
        let g = "Google";
        assert_eq!(
            advertiser_b(&format!("{}", g)),
            IResult::Done("", TargetingParsed::Advertiser(g))
        );
        let i = "American Federation of State, County and Municipal Employees";
        assert_eq!(
            interest(&format!("{}", i)),
            IResult::Done("", TargetingParsed::Interest(i))
        );
        assert_eq!(
            gender("men"),
            IResult::Done("", Some(TargetingParsed::Gender("men")))
        );
        assert_eq!(
            gender("women"),
            IResult::Done("", Some(TargetingParsed::Gender("women")))
        );
        assert_eq!(gender("people"), IResult::Done("", None));
        assert_eq!(
            age(" ages 26 to 62 who "),
            IResult::Done("who ", TargetingParsed::Age("26 to 62"))
        );
        assert_eq!(
            region("live in United States"),
            IResult::Done("", vec![Some(TargetingParsed::Region("United States"))])
        );
        assert_eq!(
            city_state("near Burlington, North Carolina"),
            IResult::Done(
                "",
                vec![
                    Some(TargetingParsed::City("Burlington")),
                    Some(TargetingParsed::State("North Carolina")),
                ],
            )
        );
        assert_eq!(
            age_and_location(
                "ages 18 and older who live or were recently near San Francisco, California",
            ),
            IResult::Done(
                "",
                vec![
                    Some(TargetingParsed::Age("18 and older")),
                    Some(TargetingParsed::City("San Francisco")),
                    Some(TargetingParsed::State("California")),
                ],
            )
        );

        assert_eq!(
            segment(" an audience called 'Member of a family-based household.'"),
            IResult::Done(
                "'",
                TargetingParsed::Segment("Member of a family-based household.")
            )
        );
    }
}
<|endoftext|>


<commit_before>use pin_project_lite::pin_project;
use std::cell::RefCell;
use std::error::Error;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{fmt, thread};

/// Declares a new task-local key of type [`tokio::task::LocalKey`].
///
/// # Syntax
///
/// The macro wraps any number of static declarations and makes them local to the current task.
/// Publicity and attributes for each static is preserved. For example:
///
/// # Examples
///
/// ```
/// # use tokio::task_local;
/// task_local! {
///     pub static ONE: u32;
///
///     #[allow(unused)]
///     static TWO: f32;
/// }
/// # fn main() {}
/// ```
///
/// See [LocalKey documentation][`tokio::task::LocalKey`] for more
/// information.
///
/// [`tokio::task::LocalKey`]: ../tokio/task/struct.LocalKey.html
#[macro_export]
macro_rules! task_local {
     // empty (base case for the recursion)
    () => {};

    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty; $($rest:tt)*) => {
        $crate::__task_local_inner!($(#[$attr])* $vis $name, $t);
        $crate::task_local!($($rest)*);
    };

    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty) => {
        $crate::__task_local_inner!($(#[$attr])* $vis $name, $t);
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! __task_local_inner {
    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
        static $name: $crate::task::LocalKey<$t> = {
            std::thread_local! {
                static __KEY: std::cell::RefCell<Option<$t>> = std::cell::RefCell::new(None);
            }

            $crate::task::LocalKey { inner: __KEY }
        };
    };
}

/// A key for task-local data.
///
/// This type is generated by the `task_local!` macro.
///
/// Unlike [`std::thread::LocalKey`], `tokio::task::LocalKey` will
/// _not_ lazily initialize the value on first access. Instead, the
/// value is first initialized when the future containing
/// the task-local is first polled by a futures executor, like Tokio.
///
/// # Examples
///
/// ```
/// # async fn dox() {
/// tokio::task_local! {
///     static NUMBER: u32;
/// }
///
/// NUMBER.scope(1, async move {
///     assert_eq!(NUMBER.get(), 1);
/// }).await;
///
/// NUMBER.scope(2, async move {
///     assert_eq!(NUMBER.get(), 2);
///
///     NUMBER.scope(3, async move {
///         assert_eq!(NUMBER.get(), 3);
///     }).await;
/// }).await;
/// # }
/// ```
/// [`std::thread::LocalKey`]: https://doc.rust-lang.org/std/thread/struct.LocalKey.html
pub struct LocalKey<T: 'static> {
    #[doc(hidden)]
    pub inner: thread::LocalKey<RefCell<Option<T>>>,
}

impl<T: 'static> LocalKey<T> {
    /// Sets a value `T` as the task-local value for the future `F`.
    ///
    /// On completion of `scope`, the task-local will be dropped.
    ///
    /// ### Examples
    ///
    /// ```
    /// # async fn dox() {
    /// tokio::task_local! {
    ///     static NUMBER: u32;
    /// }
    ///
    /// NUMBER.scope(1, async move {
    ///     println!("task local value: {}", NUMBER.get());
    /// }).await;
    /// # }
    /// ```
    pub async fn scope<F>(&'static self, value: T, f: F) -> F::Output
    where
        F: Future,
    {
        TaskLocalFuture {
            local: &self,
            slot: Some(value),
            future: f,
        }
        .await
    }

    /// Accesses the current task-local and runs the provided closure.
    ///
    /// # Panics
    ///
    /// This function will panic if not called within the context
    /// of a future containing a task-local with the corresponding key.
    pub fn with<F, R>(&'static self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        self.try_with(f).expect(
            "cannot access a Task Local Storage value \
             without setting it via `LocalKey::set`",
        )
    }

    /// Accesses the current task-local and runs the provided closure.
    ///
    /// If the task-local with the accociated key is not present, this
    /// method will return an `AccessError`. For a panicking variant,
    /// see `with`.
    pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
    where
        F: FnOnce(&T) -> R,
    {
        self.inner.with(|v| {
            if let Some(val) = v.borrow().as_ref() {
                Ok(f(val))
            } else {
                Err(AccessError { _private: () })
            }
        })
    }
}

impl<T: Copy + 'static> LocalKey<T> {
    /// Returns a copy of the task-local value
    /// if the task-local value implements `Copy`.
    pub fn get(&'static self) -> T {
        self.with(|v| *v)
    }
}

impl<T: 'static> fmt::Debug for LocalKey<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("LocalKey { .. }")
    }
}

pin_project! {
    struct TaskLocalFuture<T: StaticLifetime, F> {
        local: &'static LocalKey<T>,
        slot: Option<T>,
        #[pin]
        future: F,
    }
}

impl<T: 'static, F: Future> Future for TaskLocalFuture<T, F> {
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        struct Guard<'a, T: 'static> {
            local: &'static LocalKey<T>,
            slot: &'a mut Option<T>,
            prev: Option<T>,
        }

        impl<T> Drop for Guard<'_, T> {
            fn drop(&mut self) {
                let value = self.local.inner.with(|c| c.replace(self.prev.take()));
                *self.slot = value;
            }
        }

        let mut project = self.project();
        let val = project.slot.take();

        let prev = project.local.inner.with(|c| c.replace(val));

        let _guard = Guard {
            prev,
            slot: &mut project.slot,
            local: *project.local,
        };

        project.future.poll(cx)
    }
}

// Required to make `pin_project` happy.
trait StaticLifetime: 'static {}
impl<T: 'static> StaticLifetime for T {}

/// An error returned by [`LocalKey::try_with`](method@LocalKey::try_with).
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct AccessError {
    _private: (),
}

impl fmt::Debug for AccessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AccessError").finish()
    }
}

impl fmt::Display for AccessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt("task-local value not set", f)
    }
}

impl Error for AccessError {}
<commit_msg>task: Ensure the visibility modifier is propagated when constructing a task local (#2416)<commit_after>use pin_project_lite::pin_project;
use std::cell::RefCell;
use std::error::Error;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{fmt, thread};

/// Declares a new task-local key of type [`tokio::task::LocalKey`].
///
/// # Syntax
///
/// The macro wraps any number of static declarations and makes them local to the current task.
/// Publicity and attributes for each static is preserved. For example:
///
/// # Examples
///
/// ```
/// # use tokio::task_local;
/// task_local! {
///     pub static ONE: u32;
///
///     #[allow(unused)]
///     static TWO: f32;
/// }
/// # fn main() {}
/// ```
///
/// See [LocalKey documentation][`tokio::task::LocalKey`] for more
/// information.
///
/// [`tokio::task::LocalKey`]: ../tokio/task/struct.LocalKey.html
#[macro_export]
macro_rules! task_local {
     // empty (base case for the recursion)
    () => {};

    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty; $($rest:tt)*) => {
        $crate::__task_local_inner!($(#[$attr])* $vis $name, $t);
        $crate::task_local!($($rest)*);
    };

    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty) => {
        $crate::__task_local_inner!($(#[$attr])* $vis $name, $t);
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! __task_local_inner {
    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty) => {
        $vis static $name: $crate::task::LocalKey<$t> = {
            std::thread_local! {
                static __KEY: std::cell::RefCell<Option<$t>> = std::cell::RefCell::new(None);
            }

            $crate::task::LocalKey { inner: __KEY }
        };
    };
}

/// A key for task-local data.
///
/// This type is generated by the `task_local!` macro.
///
/// Unlike [`std::thread::LocalKey`], `tokio::task::LocalKey` will
/// _not_ lazily initialize the value on first access. Instead, the
/// value is first initialized when the future containing
/// the task-local is first polled by a futures executor, like Tokio.
///
/// # Examples
///
/// ```
/// # async fn dox() {
/// tokio::task_local! {
///     static NUMBER: u32;
/// }
///
/// NUMBER.scope(1, async move {
///     assert_eq!(NUMBER.get(), 1);
/// }).await;
///
/// NUMBER.scope(2, async move {
///     assert_eq!(NUMBER.get(), 2);
///
///     NUMBER.scope(3, async move {
///         assert_eq!(NUMBER.get(), 3);
///     }).await;
/// }).await;
/// # }
/// ```
/// [`std::thread::LocalKey`]: https://doc.rust-lang.org/std/thread/struct.LocalKey.html
pub struct LocalKey<T: 'static> {
    #[doc(hidden)]
    pub inner: thread::LocalKey<RefCell<Option<T>>>,
}

impl<T: 'static> LocalKey<T> {
    /// Sets a value `T` as the task-local value for the future `F`.
    ///
    /// On completion of `scope`, the task-local will be dropped.
    ///
    /// ### Examples
    ///
    /// ```
    /// # async fn dox() {
    /// tokio::task_local! {
    ///     static NUMBER: u32;
    /// }
    ///
    /// NUMBER.scope(1, async move {
    ///     println!("task local value: {}", NUMBER.get());
    /// }).await;
    /// # }
    /// ```
    pub async fn scope<F>(&'static self, value: T, f: F) -> F::Output
    where
        F: Future,
    {
        TaskLocalFuture {
            local: &self,
            slot: Some(value),
            future: f,
        }
        .await
    }

    /// Accesses the current task-local and runs the provided closure.
    ///
    /// # Panics
    ///
    /// This function will panic if not called within the context
    /// of a future containing a task-local with the corresponding key.
    pub fn with<F, R>(&'static self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        self.try_with(f).expect(
            "cannot access a Task Local Storage value \
             without setting it via `LocalKey::set`",
        )
    }

    /// Accesses the current task-local and runs the provided closure.
    ///
    /// If the task-local with the accociated key is not present, this
    /// method will return an `AccessError`. For a panicking variant,
    /// see `with`.
    pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>
    where
        F: FnOnce(&T) -> R,
    {
        self.inner.with(|v| {
            if let Some(val) = v.borrow().as_ref() {
                Ok(f(val))
            } else {
                Err(AccessError { _private: () })
            }
        })
    }
}

impl<T: Copy + 'static> LocalKey<T> {
    /// Returns a copy of the task-local value
    /// if the task-local value implements `Copy`.
    pub fn get(&'static self) -> T {
        self.with(|v| *v)
    }
}

impl<T: 'static> fmt::Debug for LocalKey<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("LocalKey { .. }")
    }
}

pin_project! {
    struct TaskLocalFuture<T: StaticLifetime, F> {
        local: &'static LocalKey<T>,
        slot: Option<T>,
        #[pin]
        future: F,
    }
}

impl<T: 'static, F: Future> Future for TaskLocalFuture<T, F> {
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        struct Guard<'a, T: 'static> {
            local: &'static LocalKey<T>,
            slot: &'a mut Option<T>,
            prev: Option<T>,
        }

        impl<T> Drop for Guard<'_, T> {
            fn drop(&mut self) {
                let value = self.local.inner.with(|c| c.replace(self.prev.take()));
                *self.slot = value;
            }
        }

        let mut project = self.project();
        let val = project.slot.take();

        let prev = project.local.inner.with(|c| c.replace(val));

        let _guard = Guard {
            prev,
            slot: &mut project.slot,
            local: *project.local,
        };

        project.future.poll(cx)
    }
}

// Required to make `pin_project` happy.
trait StaticLifetime: 'static {}
impl<T: 'static> StaticLifetime for T {}

/// An error returned by [`LocalKey::try_with`](method@LocalKey::try_with).
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct AccessError {
    _private: (),
}

impl fmt::Debug for AccessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("AccessError").finish()
    }
}

impl fmt::Display for AccessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt("task-local value not set", f)
    }
}

impl Error for AccessError {}
<|endoftext|>


<commit_before>//! This module is for everything about the rules of chess.

pub use self::color::{Color, White, Black};
pub use self::piece::Piece;
pub use self::piece::Type as PieceType;
pub use self::piece::{WP,WK,WQ,WB,WN,WR};
pub use self::piece::{BP,BK,BQ,BB,BN,BR};
pub use self::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
pub use self::moves::{Move, FromTo};
pub use self::pos::Position;
pub use self::fen::{fen_to_position, start_pos};
pub use self::legal::receive_legal;
pub use self::make_move::{make_move};
pub use self::mate::{is_checkmated, is_stalemated};
pub use self::mate::{is_checkmate, is_stalemate};

mod color;
mod piece;
mod square;
mod moves;

mod bitboard;
mod board;
mod castle;
pub mod pos;

mod fen;
mod gen;

mod legal;
mod make_move;

mod mate;

#[cfg(test)]
mod tests;
<commit_msg>Make unmake_move() public<commit_after>//! This module is for everything about the rules of chess.

pub use self::color::{Color, White, Black};
pub use self::piece::Piece;
pub use self::piece::Type as PieceType;
pub use self::piece::{WP,WK,WQ,WB,WN,WR};
pub use self::piece::{BP,BK,BQ,BB,BN,BR};
pub use self::piece::Type::{Pawn,King,Queen,Bishop,Knight,Rook};
pub use self::moves::{Move, FromTo};
pub use self::pos::Position;
pub use self::fen::{fen_to_position, start_pos};
pub use self::legal::receive_legal;
pub use self::make_move::{make_move, unmake_move};
pub use self::mate::{is_checkmated, is_stalemated};
pub use self::mate::{is_checkmate, is_stalemate};

mod color;
mod piece;
mod square;
mod moves;

mod bitboard;
mod board;
mod castle;
pub mod pos;

mod fen;
mod gen;

mod legal;
mod make_move;

mod mate;

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>//! A collection of serialization and deserialization functions
//! that use the `serde` crate for the serializable and deserializable
//! implementation.

use std::io::{Write, Read};
use std::io::Error as IoError;
use std::{error, fmt, result};
use ::{CountSize, SizeLimit};
use byteorder::{ByteOrder};

pub use super::de::{
    Deserializer,
};

pub use super::ser::{
    Serializer,
};

use super::ser::SizeChecker;

use serde_crate as serde;

/// The result of a serialization or deserialization operation.
pub type Result<T> = result::Result<T, Error>;

/// An error that can be produced during (de)serializing.
///
/// If decoding from a Buffer, assume that the buffer has been left
/// in an invalid state.
pub type Error = Box<ErrorKind>;

/// The kind of error that can be produced during a serialization or deserialization.
#[derive(Debug)]
pub enum ErrorKind {
    /// If the error stems from the reader/writer that is being used
    /// during (de)serialization, that error will be stored and returned here.
    Io(IoError),
    /// If the bytes in the reader are not decodable because of an invalid
    /// encoding, this error will be returned.  This error is only possible
    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
    /// should **never** produce an InvalidEncoding error.
    InvalidEncoding {
        #[allow(missing_docs)]
        desc: &'static str,
        #[allow(missing_docs)]
        detail: Option<String>
    },
    /// If (de)serializing a message takes more than the provided size limit, this
    /// error is returned.
    SizeLimit,
    /// Bincode can not encode sequences of unknown length (like iterators).
    SequenceMustHaveLength,
    /// A custom error message from Serde.
    Custom(String)
}

impl error::Error for ErrorKind {
    fn description(&self) -> &str {
        match *self {
            ErrorKind::Io(ref err) => error::Error::description(err),
            ErrorKind::InvalidEncoding{desc, ..} => desc,
            ErrorKind::SequenceMustHaveLength => "bincode can't encode infinite sequences",
            ErrorKind::SizeLimit => "the size limit for decoding has been reached",
            ErrorKind::Custom(ref msg) => msg,

        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ErrorKind::Io(ref err) => err.cause(),
            ErrorKind::InvalidEncoding{..} => None,
            ErrorKind::SequenceMustHaveLength => None,
            ErrorKind::SizeLimit => None,
            ErrorKind::Custom(_) => None,
        }
    }
}

impl From<IoError> for Error {
    fn from(err: IoError) -> Error {
        ErrorKind::Io(err).into()
    }
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ErrorKind::Io(ref ioerr) =>
                write!(fmt, "Io: {}", ioerr),
            ErrorKind::InvalidEncoding{desc, detail: None}=>
                write!(fmt, "InvalidEncoding: {}", desc),
            ErrorKind::InvalidEncoding{desc, detail: Some(ref detail)}=>
                write!(fmt, "InvalidEncoding: {} ({})", desc, detail),
            ErrorKind::SequenceMustHaveLength =>
                write!(fmt, "Bincode can only encode sequences and maps that have a knowable size ahead of time."),
            ErrorKind::SizeLimit =>
                write!(fmt, "SizeLimit"),
            ErrorKind::Custom(ref s) =>
                s.fmt(fmt),
        }
    }
}

impl serde::de::Error for Error {
    fn custom<T: fmt::Display>(desc: T) -> Error {
        ErrorKind::Custom(desc.to_string()).into()
    }
}

impl serde::ser::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        ErrorKind::Custom(msg.to_string()).into()
    }
}

/// Serializes an object directly into a `Writer`.
///
/// If the serialization would take more bytes than allowed by `size_limit`, an error
/// is returned and *no bytes* will be written into the `Writer`.
///
/// If this returns an `Error` (other than SizeLimit), assume that the
/// writer is in an invalid state, as writing could bail out in the middle of
/// serializing.
pub fn serialize_into<W: ?Sized, T: ?Sized, S, E>(writer: &mut W, value: &T, size_limit: S) -> Result<()>
    where W: Write, T: serde::Serialize, S: SizeLimit, E: ByteOrder
{
    if let Some(limit) = size_limit.limit() {
        try!(serialized_size_bounded(value, limit).ok_or(ErrorKind::SizeLimit));
    }

    let mut serializer = Serializer::<_, E>::new(writer);
    serde::Serialize::serialize(value, &mut serializer)
}

/// Serializes a serializable object into a `Vec` of bytes.
///
/// If the serialization would take more bytes than allowed by `size_limit`,
/// an error is returned.
pub fn serialize<T: ?Sized, S, E>(value: &T, size_limit: S) -> Result<Vec<u8>>
    where T: serde::Serialize, S: SizeLimit, E: ByteOrder
{
    let mut writer = match size_limit.limit() {
        Some(size_limit) => {
            let actual_size = try!(serialized_size_bounded(value, size_limit).ok_or(ErrorKind::SizeLimit));
            Vec::with_capacity(actual_size as usize)
        }
        None => {
            let size = serialized_size(value) as usize;
            Vec::with_capacity(size)
        }
    };

    try!(serialize_into::<_, _, _, E>(&mut writer, value, super::Infinite));
    Ok(writer)
}

impl SizeLimit for CountSize {
    fn add(&mut self, c: u64) -> Result<()> {
        self.total += c;
        if let Some(limit) = self.limit {
            if self.total > limit {
                return Err(Box::new(ErrorKind::SizeLimit))
            }
        }
        Ok(())
    }

    fn limit(&self) -> Option<u64> {
        unreachable!();
    }
}

/// Returns the size that an object would be if serialized using bincode.
///
/// This is used internally as part of the check for encode_into, but it can
/// be useful for preallocating buffers if thats your style.
pub fn serialized_size<T: ?Sized>(value: &T) -> u64
    where T: serde::Serialize
{
    let mut size_counter = SizeChecker {
        size_limit: CountSize { total: 0, limit: None }
    };

    value.serialize(&mut size_counter).ok();
    size_counter.size_limit.total
}

/// Given a maximum size limit, check how large an object would be if it
/// were to be serialized.
///
/// If it can be serialized in `max` or fewer bytes, that number will be returned
/// inside `Some`.  If it goes over bounds, then None is returned.
pub fn serialized_size_bounded<T: ?Sized>(value: &T, max: u64) -> Option<u64>
    where T: serde::Serialize
{
    let mut size_counter = SizeChecker {
        size_limit: CountSize { total: 0, limit: Some(max) }
    };

    match value.serialize(&mut size_counter) {
        Ok(_) => Some(size_counter.size_limit.total),
        Err(_) => None,
    }
}

/// Deserializes an object directly from a `Buffer`ed Reader.
///
/// If the provided `SizeLimit` is reached, the deserialization will bail immediately.
/// A SizeLimit can help prevent an attacker from flooding your server with
/// a neverending stream of values that runs your server out of memory.
///
/// If this returns an `Error`, assume that the buffer that you passed
/// in is in an invalid state, as the error could be returned during any point
/// in the reading.
pub fn deserialize_from<R: ?Sized, T, S, E>(reader: &mut R, size_limit: S) -> Result<T>
    where R: Read, T: serde::de::DeserializeOwned, S: SizeLimit, E: ByteOrder
{
    let reader = ::de::read::IoReadReader::new(reader);
    let mut deserializer = Deserializer::<_, S, E>::new(reader, size_limit);
    serde::Deserialize::deserialize(&mut deserializer)
}

/// Deserializes a slice of bytes into an object.
///
/// This method does not have a size-limit because if you already have the bytes
/// in memory, then you don't gain anything by having a limiter.
pub fn deserialize<'a, T, E: ByteOrder>(bytes: &'a [u8]) -> Result<T>
    where T: serde::de::Deserialize<'a>,
{
    let reader = ::de::read::SliceReader::new(bytes);
    let mut deserializer = Deserializer::<_, _, E>::new(reader, super::Infinite);
    serde::Deserialize::deserialize(&mut deserializer)
}
<commit_msg>Correctly report the cause of IO error<commit_after>//! A collection of serialization and deserialization functions
//! that use the `serde` crate for the serializable and deserializable
//! implementation.

use std::io::{Write, Read};
use std::io::Error as IoError;
use std::{error, fmt, result};
use ::{CountSize, SizeLimit};
use byteorder::{ByteOrder};

pub use super::de::{
    Deserializer,
};

pub use super::ser::{
    Serializer,
};

use super::ser::SizeChecker;

use serde_crate as serde;

/// The result of a serialization or deserialization operation.
pub type Result<T> = result::Result<T, Error>;

/// An error that can be produced during (de)serializing.
///
/// If decoding from a Buffer, assume that the buffer has been left
/// in an invalid state.
pub type Error = Box<ErrorKind>;

/// The kind of error that can be produced during a serialization or deserialization.
#[derive(Debug)]
pub enum ErrorKind {
    /// If the error stems from the reader/writer that is being used
    /// during (de)serialization, that error will be stored and returned here.
    Io(IoError),
    /// If the bytes in the reader are not decodable because of an invalid
    /// encoding, this error will be returned.  This error is only possible
    /// if a stream is corrupted.  A stream produced from `encode` or `encode_into`
    /// should **never** produce an InvalidEncoding error.
    InvalidEncoding {
        #[allow(missing_docs)]
        desc: &'static str,
        #[allow(missing_docs)]
        detail: Option<String>
    },
    /// If (de)serializing a message takes more than the provided size limit, this
    /// error is returned.
    SizeLimit,
    /// Bincode can not encode sequences of unknown length (like iterators).
    SequenceMustHaveLength,
    /// A custom error message from Serde.
    Custom(String)
}

impl error::Error for ErrorKind {
    fn description(&self) -> &str {
        match *self {
            ErrorKind::Io(ref err) => error::Error::description(err),
            ErrorKind::InvalidEncoding{desc, ..} => desc,
            ErrorKind::SequenceMustHaveLength => "bincode can't encode infinite sequences",
            ErrorKind::SizeLimit => "the size limit for decoding has been reached",
            ErrorKind::Custom(ref msg) => msg,

        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ErrorKind::Io(ref err) => Some(err),
            ErrorKind::InvalidEncoding{..} => None,
            ErrorKind::SequenceMustHaveLength => None,
            ErrorKind::SizeLimit => None,
            ErrorKind::Custom(_) => None,
        }
    }
}

impl From<IoError> for Error {
    fn from(err: IoError) -> Error {
        ErrorKind::Io(err).into()
    }
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ErrorKind::Io(ref ioerr) =>
                write!(fmt, "Io: {}", ioerr),
            ErrorKind::InvalidEncoding{desc, detail: None}=>
                write!(fmt, "InvalidEncoding: {}", desc),
            ErrorKind::InvalidEncoding{desc, detail: Some(ref detail)}=>
                write!(fmt, "InvalidEncoding: {} ({})", desc, detail),
            ErrorKind::SequenceMustHaveLength =>
                write!(fmt, "Bincode can only encode sequences and maps that have a knowable size ahead of time."),
            ErrorKind::SizeLimit =>
                write!(fmt, "SizeLimit"),
            ErrorKind::Custom(ref s) =>
                s.fmt(fmt),
        }
    }
}

impl serde::de::Error for Error {
    fn custom<T: fmt::Display>(desc: T) -> Error {
        ErrorKind::Custom(desc.to_string()).into()
    }
}

impl serde::ser::Error for Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        ErrorKind::Custom(msg.to_string()).into()
    }
}

/// Serializes an object directly into a `Writer`.
///
/// If the serialization would take more bytes than allowed by `size_limit`, an error
/// is returned and *no bytes* will be written into the `Writer`.
///
/// If this returns an `Error` (other than SizeLimit), assume that the
/// writer is in an invalid state, as writing could bail out in the middle of
/// serializing.
pub fn serialize_into<W: ?Sized, T: ?Sized, S, E>(writer: &mut W, value: &T, size_limit: S) -> Result<()>
    where W: Write, T: serde::Serialize, S: SizeLimit, E: ByteOrder
{
    if let Some(limit) = size_limit.limit() {
        try!(serialized_size_bounded(value, limit).ok_or(ErrorKind::SizeLimit));
    }

    let mut serializer = Serializer::<_, E>::new(writer);
    serde::Serialize::serialize(value, &mut serializer)
}

/// Serializes a serializable object into a `Vec` of bytes.
///
/// If the serialization would take more bytes than allowed by `size_limit`,
/// an error is returned.
pub fn serialize<T: ?Sized, S, E>(value: &T, size_limit: S) -> Result<Vec<u8>>
    where T: serde::Serialize, S: SizeLimit, E: ByteOrder
{
    let mut writer = match size_limit.limit() {
        Some(size_limit) => {
            let actual_size = try!(serialized_size_bounded(value, size_limit).ok_or(ErrorKind::SizeLimit));
            Vec::with_capacity(actual_size as usize)
        }
        None => {
            let size = serialized_size(value) as usize;
            Vec::with_capacity(size)
        }
    };

    try!(serialize_into::<_, _, _, E>(&mut writer, value, super::Infinite));
    Ok(writer)
}

impl SizeLimit for CountSize {
    fn add(&mut self, c: u64) -> Result<()> {
        self.total += c;
        if let Some(limit) = self.limit {
            if self.total > limit {
                return Err(Box::new(ErrorKind::SizeLimit))
            }
        }
        Ok(())
    }

    fn limit(&self) -> Option<u64> {
        unreachable!();
    }
}

/// Returns the size that an object would be if serialized using bincode.
///
/// This is used internally as part of the check for encode_into, but it can
/// be useful for preallocating buffers if thats your style.
pub fn serialized_size<T: ?Sized>(value: &T) -> u64
    where T: serde::Serialize
{
    let mut size_counter = SizeChecker {
        size_limit: CountSize { total: 0, limit: None }
    };

    value.serialize(&mut size_counter).ok();
    size_counter.size_limit.total
}

/// Given a maximum size limit, check how large an object would be if it
/// were to be serialized.
///
/// If it can be serialized in `max` or fewer bytes, that number will be returned
/// inside `Some`.  If it goes over bounds, then None is returned.
pub fn serialized_size_bounded<T: ?Sized>(value: &T, max: u64) -> Option<u64>
    where T: serde::Serialize
{
    let mut size_counter = SizeChecker {
        size_limit: CountSize { total: 0, limit: Some(max) }
    };

    match value.serialize(&mut size_counter) {
        Ok(_) => Some(size_counter.size_limit.total),
        Err(_) => None,
    }
}

/// Deserializes an object directly from a `Buffer`ed Reader.
///
/// If the provided `SizeLimit` is reached, the deserialization will bail immediately.
/// A SizeLimit can help prevent an attacker from flooding your server with
/// a neverending stream of values that runs your server out of memory.
///
/// If this returns an `Error`, assume that the buffer that you passed
/// in is in an invalid state, as the error could be returned during any point
/// in the reading.
pub fn deserialize_from<R: ?Sized, T, S, E>(reader: &mut R, size_limit: S) -> Result<T>
    where R: Read, T: serde::de::DeserializeOwned, S: SizeLimit, E: ByteOrder
{
    let reader = ::de::read::IoReadReader::new(reader);
    let mut deserializer = Deserializer::<_, S, E>::new(reader, size_limit);
    serde::Deserialize::deserialize(&mut deserializer)
}

/// Deserializes a slice of bytes into an object.
///
/// This method does not have a size-limit because if you already have the bytes
/// in memory, then you don't gain anything by having a limiter.
pub fn deserialize<'a, T, E: ByteOrder>(bytes: &'a [u8]) -> Result<T>
    where T: serde::de::Deserialize<'a>,
{
    let reader = ::de::read::SliceReader::new(bytes);
    let mut deserializer = Deserializer::<_, _, E>::new(reader, super::Infinite);
    serde::Deserialize::deserialize(&mut deserializer)
}
<|endoftext|>


<commit_before>use super::types::{ShardAndCommittee, ValidatorRecord, ChainConfig};
use super::TransitionError;
use super::shuffle;

type DelegatedSlot = Vec<ShardAndCommittee>;
type DelegatedCycle = Vec<DelegatedSlot>;

/* Produce a vector of validators indicies where those validators start and end
 * dynasties are within the supplied `dynasty`.
*/
fn active_validator_indicies(
    dynasty: &u64,
    validators: &Vec<ValidatorRecord>)
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if (validator.start_dynasty >= *dynasty) &
                (validator.end_dynasty < *dynasty)
            {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}

/*
 * Delegates active validators into slots for a given cycle, given a random seed.
 * Returns a vector or ShardAndComitte vectors representing the shards and committiees for
 * each slot.
 * References get_new_shuffling (ethereum 2.1 specification)
 */
pub fn delegate_validators(
    seed: &[u8],
    validators: &Vec<ValidatorRecord>,
    dynasty: &u64,
    crosslinking_shard_start: &u16,
    config: &ChainConfig)
    -> Result<DelegatedCycle, TransitionError>
{
    let shuffled_validator_indices = {
        let mut validator_indices = active_validator_indicies(dynasty, validators);
        match shuffle(seed, validator_indices) {
            Ok(shuffled) => shuffled,
            _ => return Err(TransitionError::InvalidInput(
                    String::from("Shuffle list length exceed.")))
        }
    };
    let shard_indices = (0_usize..config.shard_count as usize).into_iter().collect();
    let crosslinking_shard_start = *crosslinking_shard_start as usize;
    let cycle_length = config.cycle_length as usize;
    let min_committee_size = config.min_committee_size as usize;
    generate_cycle(
        &shuffled_validator_indices,
        &shard_indices,
        &crosslinking_shard_start,
        &cycle_length,
        &min_committee_size)
}

/*
 * Given the validator list, delegates the validators into slots and comittees for a given cycle.
 */
fn generate_cycle(
    validator_indices: &Vec<usize>,
    shard_indices: &Vec<usize>,
    crosslinking_shard_start: &usize,
    cycle_length: &usize,
    min_committee_size: &usize)
    -> Result<DelegatedCycle, TransitionError>
{
    let validator_count = validator_indices.len();
    let shard_count = shard_indices.len();

    let (committees_per_slot, slots_per_committee) = {
        if validator_count >= cycle_length * min_committee_size {
            let committees_per_slot = validator_count / cycle_length / (min_committee_size * 2) + 1;
            let slots_per_committee = 1;
            (committees_per_slot, slots_per_committee)
        } else {
            let committees_per_slot = 1;
            let mut slots_per_committee = 1;
            while (validator_count * slots_per_committee < cycle_length * min_committee_size) &
                (slots_per_committee < *cycle_length) {
                slots_per_committee = slots_per_committee * 2;
            }
            (committees_per_slot, slots_per_committee)
        }
    };

    let cycle = validator_indices
        .chunks(validator_indices.len() / *cycle_length)
        .enumerate()
        .map(|(i, slot_indices)| {
            let shard_id_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee;
            return slot_indices
                .chunks(slot_indices.len() / committees_per_slot)
                .enumerate()
                .map(|(j, shard_indices)| {
                    return ShardAndCommittee{
                        shard_id: ((shard_id_start + j) % shard_count) as u16,
                        committee: shard_indices.to_vec(),
                    }
                })
                .collect()
        })
        .collect();
    Ok(cycle)
}

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

    fn generate_cycle_helper(
        validator_count: &usize,
        shard_count: &usize,
        crosslinking_shard_start: &usize,
        cycle_length: &usize,
        min_committee_size: &usize)
        -> (Vec<usize>, Vec<usize>, Result<DelegatedCycle, TransitionError>)
    {
        let validator_indices = (0_usize..*validator_count).into_iter().collect();
        let shard_indices = (0_usize..*shard_count).into_iter().collect();
        let result = generate_cycle(
            &validator_indices,
            &shard_indices,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        (validator_indices, shard_indices, result)
    }

    #[allow(dead_code)]
    fn print_cycle(cycle: &DelegatedCycle) {
        cycle.iter()
            .enumerate()
            .for_each(|(i, slot)| {
                println!("slot {:?}", &i);
                slot.iter()
                    .enumerate()
                    .for_each(|(i, sac)| {
                        println!("#{:?}\tshard_id={}\tcommittee.len()={}",
                            &i, &sac.shard_id, &sac.committee.len())
                    })
            });
    }

    fn flatten_validators(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                for validator in sac.committee.iter() {
                    flattened.push(*validator);
                }
            }
        }
        flattened
    }

    fn flatten_and_dedup_shards(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                flattened.push(sac.shard_id as usize);
            }
        }
        flattened.dedup();
        flattened
    }

    fn flatten_shards_in_slots(cycle: &DelegatedCycle)
        -> Vec<Vec<usize>>
    {
        let mut shards_in_slots: Vec<Vec<usize>> = vec![];
        for slot in cycle.iter() {
            let mut shards: Vec<usize> = vec![];
            for sac in slot.iter() {
                shards.push(sac.shard_id as usize);
            }
            shards_in_slots.push(shards);
        }
        shards_in_slots
    }

    #[test]
    fn test_generate_cycle() {
        let validator_count: usize = 100;
        let shard_count: usize = 10;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 20;
        let min_committee_size: usize = 10;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        let cycle = result.unwrap();

        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, shards, "Shard assignment incorrect");

        let expected_shards_in_slots: Vec<Vec<usize>> = vec![
            vec![0], vec![0],   // Each line is 2 slots..
            vec![1], vec![1],
            vec![2], vec![2],
            vec![3], vec![3],
            vec![4], vec![4],
            vec![5], vec![5],
            vec![6], vec![6],
            vec![7], vec![7],
            vec![8], vec![8],
            vec![9], vec![9],
        ];
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }
}
<commit_msg>Correct get_new_shuffling. Closes #25<commit_after>use super::types::{ShardAndCommittee, ValidatorRecord, ChainConfig};
use super::TransitionError;
use super::shuffle;
use std::cmp::min;

type DelegatedSlot = Vec<ShardAndCommittee>;
type DelegatedCycle = Vec<DelegatedSlot>;

/* Produce a vector of validators indicies where those validators start and end
 * dynasties are within the supplied `dynasty`.
*/
fn active_validator_indicies(
    dynasty: &u64,
    validators: &Vec<ValidatorRecord>)
    -> Vec<usize>
{
    validators.iter()
        .enumerate()
        .filter_map(|(i, validator)| {
            if (validator.start_dynasty >= *dynasty) &
                (validator.end_dynasty < *dynasty)
            {
                Some(i)
            } else {
                None
            }
        })
        .collect()
}

/*
 * Delegates active validators into slots for a given cycle, given a random seed.
 * Returns a vector or ShardAndComitte vectors representing the shards and committiees for
 * each slot.
 * References get_new_shuffling (ethereum 2.1 specification)
 */
pub fn delegate_validators(
    seed: &[u8],
    validators: &Vec<ValidatorRecord>,
    dynasty: &u64,
    crosslinking_shard_start: &u16,
    config: &ChainConfig)
    -> Result<DelegatedCycle, TransitionError>
{
    let shuffled_validator_indices = {
        let mut validator_indices = active_validator_indicies(dynasty, validators);
        match shuffle(seed, validator_indices) {
            Ok(shuffled) => shuffled,
            _ => return Err(TransitionError::InvalidInput(
                    String::from("Shuffle list length exceed.")))
        }
    };
    let shard_indices = (0_usize..config.shard_count as usize).into_iter().collect();
    let crosslinking_shard_start = *crosslinking_shard_start as usize;
    let cycle_length = config.cycle_length as usize;
    let min_committee_size = config.min_committee_size as usize;
    generate_cycle(
        &shuffled_validator_indices,
        &shard_indices,
        &crosslinking_shard_start,
        &cycle_length,
        &min_committee_size)
}

/*
 * Given the validator list, delegates the validators into slots and comittees for a given cycle.
 */
fn generate_cycle(
    validator_indices: &Vec<usize>,
    shard_indices: &Vec<usize>,
    crosslinking_shard_start: &usize,
    cycle_length: &usize,
    min_committee_size: &usize)
    -> Result<DelegatedCycle, TransitionError>
{
    let validator_count = validator_indices.len();
    let shard_count = shard_indices.len();

    let (committees_per_slot, slots_per_committee) = {
        if validator_count >= cycle_length * min_committee_size {
            let committees_per_slot = min(validator_count / cycle_length /
			    (min_committee_size * 2) + 1, shard_count /
			    cycle_length);
            let slots_per_committee = 1;
            (committees_per_slot, slots_per_committee)
        } else {
            let committees_per_slot = 1;
            let mut slots_per_committee = 1;
            while (validator_count * slots_per_committee < cycle_length * min_committee_size) &
                (slots_per_committee < *cycle_length) {
                slots_per_committee = slots_per_committee * 2;
            }
            (committees_per_slot, slots_per_committee)
        }
    };

    let cycle = validator_indices
        .chunks(validator_indices.len() / *cycle_length)
        .enumerate()
        .map(|(i, slot_indices)| {
            let shard_id_start = crosslinking_shard_start + i * committees_per_slot / slots_per_committee;
            return slot_indices
                .chunks(slot_indices.len() / committees_per_slot)
                .enumerate()
                .map(|(j, shard_indices)| {
                    return ShardAndCommittee{
                        shard_id: ((shard_id_start + j) % shard_count) as u16,
                        committee: shard_indices.to_vec(),
                    }
                })
                .collect()
        })
        .collect();
    Ok(cycle)
}

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

    fn generate_cycle_helper(
        validator_count: &usize,
        shard_count: &usize,
        crosslinking_shard_start: &usize,
        cycle_length: &usize,
        min_committee_size: &usize)
        -> (Vec<usize>, Vec<usize>, Result<DelegatedCycle, TransitionError>)
    {
        let validator_indices = (0_usize..*validator_count).into_iter().collect();
        let shard_indices = (0_usize..*shard_count).into_iter().collect();
        let result = generate_cycle(
            &validator_indices,
            &shard_indices,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        (validator_indices, shard_indices, result)
    }

    #[allow(dead_code)]
    fn print_cycle(cycle: &DelegatedCycle) {
        cycle.iter()
            .enumerate()
            .for_each(|(i, slot)| {
                println!("slot {:?}", &i);
                slot.iter()
                    .enumerate()
                    .for_each(|(i, sac)| {
                        println!("#{:?}\tshard_id={}\tcommittee.len()={}",
                            &i, &sac.shard_id, &sac.committee.len())
                    })
            });
    }

    fn flatten_validators(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                for validator in sac.committee.iter() {
                    flattened.push(*validator);
                }
            }
        }
        flattened
    }

    fn flatten_and_dedup_shards(cycle: &DelegatedCycle)
        -> Vec<usize>
    {
        let mut flattened = vec![];
        for slot in cycle.iter() {
            for sac in slot.iter() {
                flattened.push(sac.shard_id as usize);
            }
        }
        flattened.dedup();
        flattened
    }

    fn flatten_shards_in_slots(cycle: &DelegatedCycle)
        -> Vec<Vec<usize>>
    {
        let mut shards_in_slots: Vec<Vec<usize>> = vec![];
        for slot in cycle.iter() {
            let mut shards: Vec<usize> = vec![];
            for sac in slot.iter() {
                shards.push(sac.shard_id as usize);
            }
            shards_in_slots.push(shards);
        }
        shards_in_slots
    }

    #[test]
    fn test_generate_cycle() {
        let validator_count: usize = 100;
        let shard_count: usize = 10;
        let crosslinking_shard_start: usize = 0;
        let cycle_length: usize = 20;
        let min_committee_size: usize = 10;
        let (validators, shards, result) = generate_cycle_helper(
            &validator_count,
            &shard_count,
            &crosslinking_shard_start,
            &cycle_length,
            &min_committee_size);
        let cycle = result.unwrap();

        let assigned_validators = flatten_validators(&cycle);
        let assigned_shards = flatten_and_dedup_shards(&cycle);
        let shards_in_slots = flatten_shards_in_slots(&cycle);
        assert_eq!(assigned_validators, validators, "Validator assignment incorrect");
        assert_eq!(assigned_shards, shards, "Shard assignment incorrect");

        let expected_shards_in_slots: Vec<Vec<usize>> = vec![
            vec![0], vec![0],   // Each line is 2 slots..
            vec![1], vec![1],
            vec![2], vec![2],
            vec![3], vec![3],
            vec![4], vec![4],
            vec![5], vec![5],
            vec![6], vec![6],
            vec![7], vec![7],
            vec![8], vec![8],
            vec![9], vec![9],
        ];
        // assert!(compare_shards_in_slots(&cycle, &expected_shards_in_slots));
        assert_eq!(expected_shards_in_slots, shards_in_slots, "Shard assignment incorrect.")
    }
}
<|endoftext|>


<commit_before>use cargo_test_support::{
    clippy_is_available, is_coarse_mtime, process, project, registry::Package, sleep_ms,
};
use std::path::Path;

fn as_str(bytes: &[u8]) -> &str {
    std::str::from_utf8(bytes).expect("valid utf-8")
}

#[cargo_test]
fn simple() {
    // A simple example that generates two warnings (unused functions).
    let p = project()
        .file(
            "src/lib.rs",
            "
            fn a() {}
            fn b() {}
            ",
        )
        .build();

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();

    // Capture what rustc actually emits. This is done to avoid relying on the
    // exact message formatting in rustc.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s])
        .exec_with_output()
        .expect("rustc to run");

    assert!(rustc_output.stdout.is_empty());
    assert!(rustc_output.status.success());

    // -q so the output is the same as rustc (no "Compiling" or "Finished").
    let cargo_output1 = p
        .cargo("check -q --color=never")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output1.stderr));
    assert!(cargo_output1.stdout.is_empty());
    // Check that the cached version is exactly the same.
    let cargo_output2 = p
        .cargo("check -q")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output2.stderr));
    assert!(cargo_output2.stdout.is_empty());
}

// same as `simple`, except everything is using the short format
#[cargo_test]
fn simple_short() {
    let p = project()
        .file(
            "src/lib.rs",
            "
                fn a() {}
                fn b() {}
            ",
        )
        .build();

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();

    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s, "--error-format=short"])
        .exec_with_output()
        .expect("rustc to run");

    assert!(rustc_output.stdout.is_empty());
    assert!(rustc_output.status.success());

    let cargo_output1 = p
        .cargo("check -q --color=never --message-format=short")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output1.stderr));
    // assert!(cargo_output1.stdout.is_empty());
    let cargo_output2 = p
        .cargo("check -q --message-format=short")
        .exec_with_output()
        .expect("cargo to run");
    println!("{}", String::from_utf8_lossy(&cargo_output2.stdout));
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output2.stderr));
    assert!(cargo_output2.stdout.is_empty());
}

#[cargo_test]
fn color() {
    // Check enabling/disabling color.
    let p = project().file("src/lib.rs", "fn a() {}").build();

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();
    // Capture the original color output.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s, "--color=always"])
        .exec_with_output()
        .expect("rustc to run");
    assert!(rustc_output.status.success());
    let rustc_color = as_str(&rustc_output.stderr);
    assert!(rustc_color.contains("\x1b["));

    // Capture the original non-color output.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s])
        .exec_with_output()
        .expect("rustc to run");
    let rustc_nocolor = as_str(&rustc_output.stderr);
    assert!(!rustc_nocolor.contains("\x1b["));

    // First pass, non-cached, with color, should be the same.
    let cargo_output1 = p
        .cargo("check -q --color=always")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(rustc_color, as_str(&cargo_output1.stderr));

    // Replay cached, with color.
    let cargo_output2 = p
        .cargo("check -q --color=always")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(rustc_color, as_str(&cargo_output2.stderr));

    // Replay cached, no color.
    let cargo_output_nocolor = p
        .cargo("check -q --color=never")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(rustc_nocolor, as_str(&cargo_output_nocolor.stderr));
}

#[cargo_test]
fn cached_as_json() {
    // Check that cached JSON output is the same.
    let p = project().file("src/lib.rs", "fn a() {}").build();

    // Grab the non-cached output, feature disabled.
    // NOTE: When stabilizing, this will need to be redone.
    let cargo_output = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    assert!(cargo_output.status.success());
    let orig_cargo_out = as_str(&cargo_output.stdout);
    assert!(orig_cargo_out.contains("compiler-message"));
    p.cargo("clean").run();

    // Check JSON output, not fresh.
    let cargo_output1 = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&cargo_output1.stdout), orig_cargo_out);

    // Check JSON output, fresh.
    let cargo_output2 = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    // The only difference should be this field.
    let fix_fresh = as_str(&cargo_output2.stdout).replace("\"fresh\":true", "\"fresh\":false");
    assert_eq!(fix_fresh, orig_cargo_out);
}

#[cargo_test]
fn clears_cache_after_fix() {
    // Make sure the cache is invalidated when there is no output.
    let p = project().file("src/lib.rs", "fn asdf() {}").build();
    // Fill the cache.
    p.cargo("check").with_stderr_contains("[..]asdf[..]").run();
    let cpath = p
        .glob("target/debug/.fingerprint/foo-*/output")
        .next()
        .unwrap()
        .unwrap();
    assert!(std::fs::read_to_string(cpath).unwrap().contains("asdf"));

    // Fix it.
    if is_coarse_mtime() {
        sleep_ms(1000);
    }
    p.change_file("src/lib.rs", "");

    p.cargo("check")
        .with_stdout("")
        .with_stderr(
            "\
[CHECKING] foo [..]
[FINISHED] [..]
",
        )
        .run();
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 0);

    // And again, check the cache is correct.
    p.cargo("check")
        .with_stdout("")
        .with_stderr(
            "\
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn rustdoc() {
    // Create a warning in rustdoc.
    let p = project()
        .file(
            "src/lib.rs",
            "
            #![warn(private_doc_tests)]
            /// asdf
            /// ```
            /// let x = 1;
            /// ```
            fn f() {}
            ",
        )
        .build();

    let rustdoc_output = p
        .cargo("doc -q --color=always")
        .exec_with_output()
        .expect("rustdoc to run");
    assert!(rustdoc_output.status.success());
    let rustdoc_stderr = as_str(&rustdoc_output.stderr);
    assert!(rustdoc_stderr.contains("private"));
    assert!(rustdoc_stderr.contains("\x1b["));
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 1);

    // Check the cached output.
    let rustdoc_output = p
        .cargo("doc -q --color=always")
        .exec_with_output()
        .expect("rustdoc to run");
    assert_eq!(as_str(&rustdoc_output.stderr), rustdoc_stderr);
}

#[cargo_test]
fn fix() {
    // Make sure `fix` is not broken by caching.
    let p = project().file("src/lib.rs", "pub fn try() {}").build();

    p.cargo("fix --edition --allow-no-vcs").run();

    assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}");
}

#[cargo_test]
fn clippy() {
    if !clippy_is_available() {
        return;
    }

    // Caching clippy output.
    // This is just a random clippy lint (assertions_on_constants) that
    // hopefully won't change much in the future.
    let p = project()
        .file(
            "src/lib.rs",
            "pub fn f() { assert!(true); }\n\
             fn unused_func() {}",
        )
        .build();

    p.cargo("clippy-preview -Zunstable-options -v")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[RUNNING] `clippy[..]")
        .with_stderr_contains("[..]assert!(true)[..]")
        .run();

    // `check` should be separate from clippy.
    p.cargo("check -v")
        .with_stderr_contains(
            "\
[CHECKING] foo [..]
[RUNNING] `rustc[..]
[WARNING] [..]unused_func[..]
",
        )
        .with_stderr_does_not_contain("[..]assert!(true)[..]")
        .run();

    // Again, reading from the cache.
    p.cargo("clippy-preview -Zunstable-options -v")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[FRESH] foo [..]")
        .with_stderr_contains("[..]assert!(true)[..]")
        .run();

    // And `check` should also be fresh, reading from cache.
    p.cargo("check -v")
        .with_stderr_contains("[FRESH] foo [..]")
        .with_stderr_contains("[WARNING] [..]unused_func[..]")
        .with_stderr_does_not_contain("[..]assert!(true)[..]")
        .run();
}

#[cargo_test]
fn very_verbose() {
    // Handle cap-lints in dependencies.
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "fn not_used() {}")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [dependencies]
            bar = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("check -vv")
        .with_stderr_contains("[..]not_used[..]")
        .run();

    p.cargo("check").with_stderr("[FINISHED] [..]").run();

    p.cargo("check -vv")
        .with_stderr_contains("[..]not_used[..]")
        .run();
}

#[cargo_test]
fn doesnt_create_extra_files() {
    // Ensure it doesn't create `output` files when not needed.
    Package::new("dep", "1.0.0")
        .file("src/lib.rs", "fn unused() {}")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [dependencies]
                dep = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build").run();

    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 0);
    assert_eq!(p.glob("target/debug/.fingerprint/dep-*/output").count(), 0);
    if is_coarse_mtime() {
        sleep_ms(1000);
    }
    p.change_file("src/lib.rs", "fn unused() {}");
    p.cargo("build").run();
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 1);
}
<commit_msg>Auto merge of #7607 - ehuss:fwdansi-hack, r=alexcrichton<commit_after>use cargo_test_support::{
    clippy_is_available, is_coarse_mtime, process, project, registry::Package, sleep_ms,
};
use std::path::Path;

fn as_str(bytes: &[u8]) -> &str {
    std::str::from_utf8(bytes).expect("valid utf-8")
}

#[cargo_test]
fn simple() {
    // A simple example that generates two warnings (unused functions).
    let p = project()
        .file(
            "src/lib.rs",
            "
            fn a() {}
            fn b() {}
            ",
        )
        .build();

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();

    // Capture what rustc actually emits. This is done to avoid relying on the
    // exact message formatting in rustc.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s])
        .exec_with_output()
        .expect("rustc to run");

    assert!(rustc_output.stdout.is_empty());
    assert!(rustc_output.status.success());

    // -q so the output is the same as rustc (no "Compiling" or "Finished").
    let cargo_output1 = p
        .cargo("check -q --color=never")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output1.stderr));
    assert!(cargo_output1.stdout.is_empty());
    // Check that the cached version is exactly the same.
    let cargo_output2 = p
        .cargo("check -q")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output2.stderr));
    assert!(cargo_output2.stdout.is_empty());
}

// same as `simple`, except everything is using the short format
#[cargo_test]
fn simple_short() {
    let p = project()
        .file(
            "src/lib.rs",
            "
                fn a() {}
                fn b() {}
            ",
        )
        .build();

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();

    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s, "--error-format=short"])
        .exec_with_output()
        .expect("rustc to run");

    assert!(rustc_output.stdout.is_empty());
    assert!(rustc_output.status.success());

    let cargo_output1 = p
        .cargo("check -q --color=never --message-format=short")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output1.stderr));
    // assert!(cargo_output1.stdout.is_empty());
    let cargo_output2 = p
        .cargo("check -q --message-format=short")
        .exec_with_output()
        .expect("cargo to run");
    println!("{}", String::from_utf8_lossy(&cargo_output2.stdout));
    assert_eq!(as_str(&rustc_output.stderr), as_str(&cargo_output2.stderr));
    assert!(cargo_output2.stdout.is_empty());
}

#[cargo_test]
fn color() {
    // Check enabling/disabling color.
    let p = project().file("src/lib.rs", "fn a() {}").build();

    // Hack for issue in fwdansi 1.1. It is squashing multiple resets
    // into a single reset.
    // https://github.com/kennytm/fwdansi/issues/2
    fn normalize(s: &str) -> String {
        #[cfg(windows)]
        return s.replace("\x1b[0m\x1b[0m", "\x1b[0m");
        #[cfg(not(windows))]
        return s.to_string();
    };

    let compare = |a, b| {
        assert_eq!(normalize(a), normalize(b));
    };

    let agnostic_path = Path::new("src").join("lib.rs");
    let agnostic_path_s = agnostic_path.to_str().unwrap();
    // Capture the original color output.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s, "--color=always"])
        .exec_with_output()
        .expect("rustc to run");
    assert!(rustc_output.status.success());
    let rustc_color = as_str(&rustc_output.stderr);
    assert!(rustc_color.contains("\x1b["));

    // Capture the original non-color output.
    let rustc_output = process("rustc")
        .cwd(p.root())
        .args(&["--crate-type=lib", agnostic_path_s])
        .exec_with_output()
        .expect("rustc to run");
    let rustc_nocolor = as_str(&rustc_output.stderr);
    assert!(!rustc_nocolor.contains("\x1b["));

    // First pass, non-cached, with color, should be the same.
    let cargo_output1 = p
        .cargo("check -q --color=always")
        .exec_with_output()
        .expect("cargo to run");
    compare(rustc_color, as_str(&cargo_output1.stderr));

    // Replay cached, with color.
    let cargo_output2 = p
        .cargo("check -q --color=always")
        .exec_with_output()
        .expect("cargo to run");
    compare(rustc_color, as_str(&cargo_output2.stderr));

    // Replay cached, no color.
    let cargo_output_nocolor = p
        .cargo("check -q --color=never")
        .exec_with_output()
        .expect("cargo to run");
    compare(rustc_nocolor, as_str(&cargo_output_nocolor.stderr));
}

#[cargo_test]
fn cached_as_json() {
    // Check that cached JSON output is the same.
    let p = project().file("src/lib.rs", "fn a() {}").build();

    // Grab the non-cached output, feature disabled.
    // NOTE: When stabilizing, this will need to be redone.
    let cargo_output = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    assert!(cargo_output.status.success());
    let orig_cargo_out = as_str(&cargo_output.stdout);
    assert!(orig_cargo_out.contains("compiler-message"));
    p.cargo("clean").run();

    // Check JSON output, not fresh.
    let cargo_output1 = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    assert_eq!(as_str(&cargo_output1.stdout), orig_cargo_out);

    // Check JSON output, fresh.
    let cargo_output2 = p
        .cargo("check --message-format=json")
        .exec_with_output()
        .expect("cargo to run");
    // The only difference should be this field.
    let fix_fresh = as_str(&cargo_output2.stdout).replace("\"fresh\":true", "\"fresh\":false");
    assert_eq!(fix_fresh, orig_cargo_out);
}

#[cargo_test]
fn clears_cache_after_fix() {
    // Make sure the cache is invalidated when there is no output.
    let p = project().file("src/lib.rs", "fn asdf() {}").build();
    // Fill the cache.
    p.cargo("check").with_stderr_contains("[..]asdf[..]").run();
    let cpath = p
        .glob("target/debug/.fingerprint/foo-*/output")
        .next()
        .unwrap()
        .unwrap();
    assert!(std::fs::read_to_string(cpath).unwrap().contains("asdf"));

    // Fix it.
    if is_coarse_mtime() {
        sleep_ms(1000);
    }
    p.change_file("src/lib.rs", "");

    p.cargo("check")
        .with_stdout("")
        .with_stderr(
            "\
[CHECKING] foo [..]
[FINISHED] [..]
",
        )
        .run();
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 0);

    // And again, check the cache is correct.
    p.cargo("check")
        .with_stdout("")
        .with_stderr(
            "\
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn rustdoc() {
    // Create a warning in rustdoc.
    let p = project()
        .file(
            "src/lib.rs",
            "
            #![warn(private_doc_tests)]
            /// asdf
            /// ```
            /// let x = 1;
            /// ```
            fn f() {}
            ",
        )
        .build();

    let rustdoc_output = p
        .cargo("doc -q --color=always")
        .exec_with_output()
        .expect("rustdoc to run");
    assert!(rustdoc_output.status.success());
    let rustdoc_stderr = as_str(&rustdoc_output.stderr);
    assert!(rustdoc_stderr.contains("private"));
    assert!(rustdoc_stderr.contains("\x1b["));
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 1);

    // Check the cached output.
    let rustdoc_output = p
        .cargo("doc -q --color=always")
        .exec_with_output()
        .expect("rustdoc to run");
    assert_eq!(as_str(&rustdoc_output.stderr), rustdoc_stderr);
}

#[cargo_test]
fn fix() {
    // Make sure `fix` is not broken by caching.
    let p = project().file("src/lib.rs", "pub fn try() {}").build();

    p.cargo("fix --edition --allow-no-vcs").run();

    assert_eq!(p.read_file("src/lib.rs"), "pub fn r#try() {}");
}

#[cargo_test]
fn clippy() {
    if !clippy_is_available() {
        return;
    }

    // Caching clippy output.
    // This is just a random clippy lint (assertions_on_constants) that
    // hopefully won't change much in the future.
    let p = project()
        .file(
            "src/lib.rs",
            "pub fn f() { assert!(true); }\n\
             fn unused_func() {}",
        )
        .build();

    p.cargo("clippy-preview -Zunstable-options -v")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[RUNNING] `clippy[..]")
        .with_stderr_contains("[..]assert!(true)[..]")
        .run();

    // `check` should be separate from clippy.
    p.cargo("check -v")
        .with_stderr_contains(
            "\
[CHECKING] foo [..]
[RUNNING] `rustc[..]
[WARNING] [..]unused_func[..]
",
        )
        .with_stderr_does_not_contain("[..]assert!(true)[..]")
        .run();

    // Again, reading from the cache.
    p.cargo("clippy-preview -Zunstable-options -v")
        .masquerade_as_nightly_cargo()
        .with_stderr_contains("[FRESH] foo [..]")
        .with_stderr_contains("[..]assert!(true)[..]")
        .run();

    // And `check` should also be fresh, reading from cache.
    p.cargo("check -v")
        .with_stderr_contains("[FRESH] foo [..]")
        .with_stderr_contains("[WARNING] [..]unused_func[..]")
        .with_stderr_does_not_contain("[..]assert!(true)[..]")
        .run();
}

#[cargo_test]
fn very_verbose() {
    // Handle cap-lints in dependencies.
    Package::new("bar", "1.0.0")
        .file("src/lib.rs", "fn not_used() {}")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [dependencies]
            bar = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("check -vv")
        .with_stderr_contains("[..]not_used[..]")
        .run();

    p.cargo("check").with_stderr("[FINISHED] [..]").run();

    p.cargo("check -vv")
        .with_stderr_contains("[..]not_used[..]")
        .run();
}

#[cargo_test]
fn doesnt_create_extra_files() {
    // Ensure it doesn't create `output` files when not needed.
    Package::new("dep", "1.0.0")
        .file("src/lib.rs", "fn unused() {}")
        .publish();

    let p = project()
        .file(
            "Cargo.toml",
            r#"
                [package]
                name = "foo"
                version = "0.1.0"

                [dependencies]
                dep = "1.0"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build").run();

    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 0);
    assert_eq!(p.glob("target/debug/.fingerprint/dep-*/output").count(), 0);
    if is_coarse_mtime() {
        sleep_ms(1000);
    }
    p.change_file("src/lib.rs", "fn unused() {}");
    p.cargo("build").run();
    assert_eq!(p.glob("target/debug/.fingerprint/foo-*/output").count(), 1);
}
<|endoftext|>


<commit_before>use super::Hash256;
use super::attestation_record::AttestationRecord;

pub struct ActiveState {
    pub pending_attestations: Vec<AttestationRecord>,
    pub recent_block_hashes: Vec<Hash256>,
}

impl ActiveState {
    /// Returns a new instance where all fields are empty vectors.
    pub fn zero() -> Self {
        Self {
            pending_attestations: vec![],
            recent_block_hashes: vec![],
        }
    }
}

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

    #[test]
    fn test_act_state_zero() {
        let a = ActiveState::zero();
        assert_eq!(a.pending_attestations.len(), 0);
        assert_eq!(a.recent_block_hashes.len(), 0);
    }
}
<commit_msg>Update `ActiveState` as per new spec<commit_after>use super::Hash256;
use super::{
    AttestationRecord,
    SpecialRecord,
};

pub struct ActiveState {
    pub pending_attestations: Vec<AttestationRecord>,
    pub pending_specials: Vec<SpecialRecord>,
    pub recent_block_hashes: Vec<Hash256>,
    pub randao_mix: Hash256,
}
<|endoftext|>