<commit_before>use super::{Tm, ParseError, NSEC_PER_SEC};
use super::ParseError::*;

/// Parses the time from the string according to the format string.
pub fn strptime(mut s: &str, format: &str) -> Result<Tm, ParseError> {
    let mut tm = Tm {
        tm_sec: 0,
        tm_min: 0,
        tm_hour: 0,
        tm_mday: 0,
        tm_mon: 0,
        tm_year: 0,
        tm_wday: 0,
        tm_yday: 0,
        tm_isdst: 0,
        tm_utcoff: 0,
        tm_nsec: 0,
    };
    let mut chars = format.chars();

    while let Some(ch) = chars.next() {
        if ch == '%' {
            if let Some(ch) = chars.next() {
                try!(parse_type(&mut s, ch, &mut tm));
            }
        } else {
            try!(parse_char(&mut s, ch));
        }
    }

    Ok(tm)
}

fn parse_type(s: &mut &str, ch: char, tm: &mut Tm) -> Result<(), ParseError> {
    match ch {
        'A' => match match_strs(s, &[("Sunday", 0),
                                     ("Monday", 1),
                                     ("Tuesday", 2),
                                     ("Wednesday", 3),
                                     ("Thursday", 4),
                                     ("Friday", 5),
                                     ("Saturday", 6)]) {
            Some(v) => { tm.tm_wday = v; Ok(()) }
            None => Err(ParseError::InvalidDay)
        },
        'a' => match match_strs(s, &[("Sun", 0),
                                     ("Mon", 1),
                                     ("Tue", 2),
                                     ("Wed", 3),
                                     ("Thu", 4),
                                     ("Fri", 5),
                                     ("Sat", 6)]) {
            Some(v) => { tm.tm_wday = v; Ok(()) }
            None => Err(ParseError::InvalidDay)
        },
        'B' => match match_strs(s, &[("January", 0),
                                     ("February", 1),
                                     ("March", 2),
                                     ("April", 3),
                                     ("May", 4),
                                     ("June", 5),
                                     ("July", 6),
                                     ("August", 7),
                                     ("September", 8),
                                     ("October", 9),
                                     ("November", 10),
                                     ("December", 11)]) {
            Some(v) => { tm.tm_mon = v; Ok(()) }
            None => Err(ParseError::InvalidMonth)
        },
        'b' | 'h' => match match_strs(s, &[("Jan", 0),
                                           ("Feb", 1),
                                           ("Mar", 2),
                                           ("Apr", 3),
                                           ("May", 4),
                                           ("Jun", 5),
                                           ("Jul", 6),
                                           ("Aug", 7),
                                           ("Sep", 8),
                                           ("Oct", 9),
                                           ("Nov", 10),
                                           ("Dec", 11)]) {
            Some(v) => { tm.tm_mon = v; Ok(()) }
            None => Err(ParseError::InvalidMonth)
        },
        'C' => match match_digits_in_range(s, 1, 2, false, 0, 99) {
            Some(v) => { tm.tm_year += (v * 100) - 1900; Ok(()) }
            None => Err(ParseError::InvalidYear)
        },
        'c' => {
            parse_type(s, 'a', tm)
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'b', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'e', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'T', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'Y', tm))
        }
        'D' | 'x' => {
            parse_type(s, 'm', tm)
                .and_then(|()| parse_char(s, '/'))
                .and_then(|()| parse_type(s, 'd', tm))
                .and_then(|()| parse_char(s, '/'))
                .and_then(|()| parse_type(s, 'y', tm))
        }
        'd' => match match_digits_in_range(s, 1, 2, false, 1, 31) {
            Some(v) => { tm.tm_mday = v; Ok(()) }
            None => Err(ParseError::InvalidDayOfMonth)
        },
        'e' => match match_digits_in_range(s, 1, 2, true, 1, 31) {
            Some(v) => { tm.tm_mday = v; Ok(()) }
            None => Err(ParseError::InvalidDayOfMonth)
        },
        'f' => {
            tm.tm_nsec = match_fractional_seconds(s);
            Ok(())
        }
        'F' => {
            parse_type(s, 'Y', tm)
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'm', tm))
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'd', tm))
        }
        'H' => {
            match match_digits_in_range(s, 1, 2, false, 0, 23) {
                Some(v) => { tm.tm_hour = v; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'I' => {
            match match_digits_in_range(s, 1, 2, false, 1, 12) {
                Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'j' => {
            match match_digits_in_range(s, 1, 3, false, 1, 366) {
                Some(v) => { tm.tm_yday = v - 1; Ok(()) }
                None => Err(ParseError::InvalidDayOfYear)
            }
        }
        'k' => {
            match match_digits_in_range(s, 1, 2, true, 0, 23) {
                Some(v) => { tm.tm_hour = v; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'l' => {
            match match_digits_in_range(s, 1, 2, true, 1, 12) {
                Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'M' => {
            match match_digits_in_range(s, 1, 2, false, 0, 59) {
                Some(v) => { tm.tm_min = v; Ok(()) }
                None => Err(ParseError::InvalidMinute)
            }
        }
        'm' => {
            match match_digits_in_range(s, 1, 2, false, 1, 12) {
                Some(v) => { tm.tm_mon = v - 1; Ok(()) }
                None => Err(ParseError::InvalidMonth)
            }
        }
        'n' => parse_char(s, '\n'),
        'P' => match match_strs(s, &[("am", 0), ("pm", 12)]) {
            Some(v) => { tm.tm_hour += v; Ok(()) }
            None => Err(ParseError::InvalidHour)
        },
        'p' => match match_strs(s, &[("AM", 0), ("PM", 12)]) {
            Some(v) => { tm.tm_hour += v; Ok(()) }
            None => Err(ParseError::InvalidHour)
        },
        'R' => {
            parse_type(s, 'H', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
        }
        'r' => {
            parse_type(s, 'I', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'S', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'p', tm))
        }
        'S' => {
            match match_digits_in_range(s, 1, 2, false, 0, 60) {
                Some(v) => { tm.tm_sec = v; Ok(()) }
                None => Err(ParseError::InvalidSecond)
            }
        }
        //'s' {}
        'T' | 'X' => {
            parse_type(s, 'H', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'S', tm))
        }
        't' => parse_char(s, '\t'),
        'u' => {
            match match_digits_in_range(s, 1, 1, false, 1, 7) {
                Some(v) => { tm.tm_wday = if v == 7 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidDayOfWeek)
            }
        }
        'v' => {
            parse_type(s, 'e', tm)
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'b', tm))
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'Y', tm))
        }
        //'W' {}
        'w' => {
            match match_digits_in_range(s, 1, 1, false, 0, 6) {
                Some(v) => { tm.tm_wday = v; Ok(()) }
                None => Err(ParseError::InvalidDayOfWeek)
            }
        }
        'Y' => {
            match match_digits(s, 4, 4, false) {
                Some(v) => { tm.tm_year = v - 1900; Ok(()) }
                None => Err(ParseError::InvalidYear)
            }
        }
        'y' => {
            match match_digits_in_range(s, 1, 2, false, 0, 99) {
                Some(v) => { tm.tm_year = v; Ok(()) }
                None => Err(ParseError::InvalidYear)
            }
        }
        'Z' => {
            if match_str(s, "UTC") || match_str(s, "GMT") {
                tm.tm_utcoff = 0;
                Ok(())
            } else {
                // It's odd, but to maintain compatibility with c's
                // strptime we ignore the timezone.
                for (i, ch) in s.char_indices() {
                    if ch == ' ' {
                        *s = &s[i..];
                        return Ok(())
                    }
                }
                *s = "";
                Ok(())
            }
        }
        'z' => {
            let sign = if parse_char(s, '+').is_ok() {1}
                       else if parse_char(s, '-').is_ok() {-1}
                       else { return Err(ParseError::InvalidZoneOffset) };

            let mut hours = 0;
            let mut minutes = 0;

            match match_digits(s, 2, 2, false) {
                Some(h) => hours = h,
                None => return Err(ParseError::InvalidZoneOffset)
            }

            // consume the colon if its present,
            // just ignore it otherwise
            parse_char(s, ':');

            match match_digits(s, 2, 2, false) {
                Some(m) => minutes = m,
                None => return Err(ParseError::InvalidZoneOffset)
            }

            tm.tm_utcoff = sign * (hours * 60 * 60 + minutes * 60);
            Ok(())
        }
        '%' => parse_char(s, '%'),
        ch => Err(ParseError::InvalidFormatSpecifier(ch))
    }
}

fn match_str(s: &mut &str, needle: &str) -> bool {
    if s.starts_with(needle) {
        *s = &s[needle.len()..];
        true
    } else {
        false
    }
}

fn match_strs(ss: &mut &str, strs: &[(&str, i32)]) -> Option<i32> {
    for &(needle, value) in strs.iter() {
        if match_str(ss, needle) {
            return Some(value)
        }
    }
    None
}

fn match_digits(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i32> {
    let mut value = 0;
    let mut n = 0;
    if ws {
        let s2 = ss.trim_left_matches(" ");
        n = ss.len() - s2.len();
        if n > max_digits { return None }
    }
    let chars = ss[n..].char_indices();
    for (_, ch) in chars.take(max_digits - n) {
        match ch {
            '0' ... '9' => value = value * 10 + (ch as i32 - '0' as i32),
            _ => break,
        }
        n += 1;
    }

    if n >= min_digits && n <= max_digits {
        *ss = &ss[n..];
        Some(value)
    } else {
        None
    }
}

fn match_fractional_seconds(ss: &mut &str) -> i32 {
    let mut value = 0;
    let mut multiplier = NSEC_PER_SEC / 10;

    let mut chars = ss.char_indices();
    let orig = *ss;
    for (i, ch) in &mut chars {
        *ss = &orig[i..];
        match ch {
            '0' ... '9' => {
                // This will drop digits after the nanoseconds place
                let digit = ch as i32 - '0' as i32;
                value += digit * multiplier;
                multiplier /= 10;
            }
            _ => break
        }
    }

    value
}

fn match_digits_in_range(ss: &mut &str,
                         min_digits : usize, max_digits : usize,
                         ws: bool, min: i32, max: i32) -> Option<i32> {
    let before = *ss;
    match match_digits(ss, min_digits, max_digits, ws) {
        Some(val) if val >= min && val <= max => Some(val),
        _ => { *ss = before; None }
    }
}

fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> {
    match s.char_indices().next() {
        Some((i, c2)) => {
            if c == c2 {
                *s = &s[i + c2.len_utf8()..];
                Ok(())
            } else {
                Err(ParseError::UnexpectedCharacter(c, c2))
            }
        }
        None => Err(ParseError::InvalidTime),
    }
}
<commit_msg>Fix unused result of parse_char()<commit_after>use super::{Tm, ParseError, NSEC_PER_SEC};
use super::ParseError::*;

/// Parses the time from the string according to the format string.
pub fn strptime(mut s: &str, format: &str) -> Result<Tm, ParseError> {
    let mut tm = Tm {
        tm_sec: 0,
        tm_min: 0,
        tm_hour: 0,
        tm_mday: 0,
        tm_mon: 0,
        tm_year: 0,
        tm_wday: 0,
        tm_yday: 0,
        tm_isdst: 0,
        tm_utcoff: 0,
        tm_nsec: 0,
    };
    let mut chars = format.chars();

    while let Some(ch) = chars.next() {
        if ch == '%' {
            if let Some(ch) = chars.next() {
                try!(parse_type(&mut s, ch, &mut tm));
            }
        } else {
            try!(parse_char(&mut s, ch));
        }
    }

    Ok(tm)
}

fn parse_type(s: &mut &str, ch: char, tm: &mut Tm) -> Result<(), ParseError> {
    match ch {
        'A' => match match_strs(s, &[("Sunday", 0),
                                     ("Monday", 1),
                                     ("Tuesday", 2),
                                     ("Wednesday", 3),
                                     ("Thursday", 4),
                                     ("Friday", 5),
                                     ("Saturday", 6)]) {
            Some(v) => { tm.tm_wday = v; Ok(()) }
            None => Err(ParseError::InvalidDay)
        },
        'a' => match match_strs(s, &[("Sun", 0),
                                     ("Mon", 1),
                                     ("Tue", 2),
                                     ("Wed", 3),
                                     ("Thu", 4),
                                     ("Fri", 5),
                                     ("Sat", 6)]) {
            Some(v) => { tm.tm_wday = v; Ok(()) }
            None => Err(ParseError::InvalidDay)
        },
        'B' => match match_strs(s, &[("January", 0),
                                     ("February", 1),
                                     ("March", 2),
                                     ("April", 3),
                                     ("May", 4),
                                     ("June", 5),
                                     ("July", 6),
                                     ("August", 7),
                                     ("September", 8),
                                     ("October", 9),
                                     ("November", 10),
                                     ("December", 11)]) {
            Some(v) => { tm.tm_mon = v; Ok(()) }
            None => Err(ParseError::InvalidMonth)
        },
        'b' | 'h' => match match_strs(s, &[("Jan", 0),
                                           ("Feb", 1),
                                           ("Mar", 2),
                                           ("Apr", 3),
                                           ("May", 4),
                                           ("Jun", 5),
                                           ("Jul", 6),
                                           ("Aug", 7),
                                           ("Sep", 8),
                                           ("Oct", 9),
                                           ("Nov", 10),
                                           ("Dec", 11)]) {
            Some(v) => { tm.tm_mon = v; Ok(()) }
            None => Err(ParseError::InvalidMonth)
        },
        'C' => match match_digits_in_range(s, 1, 2, false, 0, 99) {
            Some(v) => { tm.tm_year += (v * 100) - 1900; Ok(()) }
            None => Err(ParseError::InvalidYear)
        },
        'c' => {
            parse_type(s, 'a', tm)
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'b', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'e', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'T', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'Y', tm))
        }
        'D' | 'x' => {
            parse_type(s, 'm', tm)
                .and_then(|()| parse_char(s, '/'))
                .and_then(|()| parse_type(s, 'd', tm))
                .and_then(|()| parse_char(s, '/'))
                .and_then(|()| parse_type(s, 'y', tm))
        }
        'd' => match match_digits_in_range(s, 1, 2, false, 1, 31) {
            Some(v) => { tm.tm_mday = v; Ok(()) }
            None => Err(ParseError::InvalidDayOfMonth)
        },
        'e' => match match_digits_in_range(s, 1, 2, true, 1, 31) {
            Some(v) => { tm.tm_mday = v; Ok(()) }
            None => Err(ParseError::InvalidDayOfMonth)
        },
        'f' => {
            tm.tm_nsec = match_fractional_seconds(s);
            Ok(())
        }
        'F' => {
            parse_type(s, 'Y', tm)
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'm', tm))
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'd', tm))
        }
        'H' => {
            match match_digits_in_range(s, 1, 2, false, 0, 23) {
                Some(v) => { tm.tm_hour = v; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'I' => {
            match match_digits_in_range(s, 1, 2, false, 1, 12) {
                Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'j' => {
            match match_digits_in_range(s, 1, 3, false, 1, 366) {
                Some(v) => { tm.tm_yday = v - 1; Ok(()) }
                None => Err(ParseError::InvalidDayOfYear)
            }
        }
        'k' => {
            match match_digits_in_range(s, 1, 2, true, 0, 23) {
                Some(v) => { tm.tm_hour = v; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'l' => {
            match match_digits_in_range(s, 1, 2, true, 1, 12) {
                Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidHour)
            }
        }
        'M' => {
            match match_digits_in_range(s, 1, 2, false, 0, 59) {
                Some(v) => { tm.tm_min = v; Ok(()) }
                None => Err(ParseError::InvalidMinute)
            }
        }
        'm' => {
            match match_digits_in_range(s, 1, 2, false, 1, 12) {
                Some(v) => { tm.tm_mon = v - 1; Ok(()) }
                None => Err(ParseError::InvalidMonth)
            }
        }
        'n' => parse_char(s, '\n'),
        'P' => match match_strs(s, &[("am", 0), ("pm", 12)]) {
            Some(v) => { tm.tm_hour += v; Ok(()) }
            None => Err(ParseError::InvalidHour)
        },
        'p' => match match_strs(s, &[("AM", 0), ("PM", 12)]) {
            Some(v) => { tm.tm_hour += v; Ok(()) }
            None => Err(ParseError::InvalidHour)
        },
        'R' => {
            parse_type(s, 'H', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
        }
        'r' => {
            parse_type(s, 'I', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'S', tm))
                .and_then(|()| parse_char(s, ' '))
                .and_then(|()| parse_type(s, 'p', tm))
        }
        'S' => {
            match match_digits_in_range(s, 1, 2, false, 0, 60) {
                Some(v) => { tm.tm_sec = v; Ok(()) }
                None => Err(ParseError::InvalidSecond)
            }
        }
        //'s' {}
        'T' | 'X' => {
            parse_type(s, 'H', tm)
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'M', tm))
                .and_then(|()| parse_char(s, ':'))
                .and_then(|()| parse_type(s, 'S', tm))
        }
        't' => parse_char(s, '\t'),
        'u' => {
            match match_digits_in_range(s, 1, 1, false, 1, 7) {
                Some(v) => { tm.tm_wday = if v == 7 { 0 } else { v }; Ok(()) }
                None => Err(ParseError::InvalidDayOfWeek)
            }
        }
        'v' => {
            parse_type(s, 'e', tm)
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'b', tm))
                .and_then(|()| parse_char(s, '-'))
                .and_then(|()| parse_type(s, 'Y', tm))
        }
        //'W' {}
        'w' => {
            match match_digits_in_range(s, 1, 1, false, 0, 6) {
                Some(v) => { tm.tm_wday = v; Ok(()) }
                None => Err(ParseError::InvalidDayOfWeek)
            }
        }
        'Y' => {
            match match_digits(s, 4, 4, false) {
                Some(v) => { tm.tm_year = v - 1900; Ok(()) }
                None => Err(ParseError::InvalidYear)
            }
        }
        'y' => {
            match match_digits_in_range(s, 1, 2, false, 0, 99) {
                Some(v) => { tm.tm_year = v; Ok(()) }
                None => Err(ParseError::InvalidYear)
            }
        }
        'Z' => {
            if match_str(s, "UTC") || match_str(s, "GMT") {
                tm.tm_utcoff = 0;
                Ok(())
            } else {
                // It's odd, but to maintain compatibility with c's
                // strptime we ignore the timezone.
                for (i, ch) in s.char_indices() {
                    if ch == ' ' {
                        *s = &s[i..];
                        return Ok(())
                    }
                }
                *s = "";
                Ok(())
            }
        }
        'z' => {
            let sign = if parse_char(s, '+').is_ok() {1}
                       else if parse_char(s, '-').is_ok() {-1}
                       else { return Err(ParseError::InvalidZoneOffset) };

            let mut hours = 0;
            let mut minutes = 0;

            match match_digits(s, 2, 2, false) {
                Some(h) => hours = h,
                None => return Err(ParseError::InvalidZoneOffset)
            }

            // consume the colon if its present,
            // just ignore it otherwise
            let _ = parse_char(s, ':');

            match match_digits(s, 2, 2, false) {
                Some(m) => minutes = m,
                None => return Err(ParseError::InvalidZoneOffset)
            }

            tm.tm_utcoff = sign * (hours * 60 * 60 + minutes * 60);
            Ok(())
        }
        '%' => parse_char(s, '%'),
        ch => Err(ParseError::InvalidFormatSpecifier(ch))
    }
}

fn match_str(s: &mut &str, needle: &str) -> bool {
    if s.starts_with(needle) {
        *s = &s[needle.len()..];
        true
    } else {
        false
    }
}

fn match_strs(ss: &mut &str, strs: &[(&str, i32)]) -> Option<i32> {
    for &(needle, value) in strs.iter() {
        if match_str(ss, needle) {
            return Some(value)
        }
    }
    None
}

fn match_digits(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i32> {
    let mut value = 0;
    let mut n = 0;
    if ws {
        let s2 = ss.trim_left_matches(" ");
        n = ss.len() - s2.len();
        if n > max_digits { return None }
    }
    let chars = ss[n..].char_indices();
    for (_, ch) in chars.take(max_digits - n) {
        match ch {
            '0' ... '9' => value = value * 10 + (ch as i32 - '0' as i32),
            _ => break,
        }
        n += 1;
    }

    if n >= min_digits && n <= max_digits {
        *ss = &ss[n..];
        Some(value)
    } else {
        None
    }
}

fn match_fractional_seconds(ss: &mut &str) -> i32 {
    let mut value = 0;
    let mut multiplier = NSEC_PER_SEC / 10;

    let mut chars = ss.char_indices();
    let orig = *ss;
    for (i, ch) in &mut chars {
        *ss = &orig[i..];
        match ch {
            '0' ... '9' => {
                // This will drop digits after the nanoseconds place
                let digit = ch as i32 - '0' as i32;
                value += digit * multiplier;
                multiplier /= 10;
            }
            _ => break
        }
    }

    value
}

fn match_digits_in_range(ss: &mut &str,
                         min_digits : usize, max_digits : usize,
                         ws: bool, min: i32, max: i32) -> Option<i32> {
    let before = *ss;
    match match_digits(ss, min_digits, max_digits, ws) {
        Some(val) if val >= min && val <= max => Some(val),
        _ => { *ss = before; None }
    }
}

fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> {
    match s.char_indices().next() {
        Some((i, c2)) => {
            if c == c2 {
                *s = &s[i + c2.len_utf8()..];
                Ok(())
            } else {
                Err(ParseError::UnexpectedCharacter(c, c2))
            }
        }
        None => Err(ParseError::InvalidTime),
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::collections::{HashMap, VecDeque};

use lexer::{Item, ItemType, Lexer};
use node::*;

pub type Func<'a> = &'a Fn(Option<Box<Any>>) -> Option<Box<Any>>;

pub struct Tree<'a> {
    name: String,
    id: TreeId,
    parse_name: String,
    root: Option<ListNode>,
    text: String,
    funcs: HashMap<String, Func<'a>>,
    lex: Option<Lexer>,
    token: VecDeque<Item>,
    peek_count: usize,
    vars: Vec<String>,
    tree_ids: HashMap<TreeId, String>,
    tree_set: HashMap<String, Tree<'a>>,
    line: usize,
}

impl<'a, 'b> Tree<'a> {
    fn new(name: String, id: TreeId, funcs: HashMap<String, Func<'a>>) -> Tree<'a> {
        Tree {
            name,
            id,
            parse_name: String::default(),
            root: None,
            text: String::default(),
            funcs,
            lex: None,
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }
    fn clone_new(t: &Tree) -> Tree<'b> {
        Tree {
            name: t.name.clone(),
            id: t.id.clone(),
            parse_name: t.parse_name.clone(),
            root: t.root.clone(),
            text: t.text.clone(),
            funcs: HashMap::new(),
            lex: None,
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }
}

pub fn parse<'a>(name: String,
                 text: String,
                 funcs: HashMap<String, Func<'a>>)
                 -> HashMap<String, Tree<'a>> {
    let tree_ids = HashMap::new();
    tree_ids
}

impl<'a> Tree<'a> {
    fn next_from_lex(&mut self) -> Option<Item> {
        match &mut self.lex {
            &mut Some(ref mut l) => l.next(),
            &mut None => None,
        }
    }

    fn backup(&mut self, t: Item) {
        self.token.push_back(t);
        self.peek_count = 1;
    }

    fn backup2(&mut self, t0: Item, t1: Item) {
        self.token.push_back(t0);
        self.token.push_back(t1);
        self.peek_count = 2;
    }

    fn backup3(&mut self, t0: Item, t1: Item, t2: Item) {
        self.token.push_back(t0);
        self.token.push_back(t1);
        self.token.push_back(t2);
        self.peek_count = 3;
    }

    fn next_non_space(&mut self) -> Option<Item> {
        self.skip_while(|c| c.typ == ItemType::ItemSpace).next()
    }

    fn peek_non_space(&mut self) -> Option<&Item> {
        if let Some(t) = self.next_non_space() {
            self.backup(t);
            return self.token.front();
        }
        None
    }

    fn error_context(&mut self, n: Nodes) -> (String, String) {
        let pos = n.pos();
        let tree_id = n.tree();
        let tree = if tree_id == 0 && self.tree_ids.contains_key(&tree_id) {
            self.tree_by_id(tree_id).unwrap()
        } else {
            self
        };
        let text = &tree.text[0..pos];
        let byte_num = match text.rfind('\n') {
            Some(i) => pos - (i + 1),
            None => pos,
        };
        let line_num = text.chars().filter(|c| *c == '\n').count();
        let context = n.to_string();
        (format!("{}:{}:{}", tree.parse_name, line_num, byte_num), context)
    }

    fn start_parse(&mut self,
                   funcs: HashMap<String, Func<'a>>,
                   lex: Lexer,
                   tree_ids: HashMap<TreeId, String>,
                   tree_set: HashMap<String, Tree<'a>>) {
        self.root = None;
        self.lex = Some(lex);
        self.vars = vec!["$".to_owned()];
        self.funcs = funcs;
        self.tree_ids = tree_ids;
        self.tree_set = tree_set;
    }

    fn stop_parse(&mut self) {
        self.lex = None;
        self.vars = vec![];
        self.funcs = HashMap::new();
        self.tree_ids = HashMap::new();
        self.tree_set = HashMap::new();
    }

    fn parse(&mut self,
             text: String,
             tree_ids: HashMap<TreeId, String>,
             tree_set: HashMap<String, Tree<'a>>,
             funcs: HashMap<String, Func<'a>>)
             -> Result<(), String> {
        self.parse_name = self.name.clone();
        let lex_name = self.name.clone();
        self.start_parse(funcs,
                         Lexer::new(&lex_name, text.clone()),
                         tree_ids,
                         tree_set);
        self.text = text;
        //self.do_parse();
        self.stop_parse();
        Ok(())
    }

    fn tree_by_id(&self, id: TreeId) -> Option<&Tree<'a>> {
        self.tree_ids
            .get(&id)
            .and_then(|name| self.tree_set.get(name))
    }
    fn add_tree(&mut self, name: &str, t: Tree<'a>) {
        self.tree_ids.insert(t.id, name.to_owned());
        self.tree_set.insert(name.to_owned(), t);
    }

    fn error(&mut self, msg: String) -> Result<(), String> {
        self.root = None;
        Err(format!("template: {}:{}:{}", self.parse_name, self.line, msg))
    }

    fn add_to_tree_set(mut tree: Tree<'a>,
                       mut tree_set: HashMap<String, Tree<'a>>)
                       -> Result<(), String> {
        if let Some(t) = tree_set.get(&tree.name) {
            if let Some(ref r) = t.root {
                match r.is_empty_tree() {
                    Err(e) => return Err(e),
                    Ok(false) => {
                        let err = format!("template multiple definitions of template {}",
                                          &tree.name);
                        return tree.error(err);
                    }
                    Ok(true) => {}
                }
            }
        }
        tree_set.insert(tree.name.clone(), tree);
        Ok(())
    }
}

impl<'a> Iterator for Tree<'a> {
    type Item = Item;
    fn next(&mut self) -> Option<Item> {
        let item = if self.peek_count > 0 {
            self.peek_count -= 1;
            self.token.pop_front()
        } else {
            self.next_from_lex()
        };
        match item {
            Some(item) => {
                self.line = item.line;
                Some(item)
            },
            _ => None
        }
    }
}

#[cfg(test)]
mod tests_mocked {
    use super::*;
    use lexer::ItemType;

    /*
       ItemText
       ItemLeftDelim
       ItemSpace
       ItemIf
       ItemSpace
       ItemIdentifier
       ItemSpace
       ItemString
       ItemSpace
       ItemString
       ItemSpace
       ItemRightDelim
       ItemEOF
    */
    fn make_tree<'a>() -> Tree<'a> {
        let s = r#"something {{ if eq "foo" "bar" }}"#;
        let lex = Lexer::new("foo", s.to_owned());
        Tree {
            name: "foo".to_owned(),
            id: 1,
            parse_name: "bar".to_owned(),
            root: None,
            text: "nope".to_owned(),
            funcs: HashMap::new(),
            lex: Some(lex),
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }

    #[test]
    fn test_iter() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        assert_eq!(t.collect::<Vec<_>>().len(), 12);
    }

    #[test]
    fn test_backup() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        let i = t.next().unwrap();
        let s = i.to_string();
        t.backup(i);
        assert_eq!(t.next().and_then(|n| Some(n.to_string())), Some(s));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }
    #[test]
    fn test_backup3() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        let t0 = t.next().unwrap();
        let t1 = t.next().unwrap();
        let t2 = t.next().unwrap();
        assert_eq!(t0.typ, ItemType::ItemLeftDelim);
        assert_eq!(t1.typ, ItemType::ItemSpace);
        assert_eq!(t2.typ, ItemType::ItemIf);
        t.backup3(t0, t1, t2);
        let t0 = t.next().unwrap();
        let t1 = t.next().unwrap();
        let t2 = t.next().unwrap();
        assert_eq!(t0.typ, ItemType::ItemLeftDelim);
        assert_eq!(t1.typ, ItemType::ItemSpace);
        assert_eq!(t2.typ, ItemType::ItemIf);
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }

    #[test]
    fn test_next_non_space() {
        let mut t = make_tree();
        t.next();
        let i = t.next().unwrap();
        let typ = i.typ;
        assert_eq!(typ, ItemType::ItemLeftDelim);
        assert_eq!(t.next_non_space().and_then(|n| Some(n.typ)),
                   Some(ItemType::ItemIf));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }

    #[test]
    fn test_peek_non_space() {
        let mut t = make_tree();
        t.next();
        let i = t.next().unwrap();
        let typ = i.typ;
        assert_eq!(typ, ItemType::ItemLeftDelim);
        assert_eq!(t.peek_non_space().and_then(|n| Some(&n.typ)),
                   Some(&ItemType::ItemIf));
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemIf));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }
}
<commit_msg>start on parse<commit_after>use std::any::Any;
use std::collections::{HashMap, VecDeque};

