<commit_before>#![deny(warnings)]

extern crate coreutils;
extern crate extra;

use std::cell::Cell;
use std::collections::HashMap;
use std::env;
use std::io::{self, Stdout, Stderr,  BufRead, Read, Write};

use coreutils::ArgParser;
use extra::io::{fail, WriteExt};

static OK: i32                      = 0;
static INVALID_FLAG: i32            = 1;
static SEARCH_CANNOT_BE_EMPTY: i32  = 2;
static REPLACE_CANNOT_BE_EMPTY: i32 = 3;
static WRITE_ERROR: i32             = 4;
static DUMMY_RUN: i32               = 5;

static USAGE: &'static str = r#"usage: tr -c string1  string2
    tr -d string1
    tr -s string1 string2
"#;

const MAN_PAGE: &'static str = /* @MANSTART{tr} */ r#"NAME
    tr - translate characters

SYNOPSIS
    tr [ -cdst ] [ string1 [ string2 ] ]

DESCRIPTION
    Tr copies the standard input to the standard output with
    substitution or deletion of selected characters.  Input
    characters found in string1 are mapped into the correspond-
    ing characters of string2. When string2 is short it is pad-
    ded to the length of string1 by duplicating its last charac-
    ter.  Any combination of the options -cds may be used:

    --complement
    -c   complement the set of characters in string1 with
         respect to the universe of characters whose ASCII codes
         are 01 through 0377

    --delete
    -d   delete all input characters in string1

    --squeeze
    -s   squeeze all strings of repeated output characters that
         are in string2 to single characters

    --truncate
    -t   first truncate string1 to length of string2

    In either string the notation a-b means a range of charac-
    ters from a to b in increasing ASCII order.  The character
    `\' followed by 1, 2 or 3 octal digits stands for the char-
    acter whose ASCII code is given by those digits.  A `\' fol-
    lowed by any other character stands for that character.

    The following example creates a list of all the words in
    `file1' one per line in `file2', where a word is taken to be
    a maximal string of alphabetics.  The second string is
    quoted to protect `\' from the Shell.  012 is the ASCII code
    for newline.

        tr -cs A-Za-z '\012' <file1 >file2

SEE ALSO
    ed(1), ascii(7)

BUGS
    loads.
"#; /* @MANEND */

struct Translation {
    complement:  bool,
    delete:      bool,
    squeeze:     bool,
    truncate:    bool,
    search:      String,
    replace:     String,
    status:      Cell<i32>
}

impl Translation {

    fn print_opts(&self) {
        println!("flags\ncompliment:\t{}\ndelete:\t{}\nsqueeze:\t{}\ntruncate:\t{}", self.complement, self.delete, self.squeeze, self.truncate);
        println!("search: {}", self.search);
        println!("replace: {}", self.replace);
    }

    fn truncate(&mut self, input: String, length: usize) -> String {
        // use an iterator just in case we have diacretes or other complex chars
        let mut new_string = "".to_string();
        {
            let mut char_walker = input.chars();
            for _ in 0 .. length {
                let last_char = char_walker.next().unwrap();
                new_string.push(last_char);
            }
        }
        return new_string;
    }

    fn append_or_truncate(&mut self) -> &mut Translation {
        // first decide
        let search_length = self.search.chars().count();
        let replace_length = self.replace.chars().count();

        if replace_length > 0 && replace_length < search_length {
            //build adjust search or replace?
            if self.truncate {
                let old_value = self.search.clone();
                self.search = self.truncate(old_value, replace_length);
            } else {
                // fill replace with it's last char to match search in length
                let lastchar_as_string = self.replace.chars().last().unwrap();
                for _ in replace_length .. search_length {
                    self.replace.push(lastchar_as_string); // do something
                }
            }
        } else if replace_length > search_length {
            // truncate replaces length to search'
            let old_value = self.replace.clone();
            self.replace = self.truncate(old_value, search_length);
        }
        return self;
    }

    fn make_map(&mut self) -> HashMap<char, char> {
        // prereq is that search and replace are now the same length
        return self.search.chars().zip(self.replace.chars()).collect();
    }

    fn get_opts(&mut self, stdout: &mut Stdout, mut stderr: &mut Stderr) -> &mut Translation {
        let mut parser = ArgParser::new(2)
            .add_flag("c", "complement")
            .add_flag("d", "delete")
            .add_flag("s", "squeeze")
            .add_flag("t", "truncate")
            .add_flag("h", "help");
        parser.parse(env::args());
        if let Err(err) = parser.found_invalid() {
            let _ = stderr.write(err.as_bytes());
            self.status.set(INVALID_FLAG);
        } else {
            if parser.found(&'h') || parser.found("help") {
                self.status.set(DUMMY_RUN);
            }
            self.complement = parser.found(&'c') || parser.found("complement");
            self.delete = parser.found(&'d') || parser.found("delete");
            self.squeeze = parser.found(&'s') || parser.found("squeeze");
            self.truncate = parser.found(&'t') || parser.found("truncate");

            let mut iter = parser.args.iter();
            let mut next = iter.next();
            if next.is_some() {
                self.search = next.unwrap().clone();
                next = iter.next();
                if next.is_some() {
                    self.replace = next.unwrap().clone();
                }
            }
            if self.status.get() > OK {
                let _ = stdout.write(MAN_PAGE.as_bytes());
                self.print_opts();
            }
        }
        return self;
    }

    fn check_opts(&mut self) -> &mut Translation {
        if !self.delete && !self.squeeze && self.replace.len() == 0 {
            // big issue
            println!("replace string can not be empty when neither -s nor -d is specified.");
            self.status.set(REPLACE_CANNOT_BE_EMPTY);
        }
        if self.search.len() == 0 {
            println!("set of characters to replace is obligatory");
            self.status.set(SEARCH_CANNOT_BE_EMPTY);
        }
        if self.status.get() > OK {
            println!("{}", MAN_PAGE);
        }
        return self;
    }

    fn remove<R: Read, W: Write>(& mut self, input: R, output: W,) {
        // do the work
        let reader = io::BufReader::new(input);
        let mut writer = io::BufWriter::new(output);

        for line in reader.lines() {
            let line = line.unwrap();
            let chars = line.chars();
            // read a char
            for kar in chars {
                // if not in search => pass through
                if self.search.find(kar).is_none() {
                    if writer.write_char(kar).is_err() {
                        self.status.set(WRITE_ERROR);
                        return;
                    }
                }
            }

            if writer.write_char('\n').is_err() {
                self.status.set(WRITE_ERROR);
                return;
            }
        }
    }

    fn translate<R: Read, W: Write>(& mut self, input: R, output: W,) {
        // do the work
        let map = self.make_map();
        let reader = io::BufReader::new(input);
        let mut writer = io::BufWriter::new(output);

        for line in reader.lines() {
            let line = line.unwrap();
            let chars = line.chars();
            // read a char
            for kar in chars {
                // if not in search => pass through
                let mut output = kar;
                if map.get(&kar).is_some() {
                    output = *map.get(&kar).unwrap();
                }
                if writer.write_char(output).is_err() {
                    self.status.set(WRITE_ERROR);
                    return;
                }
            }
            if writer.write_char('\n').is_err() {
                self.status.set(WRITE_ERROR);
                return;
            }
        }
    }
}

fn main() {
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut stderr = io::stderr();
    let mut tr = Translation { complement: false, delete: false, squeeze: false, truncate: false, search: String::new(), replace: String::new(), status: Cell::new(OK)};
    tr.get_opts(&mut stdout,&mut stderr);
    if tr.status.get() == OK {
        tr.check_opts();
    }
    if tr.status.get() == OK {
        tr.append_or_truncate();
    }
    if tr.status.get() > OK {
        fail(USAGE, &mut stderr);
    }

    // if complement is turned on recreate 'search' to contain the complement of search
    if !tr.delete {
        tr.translate(stdin, &mut stdout);
    } else {
        tr.remove(stdin, &mut stdout);
    }
// decide what to do
// switching over:
// case 'squeeze' switched on
//
}

#[cfg(test)]
mod tests {
    use super::Translation;
    use super::OK;
    use std::cell::Cell;

    #[test]
    fn append_replace_when_it_is_short() {
        let mut tr = Translation {search: "abcde".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("xyzzz", tr.replace);
    }
    #[test]
    fn append_replace_when_it_is_long() {
        let mut tr = Translation {search: "a".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("x", tr.replace);
    }
    #[test]
    fn append_replace_when_it_is_exact_in_length() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("xyz", tr.replace);
    }
    #[test]
    fn make_and_check_map() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        // precondition is that append_or_truncate is called so search and replace have the same length
        let map = tr.make_map();
        assert_eq!(3, map.len());
        assert_eq!(3, map.keys().count());
        assert_eq!(3, map.values().count());
        assert_eq!(Some(&'x'), map.get(&'a'));
        assert_eq!(Some(&'y'), map.get(&'b'));
        assert_eq!(Some(&'z'), map.get(&'c'));
        assert_eq!(None, map.get(&'d'));
    }
    #[test]
    fn make_and_check_maptranslate() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        // precondition is that append_or_truncate is called so search and replace have the same length
        let map = tr.make_map();
        assert_eq!(3, map.len());
        assert_eq!(3, map.keys().count());
        assert_eq!(3, map.values().count());
        assert_eq!(Some(&'x'), map.get(&'a'));
        assert_eq!(Some(&'y'), map.get(&'b'));
        assert_eq!(Some(&'z'), map.get(&'c'));
        assert_eq!(None, map.get(&'d'));
    }
}
<commit_msg>honour the complement flag (#108)<commit_after>#![deny(warnings)]

extern crate coreutils;
extern crate extra;

use std::cell::Cell;
use std::collections::HashMap;
use std::env;
use std::io::{self, Stdout, Stderr,  BufRead, Read, Write};

use coreutils::ArgParser;
use extra::io::{fail, WriteExt};

static OK: i32                      = 0;
static INVALID_FLAG: i32            = 1;
static SEARCH_CANNOT_BE_EMPTY: i32  = 2;
static REPLACE_CANNOT_BE_EMPTY: i32 = 3;
static WRITE_ERROR: i32             = 4;
static DUMMY_RUN: i32               = 5;

static USAGE: &'static str = r#"usage: tr -c string1  string2
    tr -d string1
    tr -s string1 string2
"#;

const MAN_PAGE: &'static str = /* @MANSTART{tr} */ r#"NAME
    tr - translate characters

SYNOPSIS
    tr [ -cdst ] [ string1 [ string2 ] ]

DESCRIPTION
    Tr copies the standard input to the standard output with
    substitution or deletion of selected characters.  Input
    characters found in string1 are mapped into the correspond-
    ing characters of string2. When string2 is short it is pad-
    ded to the length of string1 by duplicating its last charac-
    ter.  Any combination of the options -cds may be used:

    --complement
    -c   complement the set of characters in string1 will be
         transposed. only the first character in string2 will be
         used in this case.

    --delete
    -d   delete all input characters in string1

    *NOTE* squeeze is not implemented yet

    --squeeze
    -s   squeeze all strings of repeated output characters that
         are in string2 to single characters

    --truncate
    -t   first truncate string1 to length of string2

    *NOTE* ranges are not implemented yet

    In either string the notation a-b means a range of charac-
    ters from a to b in increasing ASCII order.  The character
    `\' followed by 1, 2 or 3 octal digits stands for the char-
    acter whose ASCII code is given by those digits.  A `\' fol-
    lowed by any other character stands for that character.

    The following example creates a list of all the words in
    `file1' one per line in `file2', where a word is taken to be
    a maximal string of alphabetics.  The second string is
    quoted to protect `\' from the Shell.  012 is the ASCII code
    for newline.

        tr -cs A-Za-z '\012' <file1 >file2

SEE ALSO
    ed(1), ascii(7)

BUGS
    loads.
"#; /* @MANEND */

struct Translation {
    complement:  bool,
    delete:      bool,
    squeeze:     bool,
    truncate:    bool,
    search:      String,
    replace:     String,
    status:      Cell<i32>
}

impl Translation {

    fn print_opts(&self) {
        println!("flags\ncompliment:\t{}\ndelete:\t{}\nsqueeze:\t{}\ntruncate:\t{}", self.complement, self.delete, self.squeeze, self.truncate);
        println!("search: {}", self.search);
        println!("replace: {}", self.replace);
    }

    fn truncate(&mut self, input: String, length: usize) -> String {
        // use an iterator just in case we have diacretes or other complex chars
        let mut new_string = "".to_string();
        {
            let mut char_walker = input.chars();
            for _ in 0 .. length {
                let last_char = char_walker.next().unwrap();
                new_string.push(last_char);
            }
        }
        return new_string;
    }

    fn append_or_truncate(&mut self) -> &mut Translation {
        // first decide
        let search_length = self.search.chars().count();
        let replace_length = self.replace.chars().count();

        if replace_length > 0 && replace_length < search_length {
            //build adjust search or replace?
            if self.truncate {
                let old_value = self.search.clone();
                self.search = self.truncate(old_value, replace_length);
            } else {
                // fill replace with it's last char to match search in length
                let lastchar_as_string = self.replace.chars().last().unwrap();
                for _ in replace_length .. search_length {
                    self.replace.push(lastchar_as_string); // do something
                }
            }
        } else if replace_length > search_length {
            // truncate replaces length to search'
            let old_value = self.replace.clone();
            self.replace = self.truncate(old_value, search_length);
        }
        return self;
    }

    fn get_opts(&mut self, stdout: &mut Stdout, mut stderr: &mut Stderr) -> &mut Translation {
        let mut parser = ArgParser::new(2)
            .add_flag("c", "complement")
            .add_flag("d", "delete")
            .add_flag("s", "squeeze")
            .add_flag("t", "truncate")
            .add_flag("h", "help");
        parser.parse(env::args());
        if let Err(err) = parser.found_invalid() {
            let _ = stderr.write(err.as_bytes());
            self.status.set(INVALID_FLAG);
        } else {
            if parser.found(&'h') || parser.found("help") {
                self.status.set(DUMMY_RUN);
            }
            self.complement = parser.found(&'c') || parser.found("complement");
            self.delete = parser.found(&'d') || parser.found("delete");
            self.squeeze = parser.found(&'s') || parser.found("squeeze");
            self.truncate = parser.found(&'t') || parser.found("truncate");

            let mut iter = parser.args.iter();
            let mut next = iter.next();
            if next.is_some() {
                self.search = next.unwrap().clone();
                next = iter.next();
                if next.is_some() {
                    self.replace = next.unwrap().clone();
                }
            }
            if self.status.get() > OK {
                let _ = stdout.write(MAN_PAGE.as_bytes());
                self.print_opts();
            }
        }
        return self;
    }

    fn check_opts(&mut self) -> &mut Translation {
        if !self.delete && !self.squeeze && self.replace.len() == 0 {
            // big issue
            println!("replace string can not be empty when neither -s nor -d is specified.");
            self.status.set(REPLACE_CANNOT_BE_EMPTY);
        }
        if self.search.len() == 0 {
            println!("set of characters to replace is obligatory");
            self.status.set(SEARCH_CANNOT_BE_EMPTY);
        }
        if self.status.get() > OK {
            println!("{}", MAN_PAGE);
        }
        return self;
    }

    fn make_map(&mut self) -> HashMap<char, char> {
        // prereq is that search and replace are now the same length
        return self.search.chars().zip(self.replace.chars()).collect();
    }

    fn delete_char_if_needed(&mut self, kar: char) -> Option<char> {
        return if self.search.find(kar).is_some() {
            None
        } else {
            Some(kar)
        }
    }
    fn replace_char_if_needed(&mut self, kar: char, map: &HashMap<char,char>) -> Option<char> {
        let complement_replacement = self.replace.chars().nth(0).unwrap();
        return if map.get(&kar).is_some() {
            if self.complement {
                Some(kar)
            } else {
                Some(*map.get(&kar).unwrap())
            }
        } else {
            if self.complement {
                Some(complement_replacement)
            } else {
                Some(kar)
            }
        }
    }

    fn translate<R: Read, W: Write>(& mut self, input: R, output: W,) {
        // do the work

        let mut map = if self.delete {HashMap::new()} else {self.make_map()};
        let reader = io::BufReader::new(input);
        let mut writer = io::BufWriter::new(output);

        for line in reader.lines() {
            let line = line.unwrap();
            let chars = line.chars();
            // read a char
            for kar in chars {
                // if not in search => pass through
                // case 'squeeze' switched on
                //
                let output = if self.delete {
                    self.delete_char_if_needed(kar)
                } else {
                    self.replace_char_if_needed(kar, &mut map)
                };
                if output.is_some() {
                    if writer.write_char(output.unwrap()).is_err() {
                         self.status.set(WRITE_ERROR);
                    }
                }
                if self.status.get() > OK {
                    return;
                }
            }
            if writer.write_char('\n').is_err() {
                self.status.set(WRITE_ERROR);
                return;
            }
        }
    }
}

fn main() {
    let stdin = io::stdin();
    let mut stdout = io::stdout();
    let mut stderr = io::stderr();
    let mut tr = Translation { complement: false, delete: false, squeeze: false, truncate: false, search: String::new(), replace: String::new(), status: Cell::new(OK)};
    tr.get_opts(&mut stdout,&mut stderr);
    if tr.status.get() == OK {
        tr.check_opts();
    }
    if tr.status.get() == OK {
        tr.append_or_truncate();
    }
    if tr.status.get() > OK {
        fail(USAGE, &mut stderr);
    }

    // if complement is turned on recreate 'search' to contain the complement of search
    tr.translate(stdin, &mut stdout);
}

#[cfg(test)]
mod tests {
    use super::Translation;
    use super::OK;
    use std::cell::Cell;

    #[test]
    fn append_replace_when_it_is_short() {
        let mut tr = Translation {search: "abcde".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("xyzzz", tr.replace);
    }
    #[test]
    fn append_replace_when_it_is_long() {
        let mut tr = Translation {search: "a".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("x", tr.replace);
    }
    #[test]
    fn append_replace_when_it_is_exact_in_length() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        tr.append_or_truncate();
        assert_eq!("xyz", tr.replace);
    }
    #[test]
    fn make_and_check_map() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        // precondition is that append_or_truncate is called so search and replace have the same length
        let map = tr.make_map();
        assert_eq!(3, map.len());
        assert_eq!(3, map.keys().count());
        assert_eq!(3, map.values().count());
        assert_eq!(Some(&'x'), map.get(&'a'));
        assert_eq!(Some(&'y'), map.get(&'b'));
        assert_eq!(Some(&'z'), map.get(&'c'));
        assert_eq!(None, map.get(&'d'));
    }
    #[test]
    fn make_and_check_maptranslate() {
        let mut tr = Translation {search: "abc".to_string() , replace: "xyz".to_string(), complement: false, delete: false, squeeze: false, truncate: false, status: Cell::new(OK)};
        // precondition is that append_or_truncate is called so search and replace have the same length
        let map = tr.make_map();
        assert_eq!(3, map.len());
        assert_eq!(3, map.keys().count());
        assert_eq!(3, map.values().count());
        assert_eq!(Some(&'x'), map.get(&'a'));
        assert_eq!(Some(&'y'), map.get(&'b'));
        assert_eq!(Some(&'z'), map.get(&'c'));
        assert_eq!(None, map.get(&'d'));
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate coreutils;

use std::env;
use std::fs::File;
use std::io::{self, stdout, stderr, Read, Write, Stderr};
use std::iter;
use std::ops::{Add, AddAssign};
use std::process::exit;

use coreutils::extra::{OptionalExt, WriteExt};

static MAN_PAGE: &'static str = r#"
    NAME
        wc - count words, bytes and lines of a file or byte stream.
    SYNOPSIS
        wc [-h | --help] [-c | --bytes] [-w | --words] [-l | --lines] [FILE 1] [FILE 2]...
    DESCRIPTION
        This utility will dump word count, line count, and byte count of a file or byte stream. If multiple files are given, it will print a total count. If no file name is given, 'wc' reads from stdin until EOF.

        Flags can be arbitrarily combined into a single flag, for example, '-c -w' (count bytes and count words) can be reduced to '-cw'.

        If no flags are given, 'wc' defaults to '-cwl'.
    OPTIONS
        -h
        --help
            Print this manual page.
        -c
        --bytes
            Count bytes.
        -w
        --words
            Count words (i.e. contiguous strings seperated by whitespaces).
        -l
        --lines
            Count lines (seperated by NL).
    AUTHOR
        This program was written mainly by Alice Maz.
"#;

#[derive(Copy, Clone, Default)]
struct Counter {
    lines: u64,
    words: u64,
    bytes: u64,
}

impl Counter {
    #[inline]
    fn new<T: Read>(input: T) -> Self {
        let mut line_count = 0;
        let mut word_count = 0;
        let mut byte_count = 0;
        let mut got_space = true;

        for byte_result in input.bytes() {
            if let Ok(byte) = byte_result {
                if byte == b'\n' {
                    line_count += 1;
                }

                if is_whitespace(byte) {
                    got_space = true;
                } else if got_space {
                    got_space = false;
                    word_count += 1;
                }

                byte_count += 1;
            }
        }

        Counter { lines: line_count, words: word_count, bytes: byte_count }
    }
}

impl Add for Counter {
    type Output = Counter;

    fn add(self, other: Self) -> Self {
        Counter {
            lines: self.lines + other.lines,
            words: self.words + other.words,
            bytes: self.bytes + other.bytes
        }
    }
}

impl AddAssign for Counter {
    fn add_assign(&mut self, _rhs: Self) {
        self.lines += _rhs.lines;
        self.words += _rhs.words;
        self.bytes += _rhs.bytes;
    }
}

#[derive(Copy, Clone, Default)]
struct Flags {
    count_lines: bool,
    count_words: bool,
    count_bytes: bool,
}

fn u64_num_digits(val: u64) -> usize {
    (val as f32).log10() as usize + 1
}

impl Flags {
    fn print_counts<W: Write>(self, mut counts: Vec<(Counter, String)>, stdout: &mut W, stderr: &mut Stderr) {
        use std::cmp::max;

        let mut max_lines_digits = 0;
        let mut max_words_digits = 0;
        let mut max_bytes_digits = 0;

        for &mut (count, _) in &mut counts {
            if self.count_lines {
                max_lines_digits = max(max_lines_digits, u64_num_digits(count.lines));
            }
            if self.count_words {
                max_words_digits = max(max_words_digits, u64_num_digits(count.words));
            }
            if self.count_bytes {
                max_bytes_digits = max(max_bytes_digits, u64_num_digits(count.bytes));
            }
        }

        for &mut (count, ref mut path) in &mut counts {
            self.print_count(count, path,
                max_lines_digits - u64_num_digits(count.lines) + 1,
                max_words_digits - u64_num_digits(count.words) + 1,
                max_bytes_digits - u64_num_digits(count.bytes) + 1,
                stdout, stderr)
        }
    }

    fn print_count<'a, W: Write>(self, count: Counter, path: &'a str, lines_padding: usize, words_padding: usize, bytes_padding: usize, stdout: &mut W, stderr: &mut Stderr) {
        stdout.write(b"    ").try(stderr);

        if self.count_lines {
            stdout.write(count.lines.to_string().as_bytes()).try(stderr);
            for _ in 0..lines_padding {
                stdout.write(b" ").try(stderr);
            }
        }
        if self.count_words {
            stdout.write(count.words.to_string().as_bytes()).try(stderr);
            for _ in 0..words_padding {
                stdout.write(b" ").try(stderr);
            }
        }
        if self.count_bytes {
            stdout.write(count.bytes.to_string().as_bytes()).try(stderr);
            for _ in 0..bytes_padding {
                stdout.write(b" ").try(stderr);
            }
        }

        stdout.writeln(path.as_bytes()).try(&mut *stderr);
    }

    fn default_to(&mut self) {
        // Defaults to behavior of -lwc
        if !(self.count_lines || self.count_words || self.count_bytes) {
            self.count_lines = true;
            self.count_words = true;
            self.count_bytes = true;
        }
    }
}

fn main() {
    let mut opts = Flags::default();
    let mut first_file = String::new();
    let mut args = env::args().skip(1);
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    loop { // To avoid consumption of the iter, we use loop.
        let arg = if let Some(x) = args.next() { x } else { break };

        // TODO add -m, maybe add -L

        if arg.starts_with("-") {
            match arg.as_str() {
                "--lines" | "-l" => opts.count_lines = true,
                "--words" | "-w" => opts.count_words = true,
                "--bytes" | "-c" => opts.count_bytes = true,
                "--help"  | "-h" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                    return;
                },
                _ => {
                    stderr.write(b"error: unknown flag, ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    exit(1);
                },
            }
        } else { // This is a file name
            first_file = arg;
            break;
        }
    }

    opts.default_to();

    if first_file == "" {
        let stdin = io::stdin();
        let stdin = stdin.lock();

        opts.print_count(Counter::new(stdin), "stdin", 1, 1, 1, &mut stdout, &mut stderr);
    } else {
        let mut files = Vec::new();
        let mut total_count = Counter::default();
        let single_file = args.len() == 0;

        for path in iter::once(first_file).chain(args) {
            //TODO would be easy to use stdin for - but
            //that is probably something the shell should handle?
            //unix it's all just fds so it's whatever dunno here tho
            //(also - is specific to sh/bash fwiw).

            let file = File::open(&path).try(&mut stderr);
            let file_count = Counter::new(file);
            total_count += file_count;

            files.push((file_count, path.to_owned()));
        }

        if !single_file {
            files.push((total_count, "total".to_owned()));
        }

        opts.print_counts(files, &mut stdout, &mut stderr);
    }
}

fn is_whitespace(byte: u8) -> bool {
    //FIXME this works like iswspace w/ default C locale
    //but not good enough for en_US.UTF8 among others

    byte == b'\n' // newline
    || byte == b'\t' // tab
    || byte == b'\r' // cr
    || byte == 0xc // formfeed
    || byte == 0xb // vtab
    || byte == b' ' // space
}
<commit_msg>Fix potential crash in wc<commit_after>#![deny(warnings)]

extern crate coreutils;

use std::env;
use std::fs::File;
use std::io::{self, stdout, stderr, Read, Write, Stderr};
use std::iter;
use std::ops::{Add, AddAssign};
use std::process::exit;

use coreutils::extra::{OptionalExt, WriteExt};

static MAN_PAGE: &'static str = r#"
    NAME
        wc - count words, bytes and lines of a file or byte stream.
    SYNOPSIS
        wc [-h | --help] [-c | --bytes] [-w | --words] [-l | --lines] [FILE 1] [FILE 2]...
    DESCRIPTION
        This utility will dump word count, line count, and byte count of a file or byte stream. If multiple files are given, it will print a total count. If no file name is given, 'wc' reads from stdin until EOF.

        Flags can be arbitrarily combined into a single flag, for example, '-c -w' (count bytes and count words) can be reduced to '-cw'.

        If no flags are given, 'wc' defaults to '-cwl'.
    OPTIONS
        -h
        --help
            Print this manual page.
        -c
        --bytes
            Count bytes.
        -w
        --words
            Count words (i.e. contiguous strings seperated by whitespaces).
        -l
        --lines
            Count lines (seperated by NL).
    AUTHOR
        This program was written mainly by Alice Maz.
"#;

#[derive(Copy, Clone, Default)]
struct Counter {
    lines: u64,
    words: u64,
    bytes: u64,
}

impl Counter {
    #[inline]
    fn new<T: Read>(input: T) -> Self {
        let mut line_count = 0;
        let mut word_count = 0;
        let mut byte_count = 0;
        let mut got_space = true;

        for byte_result in input.bytes() {
            if let Ok(byte) = byte_result {
                if byte == b'\n' {
                    line_count += 1;
                }

                if is_whitespace(byte) {
                    got_space = true;
                } else if got_space {
                    got_space = false;
                    word_count += 1;
                }

                byte_count += 1;
            }
        }

        Counter { lines: line_count, words: word_count, bytes: byte_count }
    }
}

impl Add for Counter {
    type Output = Counter;

    fn add(self, other: Self) -> Self {
        Counter {
            lines: self.lines + other.lines,
            words: self.words + other.words,
            bytes: self.bytes + other.bytes
        }
    }
}

impl AddAssign for Counter {
    fn add_assign(&mut self, _rhs: Self) {
        self.lines += _rhs.lines;
        self.words += _rhs.words;
        self.bytes += _rhs.bytes;
    }
}

#[derive(Copy, Clone, Default)]
struct Flags {
    count_lines: bool,
    count_words: bool,
    count_bytes: bool,
}

fn u64_num_digits(val: u64) -> usize {
    if val == 0 {
        1
    } else {
        (val as f32).log10() as usize + 1
    }
}

impl Flags {
    fn print_counts<W: Write>(self, mut counts: Vec<(Counter, String)>, stdout: &mut W, stderr: &mut Stderr) {
        use std::cmp::max;

        let mut max_lines_digits = 0;
        let mut max_words_digits = 0;
        let mut max_bytes_digits = 0;

        for &mut (count, _) in &mut counts {
            if self.count_lines {
                max_lines_digits = max(max_lines_digits, u64_num_digits(count.lines));
            }
            if self.count_words {
                max_words_digits = max(max_words_digits, u64_num_digits(count.words));
            }
            if self.count_bytes {
                max_bytes_digits = max(max_bytes_digits, u64_num_digits(count.bytes));
            }
        }

        for &mut (count, ref mut path) in &mut counts {
            self.print_count(count, path,
                max_lines_digits - u64_num_digits(count.lines) + 1,
                max_words_digits - u64_num_digits(count.words) + 1,
                max_bytes_digits - u64_num_digits(count.bytes) + 1,
                stdout, stderr)
        }
    }

    fn print_count<'a, W: Write>(self, count: Counter, path: &'a str, lines_padding: usize, words_padding: usize, bytes_padding: usize, stdout: &mut W, stderr: &mut Stderr) {
        stdout.write(b"    ").try(stderr);

        if self.count_lines {
            stdout.write(count.lines.to_string().as_bytes()).try(stderr);
            for _ in 0..lines_padding {
                stdout.write(b" ").try(stderr);
            }
        }
        if self.count_words {
            stdout.write(count.words.to_string().as_bytes()).try(stderr);
            for _ in 0..words_padding {
                stdout.write(b" ").try(stderr);
            }
        }
        if self.count_bytes {
            stdout.write(count.bytes.to_string().as_bytes()).try(stderr);
            for _ in 0..bytes_padding {
                stdout.write(b" ").try(stderr);
            }
        }

        stdout.writeln(path.as_bytes()).try(&mut *stderr);
    }

    fn default_to(&mut self) {
        // Defaults to behavior of -lwc
        if !(self.count_lines || self.count_words || self.count_bytes) {
            self.count_lines = true;
            self.count_words = true;
            self.count_bytes = true;
        }
    }
}

fn main() {
    let mut opts = Flags::default();
    let mut first_file = String::new();
    let mut args = env::args().skip(1);
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    loop { // To avoid consumption of the iter, we use loop.
        let arg = if let Some(x) = args.next() { x } else { break };

        // TODO add -m, maybe add -L

        if arg.starts_with("-") {
            match arg.as_str() {
                "--lines" | "-l" => opts.count_lines = true,
                "--words" | "-w" => opts.count_words = true,
                "--bytes" | "-c" => opts.count_bytes = true,
                "--help"  | "-h" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                    return;
                },
                _ => {
                    stderr.write(b"error: unknown flag, ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.flush().try(&mut stderr);
                    exit(1);
                },
            }
        } else { // This is a file name
            first_file = arg;
            break;
        }
    }

    opts.default_to();

    if first_file == "" {
        let stdin = io::stdin();
        let stdin = stdin.lock();

        opts.print_count(Counter::new(stdin), "stdin", 1, 1, 1, &mut stdout, &mut stderr);
    } else {
        let mut files = Vec::new();
        let mut total_count = Counter::default();
        let single_file = args.len() == 0;

        for path in iter::once(first_file).chain(args) {
            //TODO would be easy to use stdin for - but
            //that is probably something the shell should handle?
            //unix it's all just fds so it's whatever dunno here tho
            //(also - is specific to sh/bash fwiw).

            let file = File::open(&path).try(&mut stderr);
            let file_count = Counter::new(file);
            total_count += file_count;

            files.push((file_count, path.to_owned()));
        }

        if !single_file {
            files.push((total_count, "total".to_owned()));
        }

        opts.print_counts(files, &mut stdout, &mut stderr);
    }
}

fn is_whitespace(byte: u8) -> bool {
    //FIXME this works like iswspace w/ default C locale
    //but not good enough for en_US.UTF8 among others

    byte == b'\n' // newline
    || byte == b'\t' // tab
    || byte == b'\r' // cr
    || byte == 0xc // formfeed
    || byte == 0xb // vtab
    || byte == b' ' // space
}
<|endoftext|>


<commit_before>use serde::de::{Deserialize, Deserializer};

use types::*;

/// This object represents an incoming update.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Update {
    /// The update‘s unique identifier. Update identifiers start from a certain
    /// positive number and increase sequentially.
    pub id: Integer,
    /// Kind of the incoming update.
    pub kind: UpdateKind,
}

/// Kind of the incoming update.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum UpdateKind {
    /// New incoming message of any kind — text, photo, sticker, etc.
    Message(Message),
    /// New version of a message that is known to the bot and was edited
    EditedMessage(Message),
    /// New incoming channel post of any kind — text, photo, sticker, etc.
    ChannelPost(ChannelPost),
    /// New version of a channel post that is known to the bot and was edited
    EditedChannelPost(ChannelPost),
    // InlineQuery(InlineQuery),
    // ChosenInlineResult(ChosenInlineResult),
    CallbackQuery(CallbackQuery),
    #[doc(hidden)]
    Unknown(RawUpdate),
}

impl<'de> Deserialize<'de> for Update {
    fn deserialize<D>(deserializer: D) -> Result<Update, D::Error>
        where D: Deserializer<'de>
    {
        let raw: RawUpdate = Deserialize::deserialize(deserializer)?;
        macro_rules! maybe_field {
            ($name:ident, $variant:ident) => {{
                if let Some(val) = raw.$name {
                    return Ok(Update {
                        id: raw.update_id,
                        kind: UpdateKind::$variant(val),
                    })
                }
            }}
        }

        maybe_field!(message, Message);
        maybe_field!(edited_message, EditedMessage);
        maybe_field!(channel_post, ChannelPost);
        maybe_field!(edited_channel_post, EditedChannelPost);
        // maybe_field!(inline_query, InlineQuery);
        // maybe_field!(chosen_inline_result, ChosenInlineResult);
         maybe_field!(callback_query, CallbackQuery);

        Ok(Update {
            id: raw.update_id,
            kind: UpdateKind::Unknown(raw),
        })
    }
}

/// This object represents an incoming update. Directly mapped.
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct RawUpdate {
    /// The update‘s unique identifier. Update identifiers start from a certain
    /// positive number and increase sequentially.
    pub update_id: Integer,
    /// New incoming message of any kind — text, photo, sticker, etc.
    pub message: Option<Message>,
    /// New version of a message that is known to the bot and was edited
    pub edited_message: Option<Message>,
    /// New incoming channel post of any kind — text, photo, sticker, etc.
    pub channel_post: Option<ChannelPost>,
    /// New version of a channel post that is known to the bot and was edited
    pub edited_channel_post: Option<ChannelPost>,
    // pub inline_query: Option<InlineQuery>,
    // pub chosen_inline_result: Option<ChosenInlineResult>,
     pub callback_query: Option<CallbackQuery>,
}
<commit_msg>fix: typo in update.rs<commit_after>use serde::de::{Deserialize, Deserializer};

use types::*;

/// This object represents an incoming update.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Update {
    /// The update‘s unique identifier. Update identifiers start from a certain
    /// positive number and increase sequentially.
    pub id: Integer,
    /// Kind of the incoming update.
    pub kind: UpdateKind,
}

/// Kind of the incoming update.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum UpdateKind {
    /// New incoming message of any kind — text, photo, sticker, etc.
    Message(Message),
    /// New version of a message that is known to the bot and was edited
    EditedMessage(Message),
    /// New incoming channel post of any kind — text, photo, sticker, etc.
    ChannelPost(ChannelPost),
    /// New version of a channel post that is known to the bot and was edited
    EditedChannelPost(ChannelPost),
    // InlineQuery(InlineQuery),
    // ChosenInlineResult(ChosenInlineResult),
    CallbackQuery(CallbackQuery),
    #[doc(hidden)]
    Unknown(RawUpdate),
}

impl<'de> Deserialize<'de> for Update {
    fn deserialize<D>(deserializer: D) -> Result<Update, D::Error>
        where D: Deserializer<'de>
    {
        let raw: RawUpdate = Deserialize::deserialize(deserializer)?;
        macro_rules! maybe_field {
            ($name:ident, $variant:ident) => {{
                if let Some(val) = raw.$name {
                    return Ok(Update {
                        id: raw.update_id,
                        kind: UpdateKind::$variant(val),
                    })
                }
            }}
        }

        maybe_field!(message, Message);
        maybe_field!(edited_message, EditedMessage);
        maybe_field!(channel_post, ChannelPost);
        maybe_field!(edited_channel_post, EditedChannelPost);
        // maybe_field!(inline_query, InlineQuery);
        // maybe_field!(chosen_inline_result, ChosenInlineResult);
         maybe_field!(callback_query, CallbackQuery);

        Ok(Update {
            id: raw.update_id,
            kind: UpdateKind::Unknown(raw),
        })
    }
}

/// This object represents an incoming update. Directly mapped.
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct RawUpdate {
    /// The update‘s unique identifier. Update identifiers start from a certain
    /// positive number and increase sequentially.
    pub update_id: Integer,
    /// New incoming message of any kind — text, photo, sticker, etc.
    pub message: Option<Message>,
    /// New version of a message that is known to the bot and was edited
    pub edited_message: Option<Message>,
    /// New incoming channel post of any kind — text, photo, sticker, etc.
    pub channel_post: Option<ChannelPost>,
    /// New version of a channel post that is known to the bot and was edited
    pub edited_channel_post: Option<ChannelPost>,
    // pub inline_query: Option<InlineQuery>,
    // pub chosen_inline_result: Option<ChosenInlineResult>,
    pub callback_query: Option<CallbackQuery>,
}
<|endoftext|>


<commit_before>use Delay;

use futures::{Future, Stream, Poll};

use std::time::{Instant, Duration};

/// A stream representing notifications at fixed interval
#[derive(Debug)]
pub struct Interval {
    /// Future that completes the next time the `Interval` yields a value.
    delay: Delay,

    /// The duration between values yielded by `Interval`.
    duration: Duration,
}

impl Interval {
    /// Create a new `Interval` that starts at `at` and yields every `duration`
    /// interval after that.
    ///
    /// The `duration` argument must be a non-zero duration.
    ///
    /// # Panics
    ///
    /// This function panics if `duration` is zero.
    pub fn new(at: Instant, duration: Duration) -> Interval {
        assert!(duration > Duration::new(0, 0), "`duration` must be non-zero.");

        Interval::new_with_delay(Delay::new(at), duration)
    }

    pub(crate) fn new_with_delay(delay: Delay, duration: Duration) -> Interval {
        Interval {
            delay,
            duration,
        }
    }
}

impl Stream for Interval {
    type Item = Instant;
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        // Wait for the delay to be done
        let _ = try_ready!(self.delay.poll());

        // Get the `now` by looking at the `delay` deadline
        let now = self.delay.deadline();

        // The next interval value is `duration` after the one that just
        // yielded.
        self.delay.reset(now + self.duration);

        // Return the current instant
        Ok(Some(now).into())
    }
}
<commit_msg>Add Interval::interval shortcut for a better usability (#492)<commit_after>use Delay;

use clock;

use futures::{Future, Stream, Poll};

use std::time::{Instant, Duration};

/// A stream representing notifications at fixed interval
#[derive(Debug)]
pub struct Interval {
    /// Future that completes the next time the `Interval` yields a value.
    delay: Delay,

    /// The duration between values yielded by `Interval`.
    duration: Duration,
}

impl Interval {
    /// Create a new `Interval` that starts at `at` and yields every `duration`
    /// interval after that.
    ///
    /// Note that when it starts, it produces item too.
    ///
    /// The `duration` argument must be a non-zero duration.
    ///
    /// # Panics
    ///
    /// This function panics if `duration` is zero.
    pub fn new(at: Instant, duration: Duration) -> Interval {
        assert!(duration > Duration::new(0, 0), "`duration` must be non-zero.");

        Interval::new_with_delay(Delay::new(at), duration)
    }

    /// Creates new `Interval` that yields with interval of `duration`.
    ///
    /// The function is shortcut for `Interval::new(Instant::now() + duration, duration)`.
    ///
    /// The `duration` argument must be a non-zero duration.
    ///
    /// # Panics
    ///
    /// This function panics if `duration` is zero.
    pub fn new_interval(duration: Duration) -> Interval {
        Interval::new(clock::now() + duration, duration)
    }

    pub(crate) fn new_with_delay(delay: Delay, duration: Duration) -> Interval {
        Interval {
            delay,
            duration,
        }
    }
}

impl Stream for Interval {
    type Item = Instant;
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        // Wait for the delay to be done
        let _ = try_ready!(self.delay.poll());

        // Get the `now` by looking at the `delay` deadline
        let now = self.delay.deadline();

        // The next interval value is `duration` after the one that just
        // yielded.
        self.delay.reset(now + self.duration);

        // Return the current instant
        Ok(Some(now).into())
    }
}
<|endoftext|>


<commit_before>use json;

#[cfg(feature = "import")]
use std::ops;

use {Document};

pub use json::buffer::Target;

/// A buffer points to binary data representing geometry, animations, or skins.
#[derive(Clone, Debug)]
pub struct Buffer<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::Buffer,
}

/// A view into a buffer generally representing a subset of the buffer.
#[derive(Clone, Debug)]
pub struct View<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::View,

    /// The parent `Buffer`.
    parent: Buffer<'a>,
}

/// Describes a buffer data source.
#[derive(Clone, Debug)]
pub enum Source<'a> {
    /// Buffer data is contained in the `BIN` section of binary glTF.
    Bin,

    /// Buffer data is contained in an external data source.
    Uri(&'a str),
}

/// Buffer data belonging to an imported glTF asset.
#[cfg(feature = "import")]
#[derive(Clone, Debug)]
pub struct Data(pub Vec<u8>);

#[cfg(feature = "import")]
impl ops::Deref for Data {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0.as_slice()
    }
}

impl<'a> Buffer<'a> {
    /// Constructs a `Buffer`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::Buffer,
    ) -> Self {
        Self {
            document: document,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the buffer data source.
    pub fn source(&self) -> Source<'a> {
        if let Some(uri) = self.json.uri.as_ref().map(String::as_str) {
            Source::Uri(uri)
        } else {
            Source::Bin
        }
    }

    /// The length of the buffer in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> View<'a> {
    /// Constructs a `View`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::View,
    ) -> Self {
        let parent = document.buffers().nth(json.buffer.value()).unwrap();
        Self {
            document,
            index,
            json,
            parent,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the parent `Buffer`.
    pub fn buffer(&self) -> Buffer<'a> {
        self.document.buffers().nth(self.json.buffer.value()).unwrap()
    }

    /// Returns the length of the buffer view in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Returns the offset into the parent buffer in bytes.
    pub fn offset(&self) -> usize {
        self.json.byte_offset.unwrap_or(0) as usize
    }

    /// Returns the stride in bytes between vertex attributes or other interleavable
    /// data. When `None`, data is assumed to be tightly packed.
    pub fn stride(&self) -> Option<usize> {
        self.json.byte_stride.map(|x| x.0 as usize)
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional target the buffer should be bound to.
    pub fn target(&self) -> Option<Target> {
        self.json.target.map(|target| target.unwrap())
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}
<commit_msg>Treat byte_stride = 0 same as no byte stride / tightly packed. Have found glTF meshes (on Google Poly) that specify 0 byte stride for index buffers causing debug_assert to be triggered on 0 byte stride.<commit_after>use json;

#[cfg(feature = "import")]
use std::ops;

use {Document};

pub use json::buffer::Target;

/// A buffer points to binary data representing geometry, animations, or skins.
#[derive(Clone, Debug)]
pub struct Buffer<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::Buffer,
}

/// A view into a buffer generally representing a subset of the buffer.
#[derive(Clone, Debug)]
pub struct View<'a> {
    /// The parent `Document` struct.
    document: &'a Document,

    /// The corresponding JSON index.
    index: usize,

    /// The corresponding JSON struct.
    json: &'a json::buffer::View,

    /// The parent `Buffer`.
    parent: Buffer<'a>,
}

/// Describes a buffer data source.
#[derive(Clone, Debug)]
pub enum Source<'a> {
    /// Buffer data is contained in the `BIN` section of binary glTF.
    Bin,

    /// Buffer data is contained in an external data source.
    Uri(&'a str),
}

/// Buffer data belonging to an imported glTF asset.
#[cfg(feature = "import")]
#[derive(Clone, Debug)]
pub struct Data(pub Vec<u8>);

#[cfg(feature = "import")]
impl ops::Deref for Data {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        self.0.as_slice()
    }
}

impl<'a> Buffer<'a> {
    /// Constructs a `Buffer`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::Buffer,
    ) -> Self {
        Self {
            document: document,
            index: index,
            json: json,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the buffer data source.
    pub fn source(&self) -> Source<'a> {
        if let Some(uri) = self.json.uri.as_ref().map(String::as_str) {
            Source::Uri(uri)
        } else {
            Source::Bin
        }
    }

    /// The length of the buffer in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}

impl<'a> View<'a> {
    /// Constructs a `View`.
    pub(crate) fn new(
        document: &'a Document,
        index: usize,
        json: &'a json::buffer::View,
    ) -> Self {
        let parent = document.buffers().nth(json.buffer.value()).unwrap();
        Self {
            document,
            index,
            json,
            parent,
        }
    }

    /// Returns the internal JSON index.
    pub fn index(&self) -> usize {
        self.index
    }

    /// Returns the parent `Buffer`.
    pub fn buffer(&self) -> Buffer<'a> {
        self.document.buffers().nth(self.json.buffer.value()).unwrap()
    }

    /// Returns the length of the buffer view in bytes.
    pub fn length(&self) -> usize {
        self.json.byte_length as usize
    }

    /// Returns the offset into the parent buffer in bytes.
    pub fn offset(&self) -> usize {
        self.json.byte_offset.unwrap_or(0) as usize
    }

    /// Returns the stride in bytes between vertex attributes or other interleavable
    /// data. When `None`, data is assumed to be tightly packed.
    pub fn stride(&self) -> Option<usize> {
        self.json.byte_stride.and_then(|x| {
                // Treat byte_stride == 0 same as not specifying stride.
                // This is technically a validation error, but best way we can handle it here
                if x.0 == 0 {
                    None
                } else {
                    Some(x.0 as usize)
                }
            })
    }

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    pub fn name(&self) -> Option<&str> {
        self.json.name.as_ref().map(String::as_str)
    }

    /// Optional target the buffer should be bound to.
    pub fn target(&self) -> Option<Target> {
        self.json.target.map(|target| target.unwrap())
    }

    /// Optional application specific data.
    pub fn extras(&self) -> &json::Extras {
        &self.json.extras
    }
}
<|endoftext|>


<commit_before>//! A stably addressed token buffer supporting efficient traversal based on a
//! cheaply copyable cursor.
//!
//! *This module is available only if Syn is built with the `"parsing"` feature.*

// This module is heavily commented as it contains most of the unsafe code in
// Syn, and caution should be used when editing it. The public-facing interface
// is 100% safe but the implementation is fragile internally.

#[cfg(all(
    not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
    feature = "proc-macro"
))]
use crate::proc_macro as pm;
use crate::Lifetime;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::marker::PhantomData;
use std::ptr;
use std::slice;

/// Internal type which is used instead of `TokenTree` to represent a token tree
/// within a `TokenBuffer`.
enum Entry {
    // Mimicking types from proc-macro.
    Group(Group, TokenBuffer),
    Ident(Ident),
    Punct(Punct),
    Literal(Literal),
    // End entries contain a raw pointer to the entry from the containing
    // token tree, or null if this is the outermost level.
    End(*const Entry),
}

/// A buffer that can be efficiently traversed multiple times, unlike
/// `TokenStream` which requires a deep copy in order to traverse more than
/// once.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct TokenBuffer {
    // NOTE: Do not implement clone on this - there are raw pointers inside
    // these entries which will be messed up. Moving the `TokenBuffer` itself is
    // safe as the data pointed to won't be moved.
    ptr: *const Entry,
    len: usize,
}

impl Drop for TokenBuffer {
    fn drop(&mut self) {
        unsafe {
            let slice = slice::from_raw_parts(self.ptr, self.len);
            let _ = Box::from_raw(slice as *const [Entry] as *mut [Entry]);
        }
    }
}

impl TokenBuffer {
    // NOTE: Do not mutate the Vec returned from this function once it returns;
    // the address of its backing memory must remain stable.
    fn inner_new(stream: TokenStream, up: *const Entry) -> TokenBuffer {
        // Build up the entries list, recording the locations of any Groups
        // in the list to be processed later.
        let mut entries = Vec::new();
        let mut seqs = Vec::new();
        for tt in stream {
            match tt {
                TokenTree::Ident(sym) => {
                    entries.push(Entry::Ident(sym));
                }
                TokenTree::Punct(op) => {
                    entries.push(Entry::Punct(op));
                }
                TokenTree::Literal(l) => {
                    entries.push(Entry::Literal(l));
                }
                TokenTree::Group(g) => {
                    // Record the index of the interesting entry, and store an
                    // `End(null)` there temporarially.
                    seqs.push((entries.len(), g));
                    entries.push(Entry::End(ptr::null()));
                }
            }
        }
        // Add an `End` entry to the end with a reference to the enclosing token
        // stream which was passed in.
        entries.push(Entry::End(up));

        // NOTE: This is done to ensure that we don't accidentally modify the
        // length of the backing buffer. The backing buffer must remain at a
        // constant address after this point, as we are going to store a raw
        // pointer into it.
        let mut entries = entries.into_boxed_slice();
        for (idx, group) in seqs {
            // We know that this index refers to one of the temporary
            // `End(null)` entries, and we know that the last entry is
            // `End(up)`, so the next index is also valid.
            let seq_up = &entries[idx + 1] as *const Entry;

            // The end entry stored at the end of this Entry::Group should
            // point to the Entry which follows the Group in the list.
            let inner = Self::inner_new(group.stream(), seq_up);
            entries[idx] = Entry::Group(group, inner);
        }

        let len = entries.len();
        let ptr = Box::into_raw(entries);
        TokenBuffer {
            ptr: ptr as *const Entry,
            len,
        }
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `TokenStream`.
    ///
    /// *This method is available only if Syn is built with both the `"parsing"` and
    /// `"proc-macro"` features.*
    #[cfg(all(
        not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
        feature = "proc-macro"
    ))]
    pub fn new(stream: pm::TokenStream) -> TokenBuffer {
        Self::new2(stream.into())
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `TokenStream`.
    pub fn new2(stream: TokenStream) -> TokenBuffer {
        Self::inner_new(stream, ptr::null())
    }

    /// Creates a cursor referencing the first token in the buffer and able to
    /// traverse until the end of the buffer.
    pub fn begin(&self) -> Cursor {
        unsafe { Cursor::create(self.ptr, self.ptr.add(self.len - 1)) }
    }
}

/// A cheaply copyable cursor into a `TokenBuffer`.
///
/// This cursor holds a shared reference into the immutable data which is used
/// internally to represent a `TokenStream`, and can be efficiently manipulated
/// and copied around.
///
/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer`
/// object and get a cursor to its first token with `begin()`.
///
/// Two cursors are equal if they have the same location in the same input
/// stream, and have the same scope.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct Cursor<'a> {
    // The current entry which the `Cursor` is pointing at.
    ptr: *const Entry,
    // This is the only `Entry::End(..)` object which this cursor is allowed to
    // point at. All other `End` objects are skipped over in `Cursor::create`.
    scope: *const Entry,
    // Cursor is covariant in 'a. This field ensures that our pointers are still
    // valid.
    marker: PhantomData<&'a Entry>,
}