use lexer::{Item, ItemType, Lexer};
use node::*;

pub type Func<'a> = &'a Fn(Option<Box<Any>>) -> Option<Box<Any>>;

pub struct Tree<'a> {
    name: String,
    id: TreeId,
    parse_name: String,
    root: Option<ListNode>,
    text: String,
    funcs: HashMap<String, Func<'a>>,
    lex: Option<Lexer>,
    token: VecDeque<Item>,
    peek_count: usize,
    vars: Vec<String>,
    tree_ids: HashMap<TreeId, String>,
    tree_set: HashMap<String, Tree<'a>>,
    line: usize,
}

impl<'a, 'b> Tree<'a> {
    fn new(name: String, id: TreeId, funcs: HashMap<String, Func<'a>>) -> Tree<'a> {
        Tree {
            name,
            id,
            parse_name: String::default(),
            root: None,
            text: String::default(),
            funcs,
            lex: None,
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }
    fn clone_new(t: &Tree) -> Tree<'b> {
        Tree {
            name: t.name.clone(),
            id: t.id.clone(),
            parse_name: t.parse_name.clone(),
            root: t.root.clone(),
            text: t.text.clone(),
            funcs: HashMap::new(),
            lex: None,
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }
}

pub fn parse<'a>(name: String,
                 text: String,
                 funcs: HashMap<String, Func<'a>>)
                 -> HashMap<String, Tree<'a>> {
    let tree_ids = HashMap::new();
    tree_ids
}

impl<'a> Tree<'a> {
    fn next_from_lex(&mut self) -> Option<Item> {
        match &mut self.lex {
            &mut Some(ref mut l) => l.next(),
            &mut None => None,
        }
    }

    fn backup(&mut self, t: Item) {
        self.token.push_back(t);
        self.peek_count = 1;
    }

    fn backup2(&mut self, t0: Item, t1: Item) {
        self.token.push_back(t0);
        self.token.push_back(t1);
        self.peek_count = 2;
    }

    fn backup3(&mut self, t0: Item, t1: Item, t2: Item) {
        self.token.push_back(t0);
        self.token.push_back(t1);
        self.token.push_back(t2);
        self.peek_count = 3;
    }

    fn next_non_space(&mut self) -> Option<Item> {
        self.skip_while(|c| c.typ == ItemType::ItemSpace).next()
    }

    fn peek_non_space(&mut self) -> Option<&Item> {
        if let Some(t) = self.next_non_space() {
            self.backup(t);
            return self.token.front();
        }
        None
    }

    fn peek(&mut self) -> Option<&Item> {
        if let Some(t) = self.next() {
            self.backup(t);
            return self.token.front();
        }
        None
    }

    fn error_context(&mut self, n: Nodes) -> (String, String) {
        let pos = n.pos();
        let tree_id = n.tree();
        let tree = if tree_id == 0 && self.tree_ids.contains_key(&tree_id) {
            self.tree_by_id(tree_id).unwrap()
        } else {
            self
        };
        let text = &tree.text[0..pos];
        let byte_num = match text.rfind('\n') {
            Some(i) => pos - (i + 1),
            None => pos,
        };
        let line_num = text.chars().filter(|c| *c == '\n').count();
        let context = n.to_string();
        (format!("{}:{}:{}", tree.parse_name, line_num, byte_num), context)
    }