impl<'a> Cursor<'a> {
    /// Creates a cursor referencing a static empty TokenStream.
    pub fn empty() -> Self {
        // It's safe in this situation for us to put an `Entry` object in global
        // storage, despite it not actually being safe to send across threads
        // (`Ident` is a reference into a thread-local table). This is because
        // this entry never includes a `Ident` object.
        //
        // This wrapper struct allows us to break the rules and put a `Sync`
        // object in global storage.
        struct UnsafeSyncEntry(Entry);
        unsafe impl Sync for UnsafeSyncEntry {}
        static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End(0 as *const Entry));

        Cursor {
            ptr: &EMPTY_ENTRY.0,
            scope: &EMPTY_ENTRY.0,
            marker: PhantomData,
        }
    }

    /// This create method intelligently exits non-explicitly-entered
    /// `None`-delimited scopes when the cursor reaches the end of them,
    /// allowing for them to be treated transparently.
    unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self {
        // NOTE: If we're looking at a `End(..)`, we want to advance the cursor
        // past it, unless `ptr == scope`, which means that we're at the edge of
        // our cursor's scope. We should only have `ptr != scope` at the exit
        // from None-delimited groups entered with `ignore_none`.
        while let Entry::End(exit) = *ptr {
            if ptr == scope {
                break;
            }
            ptr = exit;
        }

        Cursor {
            ptr,
            scope,
            marker: PhantomData,
        }
    }

    /// Get the current entry.
    fn entry(self) -> &'a Entry {
        unsafe { &*self.ptr }
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    unsafe fn bump(self) -> Cursor<'a> {
        Cursor::create(self.ptr.offset(1), self.scope)
    }

    /// While the cursor is looking at a `None`-delimited group, move it to look
    /// at the first token inside instead. If the group is empty, this will move
    /// the cursor past the `None`-delimited group.
    ///
    /// WARNING: This mutates its argument.
    fn ignore_none(&mut self) {
        while let Entry::Group(group, buf) = self.entry() {
            if group.delimiter() == Delimiter::None {
                // NOTE: We call `Cursor::create` here to make sure that
                // situations where we should immediately exit the span after
                // entering it are handled correctly.
                unsafe {
                    *self = Cursor::create(buf.ptr, self.scope);
                }
            } else {
                break;
            }
        }
    }

    /// Checks whether the cursor is currently pointing at the end of its valid
    /// scope.
    pub fn eof(self) -> bool {
        // We're at eof if we're at the end of our scope.
        self.ptr == self.scope
    }

    /// If the cursor is pointing at a `Group` with the given delimiter, returns
    /// a cursor into that group and one pointing to the next `TokenTree`.
    pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> {
        // If we're not trying to enter a none-delimited group, we want to
        // ignore them. We have to make sure to _not_ ignore them when we want
        // to enter them, of course. For obvious reasons.
        if delim != Delimiter::None {
            self.ignore_none();
        }

        if let Entry::Group(group, buf) = self.entry() {
            if group.delimiter() == delim {
                return Some((buf.begin(), group.span(), unsafe { self.bump() }));
            }
        }

        None
    }

    /// If the cursor is pointing at a `Ident`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at an `Punct`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(op) if op.as_char() != '\'' => Some((op.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Literal`, return it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Literal(lit) => Some((lit.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Lifetime`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    pub fn lifetime(mut self) -> Option<(Lifetime, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump() };
                match next.ident() {
                    Some((ident, rest)) => {
                        let lifetime = Lifetime {
                            apostrophe: op.span(),
                            ident,
                        };
                        Some((lifetime, rest))
                    }
                    None => None,
                }
            }
            _ => None,
        }
    }

    /// Copies all remaining tokens visible from this cursor into a
    /// `TokenStream`.
    pub fn token_stream(self) -> TokenStream {
        let mut tts = Vec::new();
        let mut cursor = self;
        while let Some((tt, rest)) = cursor.token_tree() {
            tts.push(tt);
            cursor = rest;
        }
        tts.into_iter().collect()
    }

    /// If the cursor is pointing at a `TokenTree`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    ///
    /// Returns `None` if the cursor has reached the end of its stream.
    ///
    /// This method does not treat `None`-delimited groups as transparent, and
    /// will return a `Group(None, ..)` if the cursor is looking at one.
    pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> {
        let tree = match self.entry() {
            Entry::Group(group, _) => group.clone().into(),
            Entry::Literal(lit) => lit.clone().into(),
            Entry::Ident(ident) => ident.clone().into(),
            Entry::Punct(op) => op.clone().into(),
            Entry::End(..) => {
                return None;
            }
        };

        Some((tree, unsafe { self.bump() }))
    }

    /// Returns the `Span` of the current token, or `Span::call_site()` if this
    /// cursor points to eof.
    pub fn span(self) -> Span {
        match self.entry() {
            Entry::Group(group, _) => group.span(),
            Entry::Literal(l) => l.span(),
            Entry::Ident(t) => t.span(),
            Entry::Punct(o) => o.span(),
            Entry::End(..) => Span::call_site(),
        }
    }

    /// Skip over the next token without cloning it. Returns `None` if this
    /// cursor points to eof.
    ///
    /// This method treats `'lifetimes` as a single token.
    pub(crate) fn skip(self) -> Option<Cursor<'a>> {
        match self.entry() {
            Entry::End(..) => None,

            // Treat lifetimes as a single tt for the purposes of 'skip'.
            Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump() };
                match next.entry() {
                    Entry::Ident(_) => Some(unsafe { next.bump() }),
                    _ => Some(next),
                }
            }
            _ => Some(unsafe { self.bump() }),
        }
    }
}