    fn start_parse(&mut self,
                   funcs: HashMap<String, Func<'a>>,
                   lex: Lexer,
                   tree_ids: HashMap<TreeId, String>,
                   tree_set: HashMap<String, Tree<'a>>) {
        self.root = None;
        self.lex = Some(lex);
        self.vars = vec!["$".to_owned()];
        self.funcs = funcs;
        self.tree_ids = tree_ids;
        self.tree_set = tree_set;
    }

    fn stop_parse(&mut self) {
        self.lex = None;
        self.vars = vec![];
        self.funcs = HashMap::new();
        self.tree_ids = HashMap::new();
        self.tree_set = HashMap::new();
    }

    fn parse_tree(tree: &mut Tree<'a>,
             text: String,
             tree_ids: HashMap<TreeId, String>,
             tree_set: HashMap<String, Tree<'a>>,
             funcs: HashMap<String, Func<'a>>)
             -> Result<(), String> {
        tree.parse_name = tree.name.clone();
        let lex_name = tree.name.clone();
        tree.start_parse(funcs,
                         Lexer::new(&lex_name, text.clone()),
                         tree_ids,
                         tree_set);
        tree.text = text;
        tree.parse();
        tree.stop_parse();
        Ok(())
    }

    fn tree_by_id(&self, id: TreeId) -> Option<&Tree<'a>> {
        self.tree_ids
            .get(&id)
            .and_then(|name| self.tree_set.get(name))
    }
    fn add_tree(&mut self, name: &str, t: Tree<'a>) {
        self.tree_ids.insert(t.id, name.to_owned());
        self.tree_set.insert(name.to_owned(), t);
    }

    fn error(&mut self, msg: String) -> Result<(), String> {
        self.root = None;
        Err(format!("template: {}:{}:{}", self.parse_name, self.line, msg))
    }

    fn add_to_tree_set(mut tree: Tree<'a>,
                       mut tree_set: HashMap<String, Tree<'a>>)
                       -> Result<(), String> {
        if let Some(t) = tree_set.get(&tree.name) {
            if let Some(ref r) = t.root {
                match r.is_empty_tree() {
                    Err(e) => return Err(e),
                    Ok(false) => {
                        let err = format!("template multiple definitions of template {}",
                                          &tree.name);
                        return tree.error(err);
                    }
                    Ok(true) => {}
                }
            }
        }
        tree_set.insert(tree.name.clone(), tree);
        Ok(())
    }

    fn parse(&mut self) -> Result<(), String> {
        let id = self.id;
        let n = self.next();
        if n.is_none() {
            return self.error(format!("unable to peek for tree {}", id));
        }
        let t = n.unwrap();
        self.root = Some(ListNode::new(id, t.pos));
        let mut typ = t.typ;
        while typ == ItemType::ItemEOF {
            if typ == ItemType::ItemLeftDelim  {
                let nos = self.next_non_space();
                // lots missing
            }
        }
        Ok(())
    }
}

impl<'a> Iterator for Tree<'a> {
    type Item = Item;
    fn next(&mut self) -> Option<Item> {
        let item = if self.peek_count > 0 {
            self.peek_count -= 1;
            self.token.pop_front()
        } else {
            self.next_from_lex()
        };
        match item {
            Some(item) => {
                self.line = item.line;
                Some(item)
            },
            _ => None
        }
    }
}

#[cfg(test)]
mod tests_mocked {
    use super::*;
    use lexer::ItemType;

    /*
       ItemText
       ItemLeftDelim
       ItemSpace
       ItemIf
       ItemSpace
       ItemIdentifier
       ItemSpace
       ItemString
       ItemSpace
       ItemString
       ItemSpace
       ItemRightDelim
       ItemEOF
    */
    fn make_tree<'a>() -> Tree<'a> {
        let s = r#"something {{ if eq "foo" "bar" }}"#;
        let lex = Lexer::new("foo", s.to_owned());
        Tree {
            name: "foo".to_owned(),
            id: 1,
            parse_name: "bar".to_owned(),
            root: None,
            text: "nope".to_owned(),
            funcs: HashMap::new(),
            lex: Some(lex),
            token: VecDeque::new(),
            peek_count: 0,
            vars: vec![],
            tree_ids: HashMap::new(),
            tree_set: HashMap::new(),
            line: 0,
        }
    }