impl<'a> Copy for Cursor<'a> {}

impl<'a> Clone for Cursor<'a> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a> Eq for Cursor<'a> {}

impl<'a> PartialEq for Cursor<'a> {
    fn eq(&self, other: &Self) -> bool {
        let Cursor { ptr, scope, marker } = self;
        let _ = marker;
        *ptr == other.ptr && *scope == other.scope
    }
}

pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
    a.scope == b.scope
}

pub(crate) fn open_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_open(),
        _ => cursor.span(),
    }
}

pub(crate) fn close_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_close(),
        _ => cursor.span(),
    }
}
<commit_msg>Fix typo in a TokenBuffer comment<commit_after>//! A stably addressed token buffer supporting efficient traversal based on a
//! cheaply copyable cursor.
//!
//! *This module is available only if Syn is built with the `"parsing"` feature.*

// This module is heavily commented as it contains most of the unsafe code in
// Syn, and caution should be used when editing it. The public-facing interface
// is 100% safe but the implementation is fragile internally.

#[cfg(all(
    not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
    feature = "proc-macro"
))]
use crate::proc_macro as pm;
use crate::Lifetime;
use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
use std::marker::PhantomData;
use std::ptr;
use std::slice;

/// Internal type which is used instead of `TokenTree` to represent a token tree
/// within a `TokenBuffer`.
enum Entry {
    // Mimicking types from proc-macro.
    Group(Group, TokenBuffer),
    Ident(Ident),
    Punct(Punct),
    Literal(Literal),
    // End entries contain a raw pointer to the entry from the containing
    // token tree, or null if this is the outermost level.
    End(*const Entry),
}

/// A buffer that can be efficiently traversed multiple times, unlike
/// `TokenStream` which requires a deep copy in order to traverse more than
/// once.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct TokenBuffer {
    // NOTE: Do not implement clone on this - there are raw pointers inside
    // these entries which will be messed up. Moving the `TokenBuffer` itself is
    // safe as the data pointed to won't be moved.
    ptr: *const Entry,
    len: usize,
}

impl Drop for TokenBuffer {
    fn drop(&mut self) {
        unsafe {
            let slice = slice::from_raw_parts(self.ptr, self.len);
            let _ = Box::from_raw(slice as *const [Entry] as *mut [Entry]);
        }
    }
}

impl TokenBuffer {
    // NOTE: Do not mutate the Vec returned from this function once it returns;
    // the address of its backing memory must remain stable.
    fn inner_new(stream: TokenStream, up: *const Entry) -> TokenBuffer {
        // Build up the entries list, recording the locations of any Groups
        // in the list to be processed later.
        let mut entries = Vec::new();
        let mut seqs = Vec::new();
        for tt in stream {
            match tt {
                TokenTree::Ident(sym) => {
                    entries.push(Entry::Ident(sym));
                }
                TokenTree::Punct(op) => {
                    entries.push(Entry::Punct(op));
                }
                TokenTree::Literal(l) => {
                    entries.push(Entry::Literal(l));
                }
                TokenTree::Group(g) => {
                    // Record the index of the interesting entry, and store an
                    // `End(null)` there temporarily.
                    seqs.push((entries.len(), g));
                    entries.push(Entry::End(ptr::null()));
                }
            }
        }
        // Add an `End` entry to the end with a reference to the enclosing token
        // stream which was passed in.
        entries.push(Entry::End(up));

        // NOTE: This is done to ensure that we don't accidentally modify the
        // length of the backing buffer. The backing buffer must remain at a
        // constant address after this point, as we are going to store a raw
        // pointer into it.
        let mut entries = entries.into_boxed_slice();
        for (idx, group) in seqs {
            // We know that this index refers to one of the temporary
            // `End(null)` entries, and we know that the last entry is
            // `End(up)`, so the next index is also valid.
            let seq_up = &entries[idx + 1] as *const Entry;

            // The end entry stored at the end of this Entry::Group should
            // point to the Entry which follows the Group in the list.
            let inner = Self::inner_new(group.stream(), seq_up);
            entries[idx] = Entry::Group(group, inner);
        }

        let len = entries.len();
        let ptr = Box::into_raw(entries);
        TokenBuffer {
            ptr: ptr as *const Entry,
            len,
        }
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `TokenStream`.
    ///
    /// *This method is available only if Syn is built with both the `"parsing"` and
    /// `"proc-macro"` features.*
    #[cfg(all(
        not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))),
        feature = "proc-macro"
    ))]
    pub fn new(stream: pm::TokenStream) -> TokenBuffer {
        Self::new2(stream.into())
    }

    /// Creates a `TokenBuffer` containing all the tokens from the input
    /// `TokenStream`.
    pub fn new2(stream: TokenStream) -> TokenBuffer {
        Self::inner_new(stream, ptr::null())
    }

    /// Creates a cursor referencing the first token in the buffer and able to
    /// traverse until the end of the buffer.
    pub fn begin(&self) -> Cursor {
        unsafe { Cursor::create(self.ptr, self.ptr.add(self.len - 1)) }
    }
}