    #[test]
    fn test_iter() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        assert_eq!(t.collect::<Vec<_>>().len(), 12);
    }

    #[test]
    fn test_backup() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        let i = t.next().unwrap();
        let s = i.to_string();
        t.backup(i);
        assert_eq!(t.next().and_then(|n| Some(n.to_string())), Some(s));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }
    #[test]
    fn test_backup3() {
        let mut t = make_tree();
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemText));
        let t0 = t.next().unwrap();
        let t1 = t.next().unwrap();
        let t2 = t.next().unwrap();
        assert_eq!(t0.typ, ItemType::ItemLeftDelim);
        assert_eq!(t1.typ, ItemType::ItemSpace);
        assert_eq!(t2.typ, ItemType::ItemIf);
        t.backup3(t0, t1, t2);
        let t0 = t.next().unwrap();
        let t1 = t.next().unwrap();
        let t2 = t.next().unwrap();
        assert_eq!(t0.typ, ItemType::ItemLeftDelim);
        assert_eq!(t1.typ, ItemType::ItemSpace);
        assert_eq!(t2.typ, ItemType::ItemIf);
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }

    #[test]
    fn test_next_non_space() {
        let mut t = make_tree();
        t.next();
        let i = t.next().unwrap();
        let typ = i.typ;
        assert_eq!(typ, ItemType::ItemLeftDelim);
        assert_eq!(t.next_non_space().and_then(|n| Some(n.typ)),
                   Some(ItemType::ItemIf));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }

    #[test]
    fn test_peek_non_space() {
        let mut t = make_tree();
        t.next();
        let i = t.next().unwrap();
        let typ = i.typ;
        assert_eq!(typ, ItemType::ItemLeftDelim);
        assert_eq!(t.peek_non_space().and_then(|n| Some(&n.typ)),
                   Some(&ItemType::ItemIf));
        assert_eq!(t.next().and_then(|n| Some(n.typ)), Some(ItemType::ItemIf));
        assert_eq!(t.last().and_then(|n| Some(n.typ)), Some(ItemType::ItemEOF));
    }
}
<|endoftext|>


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

/// Inclusive range from min to max
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InclusiveRange<T: Copy + PartialOrd> {
    pub min: T,
    pub max: T
}

pub const RANGE_0_1_F32: InclusiveRange<f32> = InclusiveRange { min: 0.0, max: 0.0 };

fn pomax<T: PartialOrd>(a: T, b: T) -> T {
    if a > b { a } else { b }
}

fn pomin<T: PartialOrd>(a: T, b: T) -> T {
    if a < b { a } else { b }
}

impl<T: Copy + PartialOrd> InclusiveRange<T> {
    /// Create an InclusiveRange range from min to max
    pub fn new(min: T, max: T) -> InclusiveRange<T> {
        assert!(min <= max);
        InclusiveRange { min: min, max: max }
    }

    pub fn expand(&mut self, other: &InclusiveRange<T>) {
        self.min = pomin(self.min, other.min);
        self.max = pomax(self.max, other.max);
    }
}

/// Create range covering the overlap between two ranges, or None if
/// there is no overlap.
pub fn range_clamp<T: Copy + PartialOrd>(a: &InclusiveRange<T>,
                                         b: &InclusiveRange<T>)
                                         -> Option<InclusiveRange<T>> {
    let min = pomax(a.min, b.min);
    let max = pomin(a.max, b.max);

    if min <= max {
        Some(InclusiveRange::new(min, max))
    }
    else {
        None
    }
}

/// Create range covering both ranges (and any gap between them).
pub fn range_combine<T: Copy + PartialOrd>(a: &InclusiveRange<T>,
                                           b: &InclusiveRange<T>) -> InclusiveRange<T> {
    InclusiveRange::new(pomin(a.min, b.min),
                        pomax(a.max, b.max))
}

#[test]
fn test_range_clamp() {
    assert!(range_clamp(&InclusiveRange::new(0, 2),
                        &InclusiveRange::new(0, 2)) ==
            Some(InclusiveRange::new(0, 2)));

    assert!(range_clamp(&InclusiveRange::new(0, 1),
                        &InclusiveRange::new(2, 3)) ==
            None);

    assert!(range_clamp(&InclusiveRange::new(2, 3),
                        &InclusiveRange::new(0, 1)) ==
            None);

    assert!(range_clamp(&InclusiveRange::new(0, 2),
                        &InclusiveRange::new(1, 3)) ==
            Some(InclusiveRange::new(1, 2)));

    assert!(range_clamp(&InclusiveRange::new(1, 3),
                        &InclusiveRange::new(0, 2)) ==
            Some(InclusiveRange::new(1, 2)));

    assert!(range_clamp(&InclusiveRange::new(1.0, 3.0),
                        &InclusiveRange::new(0.0, 2.0)) ==
            Some(InclusiveRange::new(1.0, 2.0)));
}

#[test]
fn test_range_expand() {
    let mut r = InclusiveRange::new(0, 1);
    r.expand(&InclusiveRange::new(-1, 2));
    assert!(r == InclusiveRange::new(-1, 2));
}
<commit_msg>Add docstring<commit_after>// Copyright 2015 Nicholas Bishop
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Inclusive range from min to max
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct InclusiveRange<T: Copy + PartialOrd> {
    pub min: T,
    pub max: T
}

pub const RANGE_0_1_F32: InclusiveRange<f32> = InclusiveRange { min: 0.0, max: 0.0 };

fn pomax<T: PartialOrd>(a: T, b: T) -> T {
    if a > b { a } else { b }
}

fn pomin<T: PartialOrd>(a: T, b: T) -> T {
    if a < b { a } else { b }
}

impl<T: Copy + PartialOrd> InclusiveRange<T> {
    /// Create an InclusiveRange range from min to max
    pub fn new(min: T, max: T) -> InclusiveRange<T> {
        assert!(min <= max);
        InclusiveRange { min: min, max: max }
    }

    /// Expand `self` as needed to include another range.
    pub fn expand(&mut self, other: &InclusiveRange<T>) {
        self.min = pomin(self.min, other.min);
        self.max = pomax(self.max, other.max);
    }
}

/// Create range covering the overlap between two ranges, or None if
/// there is no overlap.
pub fn range_clamp<T: Copy + PartialOrd>(a: &InclusiveRange<T>,
                                         b: &InclusiveRange<T>)
                                         -> Option<InclusiveRange<T>> {
    let min = pomax(a.min, b.min);
    let max = pomin(a.max, b.max);

    if min <= max {
        Some(InclusiveRange::new(min, max))
    }
    else {
        None
    }
}

/// Create range covering both ranges (and any gap between them).
pub fn range_combine<T: Copy + PartialOrd>(a: &InclusiveRange<T>,
                                           b: &InclusiveRange<T>) -> InclusiveRange<T> {
    InclusiveRange::new(pomin(a.min, b.min),
                        pomax(a.max, b.max))
}

#[test]
fn test_range_clamp() {
    assert!(range_clamp(&InclusiveRange::new(0, 2),
                        &InclusiveRange::new(0, 2)) ==
            Some(InclusiveRange::new(0, 2)));

    assert!(range_clamp(&InclusiveRange::new(0, 1),
                        &InclusiveRange::new(2, 3)) ==
            None);

    assert!(range_clamp(&InclusiveRange::new(2, 3),
                        &InclusiveRange::new(0, 1)) ==
            None);

    assert!(range_clamp(&InclusiveRange::new(0, 2),
                        &InclusiveRange::new(1, 3)) ==
            Some(InclusiveRange::new(1, 2)));

    assert!(range_clamp(&InclusiveRange::new(1, 3),
                        &InclusiveRange::new(0, 2)) ==
            Some(InclusiveRange::new(1, 2)));

    assert!(range_clamp(&InclusiveRange::new(1.0, 3.0),
                        &InclusiveRange::new(0.0, 2.0)) ==
            Some(InclusiveRange::new(1.0, 2.0)));
}

#[test]
fn test_range_expand() {
    let mut r = InclusiveRange::new(0, 1);
    r.expand(&InclusiveRange::new(-1, 2));
    assert!(r == InclusiveRange::new(-1, 2));
}
<|endoftext|>


<commit_before>use cgmath::prelude::*;
use cgmath::Point3;

use crate::color::Color;
use crate::consts;
use crate::index_ptr::IndexPtr;
use crate::intersect::Interaction;
use crate::triangle::Triangle;
use crate::Float;

pub trait Light {
    fn power(&self) -> Color;

    /// Sample radiance toward receiving interaction.
    /// Return radiance, shadow ray and the pdf
    fn sample_li(&self, recv: &Interaction) -> (Color, Point3<Float>, Float);

    // fn pdf_li(&self) {}

    // fn sample_le(&self) {}

    // fn pdf_le(&self) {}
}

pub struct AreaLight {
    tri: IndexPtr<Triangle>,
}

impl AreaLight {
    pub fn new(tri: IndexPtr<Triangle>) -> Self {
        Self { tri }
    }
}

impl Light for AreaLight {
    fn power(&self) -> Color {
        consts::PI * self.tri.material.emissive.unwrap() * self.tri.area()
    }

    /// Sample radiance toward receiving interaction.
    /// Return radiance, shadow ray and the pdf
    fn sample_li(&self, recv: &Interaction) -> (Color, Point3<Float>, Float) {
        let (u, v) = Triangle::sample();
        let (p, n, _) = self.tri.bary_pnt(u, v);
        let mut pdf = self.tri.pdf_a();
        let dp = p - recv.p;
        let dir = dp.normalize();
        // Convert pdf to solid angle
        pdf *= dp.magnitude2() / n.dot(-dir).abs();
        let li = self.tri.le(-dir);
        (li, p, pdf)
    }

    // fn pdf_li(&self) {}

    // fn sample_le(&self) {}

    // fn pdf_le(&self) {}
}
<commit_msg>Remove unnecessary negation<commit_after>use cgmath::prelude::*;
use cgmath::Point3;

use crate::color::Color;
use crate::consts;
use crate::index_ptr::IndexPtr;
use crate::intersect::Interaction;
use crate::triangle::Triangle;
use crate::Float;

pub trait Light {
    fn power(&self) -> Color;

    /// Sample radiance toward receiving interaction.
    /// Return radiance, shadow ray and the pdf
    fn sample_li(&self, recv: &Interaction) -> (Color, Point3<Float>, Float);

    // fn pdf_li(&self) {}

    // fn sample_le(&self) {}

    // fn pdf_le(&self) {}
}

pub struct AreaLight {
    tri: IndexPtr<Triangle>,
}

impl AreaLight {
    pub fn new(tri: IndexPtr<Triangle>) -> Self {
        Self { tri }
    }
}

impl Light for AreaLight {
    fn power(&self) -> Color {
        consts::PI * self.tri.material.emissive.unwrap() * self.tri.area()
    }

    /// Sample radiance toward receiving interaction.
    /// Return radiance, shadow ray and the pdf
    fn sample_li(&self, recv: &Interaction) -> (Color, Point3<Float>, Float) {
        let (u, v) = Triangle::sample();
        let (p, n, _) = self.tri.bary_pnt(u, v);
        let mut pdf = self.tri.pdf_a();
        let dp = p - recv.p;
        let dir = dp.normalize();
        // Convert pdf to solid angle
        pdf *= dp.magnitude2() / n.dot(dir).abs();
        let li = self.tri.le(-dir);
        (li, p, pdf)
    }

    // fn pdf_li(&self) {}

    // fn sample_le(&self) {}

    // fn pdf_le(&self) {}
}
<|endoftext|>


<commit_before>//! Miscellaneous tools for used for Searching. Most notably this module
//! contains the `TranspositionTable`, a fast lookup table able to be accessed by
//! multiple threads. Other useful objects are the `UciLimit` enum and `Searcher` trait
//! for building bots.

pub mod prng;
pub mod eval;
pub mod tt;
pub mod timer;
pub mod pawn_table;

use std::ptr::NonNull;
use std::heap::{Alloc, Layout, Heap};
use std::cell::UnsafeCell;

use core::piece_move::BitMove;
use tools::timer::Timer;
use board::Board;

/// Defines an object that can play chess.
pub trait Searcher {
    fn name() -> &'static str where Self: Sized;

    fn best_move(board: Board, limit: UCILimit) -> BitMove
        where
            Self: Sized;

    fn best_move_depth(board: Board, max_depth: u16) -> BitMove
        where
            Self: Sized {
        Self::best_move(board, UCILimit::Depth(max_depth))
    }
}

// TODO: Decrement this
/// Defines a Limit for a Searcher. e.g., when a searcher should stop
/// searching.
#[derive(Clone)]
pub enum UCILimit {
    Infinite,
    Depth(u16),
    Nodes(u64),
    Time(Timer),
}

impl UCILimit {
    /// Returns if time management should be used.
    pub fn use_time(&self) -> bool {
        if let UCILimit::Time(_) = *self {
            true
        } else {
            false
        }
    }

    /// Returns if the limit is depth.
    pub fn is_depth(&self) -> bool {
        if let UCILimit::Depth(_) = *self {
            true
        } else {
            false
        }
    }

    /// Returns the depth limit if there is one, otherwise returns 10000.
    pub fn depth_limit(&self) -> u16 {
        if let UCILimit::Depth(depth) = *self {
            depth
        } else {
            10_000
        }
    }

    /// Returns the Timer for the UCILimit, if there is one to be sent.
    pub fn timer(&self) -> Option<Timer> {
        if let UCILimit::Time(timer) = *self {
            Some(timer.clone())
        } else {
            None
        }
    }
}

// TODO: Performance increase awaiting with const generics: https://github.com/rust-lang/rust/issues/44580

/// Generic Heap-stored array of entries.
pub struct TableBase<T: Sized> {
    table: UnsafeCell<NonNull<T>>,
    size: UnsafeCell<usize>
}

impl<T: Sized> TableBase<T> {
    /// Constructs a new `TableBase`. The size must be a power of 2, or else `None` is
    /// returned.
    pub fn new(size: usize) -> Option<TableBase<T>> {
        if size.count_ones() != 1 {
            None
        } else {
            unsafe  {
                let table = TableBase {
                    table: UnsafeCell::new(TableBase::alloc(size)),
                    size: UnsafeCell::new(size)
                };
                Some(table)
            }
        }
    }

    /// Returns the size of the Table.
    #[inline]
    pub fn size(&self) -> usize {
        unsafe {*self.size.get()}
    }

    /// Gets a mutable reference to an entry with a certain key.
    #[inline]
    pub fn get_mut(&self, key: u64) -> &mut T {
        let ptr: *mut T = self.get_ptr(key);
        unsafe {
            &mut *ptr
        }
    }

    /// Gets a mutable pointer to an entry with a certain key.
    #[inline(always)]
    pub fn get_ptr(&self, key: u64) -> *mut T {
        let index: usize = (key & (self.size() as u64 - 1)) as usize;
        unsafe {
            (*self.table.get()).as_ptr().offset(index as isize)
        }
    }

    /// Re-sizes the table to a particular size, which must be a power of 2.
    pub unsafe fn resize(&self, size: usize) {
        assert_eq!(size.count_ones(), 1);
        self.de_alloc();
        *self.table.get() = TableBase::alloc(size);
        *self.size.get() = size;
    }

    unsafe fn alloc(size: usize) -> NonNull<T> {
        let ptr = Heap.alloc_zeroed(Layout::array::<T>(size).unwrap());
        let new_ptr = match ptr {
            Ok(ptr) => ptr,
            Err(err) => Heap.oom(err),
        };
        NonNull::new(new_ptr as *mut T).unwrap()
    }

    unsafe fn de_alloc(&self) {
        Heap.dealloc(self.table.get() as *mut _,
                     Layout::array::<T>(*self.size.get()).unwrap());
    }
}

impl<T: Sized> Drop for TableBase<T> {
    fn drop(&mut self) {
        unsafe {
            self.de_alloc();
        }
    }
}

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

    #[test]
    fn table_base_allocs() {
        for i in 0..14 {
            let size: usize = 1 << i;
            let t = TableBase::<u64>::new(size).unwrap();
            for x in 0..(3*size) {
                *t.get_mut(x as u64) = x as u64;
            }
        }
//        println!("ok");
    }
}<commit_msg>Lets just not drop anything<commit_after>//! Miscellaneous tools for used for Searching. Most notably this module
//! contains the `TranspositionTable`, a fast lookup table able to be accessed by
//! multiple threads. Other useful objects are the `UciLimit` enum and `Searcher` trait
//! for building bots.

pub mod prng;
pub mod eval;
pub mod tt;
pub mod timer;
pub mod pawn_table;

use std::ptr::NonNull;
use std::heap::{Alloc, Layout, Heap};
use std::cell::UnsafeCell;

use core::piece_move::BitMove;
use tools::timer::Timer;
use board::Board;

/// Defines an object that can play chess.
pub trait Searcher {
    fn name() -> &'static str where Self: Sized;

    fn best_move(board: Board, limit: UCILimit) -> BitMove
        where
            Self: Sized;

    fn best_move_depth(board: Board, max_depth: u16) -> BitMove
        where
            Self: Sized {
        Self::best_move(board, UCILimit::Depth(max_depth))
    }
}

// TODO: Decrement this
/// Defines a Limit for a Searcher. e.g., when a searcher should stop
/// searching.
#[derive(Clone)]
pub enum UCILimit {
    Infinite,
    Depth(u16),
    Nodes(u64),
    Time(Timer),
}

impl UCILimit {
    /// Returns if time management should be used.
    pub fn use_time(&self) -> bool {
        if let UCILimit::Time(_) = *self {
            true
        } else {
            false
        }
    }

    /// Returns if the limit is depth.
    pub fn is_depth(&self) -> bool {
        if let UCILimit::Depth(_) = *self {
            true
        } else {
            false
        }
    }

    /// Returns the depth limit if there is one, otherwise returns 10000.
    pub fn depth_limit(&self) -> u16 {
        if let UCILimit::Depth(depth) = *self {
            depth
        } else {
            10_000
        }
    }

    /// Returns the Timer for the UCILimit, if there is one to be sent.
    pub fn timer(&self) -> Option<Timer> {
        if let UCILimit::Time(timer) = *self {
            Some(timer.clone())
        } else {
            None
        }
    }
}

// TODO: Performance increase awaiting with const generics: https://github.com/rust-lang/rust/issues/44580

/// Generic Heap-stored array of entries.
pub struct TableBase<T: Sized> {
    table: UnsafeCell<NonNull<T>>,
    size: UnsafeCell<usize>
}

impl<T: Sized> TableBase<T> {
    /// Constructs a new `TableBase`. The size must be a power of 2, or else `None` is
    /// returned.
    pub fn new(size: usize) -> Option<TableBase<T>> {
        if size.count_ones() != 1 {
            None
        } else {
            unsafe  {
                let table = TableBase {
                    table: UnsafeCell::new(TableBase::alloc(size)),
                    size: UnsafeCell::new(size)
                };
                Some(table)
            }
        }
    }