/// A cheaply copyable cursor into a `TokenBuffer`.
///
/// This cursor holds a shared reference into the immutable data which is used
/// internally to represent a `TokenStream`, and can be efficiently manipulated
/// and copied around.
///
/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer`
/// object and get a cursor to its first token with `begin()`.
///
/// Two cursors are equal if they have the same location in the same input
/// stream, and have the same scope.
///
/// *This type is available only if Syn is built with the `"parsing"` feature.*
pub struct Cursor<'a> {
    // The current entry which the `Cursor` is pointing at.
    ptr: *const Entry,
    // This is the only `Entry::End(..)` object which this cursor is allowed to
    // point at. All other `End` objects are skipped over in `Cursor::create`.
    scope: *const Entry,
    // Cursor is covariant in 'a. This field ensures that our pointers are still
    // valid.
    marker: PhantomData<&'a Entry>,
}

impl<'a> Cursor<'a> {
    /// Creates a cursor referencing a static empty TokenStream.
    pub fn empty() -> Self {
        // It's safe in this situation for us to put an `Entry` object in global
        // storage, despite it not actually being safe to send across threads
        // (`Ident` is a reference into a thread-local table). This is because
        // this entry never includes a `Ident` object.
        //
        // This wrapper struct allows us to break the rules and put a `Sync`
        // object in global storage.
        struct UnsafeSyncEntry(Entry);
        unsafe impl Sync for UnsafeSyncEntry {}
        static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End(0 as *const Entry));

        Cursor {
            ptr: &EMPTY_ENTRY.0,
            scope: &EMPTY_ENTRY.0,
            marker: PhantomData,
        }
    }

    /// This create method intelligently exits non-explicitly-entered
    /// `None`-delimited scopes when the cursor reaches the end of them,
    /// allowing for them to be treated transparently.
    unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self {
        // NOTE: If we're looking at a `End(..)`, we want to advance the cursor
        // past it, unless `ptr == scope`, which means that we're at the edge of
        // our cursor's scope. We should only have `ptr != scope` at the exit
        // from None-delimited groups entered with `ignore_none`.
        while let Entry::End(exit) = *ptr {
            if ptr == scope {
                break;
            }
            ptr = exit;
        }

        Cursor {
            ptr,
            scope,
            marker: PhantomData,
        }
    }

    /// Get the current entry.
    fn entry(self) -> &'a Entry {
        unsafe { &*self.ptr }
    }

    /// Bump the cursor to point at the next token after the current one. This
    /// is undefined behavior if the cursor is currently looking at an
    /// `Entry::End`.
    unsafe fn bump(self) -> Cursor<'a> {
        Cursor::create(self.ptr.offset(1), self.scope)
    }

    /// While the cursor is looking at a `None`-delimited group, move it to look
    /// at the first token inside instead. If the group is empty, this will move
    /// the cursor past the `None`-delimited group.
    ///
    /// WARNING: This mutates its argument.
    fn ignore_none(&mut self) {
        while let Entry::Group(group, buf) = self.entry() {
            if group.delimiter() == Delimiter::None {
                // NOTE: We call `Cursor::create` here to make sure that
                // situations where we should immediately exit the span after
                // entering it are handled correctly.
                unsafe {
                    *self = Cursor::create(buf.ptr, self.scope);
                }
            } else {
                break;
            }
        }
    }

    /// Checks whether the cursor is currently pointing at the end of its valid
    /// scope.
    pub fn eof(self) -> bool {
        // We're at eof if we're at the end of our scope.
        self.ptr == self.scope
    }

    /// If the cursor is pointing at a `Group` with the given delimiter, returns
    /// a cursor into that group and one pointing to the next `TokenTree`.
    pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> {
        // If we're not trying to enter a none-delimited group, we want to
        // ignore them. We have to make sure to _not_ ignore them when we want
        // to enter them, of course. For obvious reasons.
        if delim != Delimiter::None {
            self.ignore_none();
        }

        if let Entry::Group(group, buf) = self.entry() {
            if group.delimiter() == delim {
                return Some((buf.begin(), group.span(), unsafe { self.bump() }));
            }
        }

        None
    }

    /// If the cursor is pointing at a `Ident`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at an `Punct`, returns it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(op) if op.as_char() != '\'' => Some((op.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Literal`, return it along with a cursor
    /// pointing at the next `TokenTree`.
    pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Literal(lit) => Some((lit.clone(), unsafe { self.bump() })),
            _ => None,
        }
    }

    /// If the cursor is pointing at a `Lifetime`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    pub fn lifetime(mut self) -> Option<(Lifetime, Cursor<'a>)> {
        self.ignore_none();
        match self.entry() {
            Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump() };
                match next.ident() {
                    Some((ident, rest)) => {
                        let lifetime = Lifetime {
                            apostrophe: op.span(),
                            ident,
                        };
                        Some((lifetime, rest))
                    }
                    None => None,
                }
            }
            _ => None,
        }
    }

    /// Copies all remaining tokens visible from this cursor into a
    /// `TokenStream`.
    pub fn token_stream(self) -> TokenStream {
        let mut tts = Vec::new();
        let mut cursor = self;
        while let Some((tt, rest)) = cursor.token_tree() {
            tts.push(tt);
            cursor = rest;
        }
        tts.into_iter().collect()
    }

    /// If the cursor is pointing at a `TokenTree`, returns it along with a
    /// cursor pointing at the next `TokenTree`.
    ///
    /// Returns `None` if the cursor has reached the end of its stream.
    ///
    /// This method does not treat `None`-delimited groups as transparent, and
    /// will return a `Group(None, ..)` if the cursor is looking at one.
    pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> {
        let tree = match self.entry() {
            Entry::Group(group, _) => group.clone().into(),
            Entry::Literal(lit) => lit.clone().into(),
            Entry::Ident(ident) => ident.clone().into(),
            Entry::Punct(op) => op.clone().into(),
            Entry::End(..) => {
                return None;
            }
        };

        Some((tree, unsafe { self.bump() }))
    }

    /// Returns the `Span` of the current token, or `Span::call_site()` if this
    /// cursor points to eof.
    pub fn span(self) -> Span {
        match self.entry() {
            Entry::Group(group, _) => group.span(),
            Entry::Literal(l) => l.span(),
            Entry::Ident(t) => t.span(),
            Entry::Punct(o) => o.span(),
            Entry::End(..) => Span::call_site(),
        }
    }

    /// Skip over the next token without cloning it. Returns `None` if this
    /// cursor points to eof.
    ///
    /// This method treats `'lifetimes` as a single token.
    pub(crate) fn skip(self) -> Option<Cursor<'a>> {
        match self.entry() {
            Entry::End(..) => None,

            // Treat lifetimes as a single tt for the purposes of 'skip'.
            Entry::Punct(op) if op.as_char() == '\'' && op.spacing() == Spacing::Joint => {
                let next = unsafe { self.bump() };
                match next.entry() {
                    Entry::Ident(_) => Some(unsafe { next.bump() }),
                    _ => Some(next),
                }
            }
            _ => Some(unsafe { self.bump() }),
        }
    }
}

impl<'a> Copy for Cursor<'a> {}

impl<'a> Clone for Cursor<'a> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a> Eq for Cursor<'a> {}

impl<'a> PartialEq for Cursor<'a> {
    fn eq(&self, other: &Self) -> bool {
        let Cursor { ptr, scope, marker } = self;
        let _ = marker;
        *ptr == other.ptr && *scope == other.scope
    }
}

pub(crate) fn same_scope(a: Cursor, b: Cursor) -> bool {
    a.scope == b.scope
}

pub(crate) fn open_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_open(),
        _ => cursor.span(),
    }
}

pub(crate) fn close_span_of_group(cursor: Cursor) -> Span {
    match cursor.entry() {
        Entry::Group(group, _) => group.span_close(),
        _ => cursor.span(),
    }
}
<|endoftext|>