    /// Returns the size of the Table.
    #[inline]
    pub fn size(&self) -> usize {
        unsafe {*self.size.get()}
    }

    /// Gets a mutable reference to an entry with a certain key.
    #[inline]
    pub fn get_mut(&self, key: u64) -> &mut T {
        let ptr: *mut T = self.get_ptr(key);
        unsafe {
            &mut *ptr
        }
    }

    /// Gets a mutable pointer to an entry with a certain key.
    #[inline(always)]
    pub fn get_ptr(&self, key: u64) -> *mut T {
        let index: usize = (key & (self.size() as u64 - 1)) as usize;
        unsafe {
            (*self.table.get()).as_ptr().offset(index as isize)
        }
    }

    /// Re-sizes the table to a particular size, which must be a power of 2.
    pub unsafe fn resize(&self, size: usize) {
        assert_eq!(size.count_ones(), 1);
        self.de_alloc();
        *self.table.get() = TableBase::alloc(size);
        *self.size.get() = size;
    }

    unsafe fn alloc(size: usize) -> NonNull<T> {
        let ptr = Heap.alloc_zeroed(Layout::array::<T>(size).unwrap());
        let new_ptr = match ptr {
            Ok(ptr) => ptr,
            Err(err) => Heap.oom(err),
        };
        NonNull::new(new_ptr as *mut T).unwrap()
    }

    unsafe fn de_alloc(&self) {
        Heap.dealloc(self.table.get() as *mut _,
                     Layout::array::<T>(*self.size.get()).unwrap());
    }
}

//impl<T: Sized> Drop for TableBase<T> {
//    fn drop(&mut self) {
//        unsafe {
//            self.de_alloc();
//        }
//    }
//}

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

    #[test]
    fn table_base_allocs() {
        for i in 0..14 {
            let size: usize = 1 << i;
            let t = TableBase::<u64>::new(size).unwrap();
            for x in 0..(3*size) {
                *t.get_mut(x as u64) = x as u64;
            }
        }
//        println!("ok");
    }
}<|endoftext|>


<commit_before>use super::*;

pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
    let m;
    if p.at(T![::]) && p.nth(2) == T![<] {
        m = p.start();
        p.bump(T![::]);
        p.bump(T![<]);
    } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
        m = p.start();
        p.bump(T![<]);
    } else {
        return;
    }

    while !p.at(EOF) && !p.at(T![>]) {
        generic_arg(p);
        if !p.at(T![>]) && !p.expect(T![,]) {
            break;
        }
    }
    p.expect(T![>]);
    m.complete(p, GENERIC_ARG_LIST);
}

// test type_arg
// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
fn generic_arg(p: &mut Parser) {
    match p.current() {
        LIFETIME_IDENT => {
            let m = p.start();
            lifetime(p);
            m.complete(p, LIFETIME_ARG);
        }
        // test associated_type_bounds
        // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
        IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) => {
            let m = p.start();
            let path_ty = p.start();
            let path = p.start();
            let path_seg = p.start();
            name_ref(p);
            opt_generic_arg_list(p, false);
            match p.current() {
                // NameRef<...> =
                T![=] => {
                    p.bump_any();
                    types::type_(p);

                    path_seg.abandon(p);
                    path.abandon(p);
                    path_ty.abandon(p);
                    m.complete(p, ASSOC_TYPE_ARG);
                }
                T![:] if p.at(T![::]) => {
                    // NameRef::, this is a path type
                    path_seg.complete(p, PATH_SEGMENT);
                    let qual = path.complete(p, PATH);
                    opt_generic_arg_list(p, false);
                    paths::type_path_for_qualifier(p, qual);
                    path_ty.complete(p, PATH_TYPE);
                    m.complete(p, TYPE_ARG);
                }
                // NameRef<...>:
                T![:] => {
                    generic_params::bounds(p);

                    path_seg.abandon(p);
                    path.abandon(p);
                    path_ty.abandon(p);
                    m.complete(p, ASSOC_TYPE_ARG);
                }
                // NameRef, this is a single segment path type
                _ => {
                    path_seg.complete(p, PATH_SEGMENT);
                    path.complete(p, PATH);
                    path_ty.complete(p, PATH_TYPE);
                    m.complete(p, TYPE_ARG);
                }
            }
        }
        // test const_generic_negated_literal
        // fn f() { S::<-1> }
        T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
        k if k.is_literal() => const_arg(p),
        _ => {
            let m = p.start();
            types::type_(p);
            m.complete(p, TYPE_ARG);
        }
    }
}

pub(super) fn const_arg(p: &mut Parser) {
    let m = p.start();
    match p.current() {
        T!['{'] => {
            expressions::block_expr(p);
            m.complete(p, CONST_ARG);
        }
        k if k.is_literal() => {
            expressions::literal(p);
            m.complete(p, CONST_ARG);
        }
        T![true] | T![false] => {
            expressions::literal(p);
            m.complete(p, CONST_ARG);
        }
        T![-] => {
            let lm = p.start();
            p.bump(T![-]);
            expressions::literal(p);
            lm.complete(p, PREFIX_EXPR);
            m.complete(p, CONST_ARG);
        }
        _ => {
            let lm = p.start();
            paths::use_path(p);
            lm.complete(p, PATH_EXPR);
            m.complete(p, CONST_ARG);
        }
    }
}
<commit_msg>minor: improve readability<commit_after>use super::*;

pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
    let m;
    if p.at(T![::]) && p.nth(2) == T![<] {
        m = p.start();
        p.bump(T![::]);
        p.bump(T![<]);
    } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
        m = p.start();
        p.bump(T![<]);
    } else {
        return;
    }

    while !p.at(EOF) && !p.at(T![>]) {
        generic_arg(p);
        if !p.at(T![>]) && !p.expect(T![,]) {
            break;
        }
    }
    p.expect(T![>]);
    m.complete(p, GENERIC_ARG_LIST);
}

// test type_arg
// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
fn generic_arg(p: &mut Parser) {
    match p.current() {
        LIFETIME_IDENT => lifetime_arg(p),
        // test associated_type_bounds
        // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
        IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) => {
            let m = p.start();
            let path_ty = p.start();
            let path = p.start();
            let path_seg = p.start();
            name_ref(p);
            opt_generic_arg_list(p, false);
            match p.current() {
                // NameRef<...> =
                T![=] => {
                    p.bump_any();
                    types::type_(p);

                    path_seg.abandon(p);
                    path.abandon(p);
                    path_ty.abandon(p);
                    m.complete(p, ASSOC_TYPE_ARG);
                }
                T![:] if p.at(T![::]) => {
                    // NameRef::, this is a path type
                    path_seg.complete(p, PATH_SEGMENT);
                    let qual = path.complete(p, PATH);
                    opt_generic_arg_list(p, false);
                    paths::type_path_for_qualifier(p, qual);
                    path_ty.complete(p, PATH_TYPE);
                    m.complete(p, TYPE_ARG);
                }
                // NameRef<...>:
                T![:] => {
                    generic_params::bounds(p);

                    path_seg.abandon(p);
                    path.abandon(p);
                    path_ty.abandon(p);
                    m.complete(p, ASSOC_TYPE_ARG);
                }
                // NameRef, this is a single segment path type
                _ => {
                    path_seg.complete(p, PATH_SEGMENT);
                    path.complete(p, PATH);
                    path_ty.complete(p, PATH_TYPE);
                    m.complete(p, TYPE_ARG);
                }
            }
        }
        // test const_generic_negated_literal
        // fn f() { S::<-1> }
        T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
        k if k.is_literal() => const_arg(p),
        _ => type_arg(p),
    }
}

fn lifetime_arg(p: &mut Parser) {
    let m = p.start();
    lifetime(p);
    m.complete(p, LIFETIME_ARG);
}

pub(super) fn const_arg(p: &mut Parser) {
    let m = p.start();
    match p.current() {
        T!['{'] => {
            expressions::block_expr(p);
            m.complete(p, CONST_ARG);
        }
        k if k.is_literal() => {
            expressions::literal(p);
            m.complete(p, CONST_ARG);
        }
        T![true] | T![false] => {
            expressions::literal(p);
            m.complete(p, CONST_ARG);
        }
        T![-] => {
            let lm = p.start();
            p.bump(T![-]);
            expressions::literal(p);
            lm.complete(p, PREFIX_EXPR);
            m.complete(p, CONST_ARG);
        }
        _ => {
            let lm = p.start();
            paths::use_path(p);
            lm.complete(p, PATH_EXPR);
            m.complete(p, CONST_ARG);
        }
    }
}

fn type_arg(p: &mut Parser) {
    let m = p.start();
    types::type_(p);
    m.complete(p, TYPE_ARG);
}
<|endoftext|>


<commit_before>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{
    io,
    path::PathBuf,
    process::{Command, Stdio},
};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    pub(crate) outputs: ArenaMap<Package, BuildScriptOutput>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }
    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildScripts::default();
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();

        for package in workspace.packages() {
            res.outputs.insert(package, BuildScriptOutput::default());
            by_id.insert(workspace[package].id.clone(), package);
        }

        let mut cfg_err = None;
        let mut stderr = String::new();
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                if cfg_err.is_some() {
                    return;
                }

                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        cfg_err = Some(format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data = &mut res.outputs[package];
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !message.out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(message.out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = message.env;
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                res.outputs[package].proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                stderr.push_str(line);
                stderr.push('\n');
            },
        )?;

        for package in workspace.packages() {
            let package_build_data = &mut res.outputs[package];
            tracing::info!(
                "{} BuildScriptOutput: {:?}",
                workspace[package].manifest.parent().display(),
                package_build_data,
            );
            // inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        if let Some(cfg_err) = cfg_err {
            stderr.push_str(&cfg_err);
            stderr.push('\n');
        }

        if !output.status.success() {
            if stderr.is_empty() {
                stderr = "cargo check failed".to_string();
            }
            res.error = Some(stderr)
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>Don't swallow build script errors<commit_after>//! Workspace information we get from cargo consists of two pieces. The first is
//! the output of `cargo metadata`. The second is the output of running
//! `build.rs` files (`OUT_DIR` env var, extra cfg flags) and compiling proc
//! macro.
//!
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.

use std::{
    cell::RefCell,
    io,
    path::PathBuf,
    process::{Command, Stdio},
};

use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;

use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
    pub(crate) outputs: ArenaMap<Package, BuildScriptOutput>,
    error: Option<String>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct BuildScriptOutput {
    /// List of config flags defined by this package's build script.
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value.
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output.
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros.
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

impl WorkspaceBuildScripts {
    fn build_command(config: &CargoConfig) -> Command {
        if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
            let mut cmd = Command::new(program);
            cmd.args(args);
            return cmd;
        }

        let mut cmd = Command::new(toolchain::cargo());

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &config.target {
            cmd.args(&["--target", target]);
        }

        if config.all_features {
            cmd.arg("--all-features");
        } else {
            if config.no_default_features {
                cmd.arg("--no-default-features");
            }
            if !config.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(config.features.join(" "));
            }
        }

        cmd
    }
    pub(crate) fn run(
        config: &CargoConfig,
        workspace: &CargoWorkspace,
        progress: &dyn Fn(String),
    ) -> io::Result<WorkspaceBuildScripts> {
        let mut cmd = Self::build_command(config);

        if config.wrap_rustc_in_build_scripts {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.current_dir(workspace.workspace_root());

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildScripts::default();
        // NB: Cargo.toml could have been modified between `cargo metadata` and
        // `cargo check`. We shouldn't assume that package ids we see here are
        // exactly those from `config`.
        let mut by_id: FxHashMap<String, Package> = FxHashMap::default();

        for package in workspace.packages() {
            res.outputs.insert(package, BuildScriptOutput::default());
            by_id.insert(workspace[package].id.clone(), package);
        }

        let errors = RefCell::new(String::new());
        let push_err = |err: &str| {
            let mut e = errors.borrow_mut();
            e.push_str(err);
            e.push('\n');
        };
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using serde_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or_else(|_| Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(&it) => it,
                            None => return,
                        };
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in message.cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        push_err(&format!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data = &mut res.outputs[package];
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !message.out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(message.out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = message.env;
                    }
                    Message::CompilerArtifact(message) => {
                        let package = match by_id.get(&message.package_id.repr) {
                            Some(it) => *it,
                            None => return,
                        };

                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.iter().any(|k| k == "proc-macro") {
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                res.outputs[package].proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name);

                        if let Some(diag) = message.message.rendered.as_deref() {
                            push_err(diag);
                        }
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |line| {
                push_err(line);
            },
        )?;

        for package in workspace.packages() {
            let package_build_data = &mut res.outputs[package];
            tracing::info!(
                "{} BuildScriptOutput: {:?}",
                workspace[package].manifest.parent().display(),
                package_build_data,
            );
            // inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        let mut errors = errors.into_inner();
        if !output.status.success() {
            if errors.is_empty() {
                errors = "cargo check failed".to_string();
            }
            res.error = Some(errors);
        }

        Ok(res)
    }

    pub fn error(&self) -> Option<&str> {
        self.error.as_deref()
    }
}

// FIXME: File a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
    match path.extension().map(|e| e.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>use super::*;

#[test]
fn macro_rules_are_globally_visible() {
    let map = def_map(
        "
        //- /lib.rs
        macro_rules! structs {
            ($($i:ident),*) => {
                $(struct $i { field: u32 } )*
            }
        }
        structs!(Foo);
        mod nested;

        //- /nested.rs
        structs!(Bar, Baz);
        ",
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Foo: t v
   ⋮nested: t
   ⋮structs: m
   ⋮
   ⋮crate::nested
   ⋮Bar: t v
   ⋮Baz: t v
    "###);
}

#[test]
fn macro_rules_can_define_modules() {
    let map = def_map(
        "
        //- /lib.rs
        macro_rules! m {
            ($name:ident) => { mod $name;  }
        }
        m!(n1);

        //- /n1.rs
        m!(n2)
        //- /n1/n2.rs
        struct X;
        ",
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮m: m
   ⋮n1: t
   ⋮
   ⋮crate::n1
   ⋮n2: t
   ⋮
   ⋮crate::n1::n2
   ⋮X: t v
    "###);
}

#[test]
fn macro_rules_from_other_crates_are_visible() {
    let map = def_map_with_crate_graph(
        "
        //- /main.rs
        foo::structs!(Foo, Bar)
        mod bar;

        //- /bar.rs
        use crate::*;

        //- /lib.rs
        #[macro_export]
        macro_rules! structs {
            ($($i:ident),*) => {
                $(struct $i { field: u32 } )*
            }
        }
        ",
        crate_graph! {
            "main": ("/main.rs", ["foo"]),
            "foo": ("/lib.rs", []),
        },
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
   ⋮
   ⋮crate::bar
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
    "###);
}

#[test]
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
    let map = def_map_with_crate_graph(
        "
        //- /main.rs
        macro_rules! baz {
            () => {
                use foo::bar;
            }
        }

        foo!();
        bar!();
        baz!();

        //- /lib.rs
        #[macro_export]
        macro_rules! foo {
            () => {
                struct Foo { field: u32 }
            }
        }
        #[macro_export]
        macro_rules! bar {
            () => {
                use foo::foo;
            }
        }
        ",
        crate_graph! {
            "main": ("/main.rs", ["foo"]),
            "foo": ("/lib.rs", []),
        },
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Foo: t v
   ⋮bar: m
   ⋮baz: m
   ⋮foo: m
    "###);
}
<commit_msg>Add test<commit_after>use super::*;

#[test]
fn macro_rules_are_globally_visible() {
    let map = def_map(
        "
        //- /lib.rs
        macro_rules! structs {
            ($($i:ident),*) => {
                $(struct $i { field: u32 } )*
            }
        }
        structs!(Foo);
        mod nested;

        //- /nested.rs
        structs!(Bar, Baz);
        ",
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Foo: t v
   ⋮nested: t
   ⋮structs: m
   ⋮
   ⋮crate::nested
   ⋮Bar: t v
   ⋮Baz: t v
    "###);
}

#[test]
fn macro_rules_can_define_modules() {
    let map = def_map(
        "
        //- /lib.rs
        macro_rules! m {
            ($name:ident) => { mod $name;  }
        }
        m!(n1);

        //- /n1.rs
        m!(n2)
        //- /n1/n2.rs
        struct X;
        ",
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮m: m
   ⋮n1: t
   ⋮
   ⋮crate::n1
   ⋮n2: t
   ⋮
   ⋮crate::n1::n2
   ⋮X: t v
    "###);
}

#[test]
fn macro_rules_from_other_crates_are_visible() {
    let map = def_map_with_crate_graph(
        "
        //- /main.rs
        foo::structs!(Foo, Bar)
        mod bar;

        //- /bar.rs
        use crate::*;

        //- /lib.rs
        #[macro_export]
        macro_rules! structs {
            ($($i:ident),*) => {
                $(struct $i { field: u32 } )*
            }
        }
        ",
        crate_graph! {
            "main": ("/main.rs", ["foo"]),
            "foo": ("/lib.rs", []),
        },
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
   ⋮
   ⋮crate::bar
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
    "###);
}

#[test]
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
    let map = def_map_with_crate_graph(
        "
        //- /main.rs
        macro_rules! baz {
            () => {
                use foo::bar;
            }
        }

        foo!();
        bar!();
        baz!();

        //- /lib.rs
        #[macro_export]
        macro_rules! foo {
            () => {
                struct Foo { field: u32 }
            }
        }
        #[macro_export]
        macro_rules! bar {
            () => {
                use foo::foo;
            }
        }
        ",
        crate_graph! {
            "main": ("/main.rs", ["foo"]),
            "foo": ("/lib.rs", []),
        },
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Foo: t v
   ⋮bar: m
   ⋮baz: m
   ⋮foo: m
    "###);
}

#[test]
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
    let map = def_map_with_crate_graph(
        "
        //- /main.rs
        #[macro_use]
        extern crate foo;

        structs!(Foo, Bar)

        mod bar;

        //- /bar.rs
        use crate::*;

        //- /lib.rs
        #[macro_export]
        macro_rules! structs {
            ($($i:ident),*) => {
                $(struct $i { field: u32 } )*
            }
        }
        ",
        crate_graph! {
            "main": ("/main.rs", ["foo"]),
            "foo": ("/lib.rs", []),
        },
    );
    assert_snapshot!(map, @r###"
   ⋮crate
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
   ⋮foo: t
   ⋮structs: m
   ⋮
   ⋮crate::bar
   ⋮Bar: t v
   ⋮Foo: t v
   ⋮bar: t
   ⋮foo: t
   ⋮structs: m
    "###);
}
<|endoftext|>