<commit_before>use std::io::Write;
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: self.line.1.clone(),
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // No transition, nothing to do
            (cur, next) if cur == next => {}

            // Beginning a new match or ending a previous one
            (_, MatchState::Before) | (MatchState::After, _) => {
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            self.display(m);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change()
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change()
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change()
                    }
                }
            };
            change_list.push(change);
        }

        self.apply_changes(path, &change_list.into_iter());

        println!("");
    }

    fn display(&self, m: &SearchMatch) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        if let Some(replacement) = self.replacer.replace(&m.line.1) {
            print!("+   {}: {}", m.line.0, replacement);
        }

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, changes: &dyn Iterator<Item = Change>) {}
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            // TODO: handle these somehow
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(1)
        .after_context(1)
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    for dir_entry in Walk::new("../") {
        let dir_entry = dir_entry.unwrap();
        let path = dir_entry.path();
        if !path.is_file() {
            continue;
        }

        let mut sink = SearchMatchCollector::new();

        searcher
            .search_path(&matcher, dir_entry.path(), &mut sink)
            .expect("search failed");

        let matches = sink.collect();
        proc.handle_path(path, matches);
    }

    println!("All done.");
}
<commit_msg>Support -A -B -C for context<commit_after>use std::cmp::max;
use std::io::Write;
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: self.line.1.clone(),
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        match (self.state, next) {
            // No transition, nothing to do
            (cur, next) if cur == next => {}

            // Beginning a new match or ending a previous one
            (_, MatchState::Before) | (MatchState::After, _) => {
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            self.display(m);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change()
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change()
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change()
                    }
                }
            };
            change_list.push(change);
        }

        self.apply_changes(path, &change_list.into_iter());

        println!("");
    }

    fn display(&self, m: &SearchMatch) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        if let Some(replacement) = self.replacer.replace(&m.line.1) {
            print!("+   {}: {}", m.line.0, replacement);
        }

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, changes: &dyn Iterator<Item = Change>) {}
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            // TODO: handle these somehow
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    for dir_entry in Walk::new("../") {
        let dir_entry = dir_entry.unwrap();
        let path = dir_entry.path();
        if !path.is_file() {
            continue;
        }

        let mut sink = SearchMatchCollector::new();

        searcher
            .search_path(&matcher, dir_entry.path(), &mut sink)
            .expect("search failed");

        let matches = sink.collect();
        proc.handle_path(path, matches);
    }

    println!("All done.");
}
<|endoftext|>


<commit_before>use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));
    println!("Generating file {:?}", path);

    save_to_file(path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_enums(w, &ns.name, &prepare(ns)));
    try!(generate_bitfields(w, &ns.name, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare_records(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));

    //TODO: other functions
    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn prepare_records(ns: &library::Namespace) -> Vec<&library::Record> {
    let mut vec = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(rec) = typ.maybe_ref_as::<library::Record>() {
            // We don't want the FooBarPrivate and similar records where FooBar is a type
            if ["Private", "Class", "Iface", "Interface"].iter()
                    .filter_map(|s| strip_suffix(&rec.name, s))
                    .any(|s| ns.index.get(s).is_some()) {
                continue;
            }
            vec.push(rec);
        }
    }
    vec.sort();
    vec
}

fn generate_bitfields<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: i32 {{", tabs(1), item.name));
        for member in &item.members {
            try!(writeln!(w, "{}const {} = {},",
                          tabs(2), strip_prefix(ns_name, &member.c_identifier), member.value));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.name));
        for member in &item.members {
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};",
                          strip_prefix(ns_name, &member.c_identifier),
                          item.name, &prepare_enum_member_name(&member.name)));
        }
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.glib_type_name));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.glib_type_name));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        let mut has_union = false;
        for field in &record.fields {
            if has_union {
                warn!("A record has fields after the union placeholder");
                lines.push(format!("{}// ignoring the fields after the union", tabs(1)));
                break;
            }
            if env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some() {
                lines.push(format!("{}_union_placeholder: (),", tabs(1)));
                has_union = true;
            }
            else if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{} {}: {},", tabs(1), "pub", name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{} {}: fn{},", tabs(1), "pub", name, sig));
                    commented |= com;
                }
                else {
                    lines.push(format!("{}{} {}: [{:?}],", tabs(1), "pub", name, field.typ));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.glib_type_name));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.glib_type_name));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<commit_msg>Auto merge of #57 - gkoz:enums, r=gkoz<commit_after>use std::collections::HashMap;
use std::path::*;
use std::io::{Result, Write};
use case::CaseExt;

use env::Env;
use file_saver::*;
use library;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use super::super::general::{self, tabs};
use traits::*;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  PathBuf::from(&env.config.target_path)
        .join(file_name_sys("lib"));
    println!("Generating file {:?}", path);

    save_to_file(path, &mut |w| generate_lib(w, env));
}

fn generate_lib<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib_ffi(w));
    } else {
        try!(statics::only_for_glib(w));
    }
    if env.config.library_name == "Gtk" {
        try!(statics::only_for_gtk(w));
    }

    let ns = env.library.namespace(library::MAIN_NAMESPACE);
    let classes = prepare(ns);
    let interfaces = prepare(ns);

    try!(generate_enums(w, &ns.name, &prepare(ns)));
    try!(generate_bitfields(w, &ns.name, &prepare(ns)));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &prepare_records(ns)));
    try!(generate_classes_structs(w, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, ""));
    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));

    //TODO: other functions
    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates<W: Write>(w: &mut W, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0}_ffi;", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &library::Namespace) -> Vec<&T>
where library::Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn prepare_records(ns: &library::Namespace) -> Vec<&library::Record> {
    let mut vec = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(rec) = typ.maybe_ref_as::<library::Record>() {
            // We don't want the FooBarPrivate and similar records where FooBar is a type
            if ["Private", "Class", "Iface", "Interface"].iter()
                    .filter_map(|s| strip_suffix(&rec.name, s))
                    .any(|s| ns.index.get(s).is_some()) {
                continue;
            }
            vec.push(rec);
        }
    }
    vec.sort();
    vec
}

fn generate_bitfields<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Bitfield])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        try!(writeln!(w, "bitflags! {{\n{}#[repr(C)]\n{0}flags {}: i32 {{", tabs(1), item.name));
        for member in &item.members {
            try!(writeln!(w, "{}const {} = {},",
                          tabs(2), strip_prefix(ns_name, &member.c_identifier), member.value));
        }
        try!(writeln!(w, "{}}}\n}}", tabs(1)));
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums<W: Write>(w: &mut W, ns_name: &str, items: &[&library::Enumeration])
        -> Result<()> {
    try!(writeln!(w, ""));
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = i32;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let mut vals = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.name));
        for member in &item.members {
            if vals.get(&member.value).is_some() {
                continue;
            }
            try!(writeln!(w, "{}{} = {},",
                          tabs(1), &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), member.name.clone());
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            try!(writeln!(w, "pub const {}: {} = {1}::{};",
                          strip_prefix(ns_name, &member.c_identifier),
                          item.name, &prepare_enum_member_name(vals.get(&member.value).unwrap())));
        }
        try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs<W: Write>(w: &mut W, classes: &[&library::Class]) -> Result<()> {
    try!(writeln!(w, ""));
    for klass in classes {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", klass.glib_type_name));
    }

    Ok(())
}

fn generate_interfaces_structs<W: Write>(w: &mut W, interfaces: &[&library::Interface]) -> Result<()> {
    try!(writeln!(w, ""));
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {};", interface.glib_type_name));
    }

    Ok(())
}

fn generate_records<W: Write>(w: &mut W, env: &Env, records: &[&library::Record]) -> Result<()> {
    try!(writeln!(w, ""));
    for record in records {
        let mut lines = Vec::new();
        let mut commented = false;
        let mut has_union = false;
        for field in &record.fields {
            if has_union {
                warn!("A record has fields after the union placeholder");
                lines.push(format!("{}// ignoring the fields after the union", tabs(1)));
                break;
            }
            if env.library.type_(field.typ).maybe_ref_as::<library::Union>().is_some() {
                lines.push(format!("{}_union_placeholder: (),", tabs(1)));
                has_union = true;
            }
            else if let Some(ref c_type) = field.c_type {
                let name = mangle_keywords(&*field.name);
                let c_type = ffi_type(env, field.typ, c_type);
                lines.push(format!("{}{} {}: {},", tabs(1), "pub", name, c_type.as_str()));
                if c_type.is_err() {
                    commented = true;
                }
            }
            else {
                let name = mangle_keywords(&*field.name);
                if let Some(ref func) =
                        env.library.type_(field.typ).maybe_ref_as::<library::Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("{}{} {}: fn{},", tabs(1), "pub", name, sig));
                    commented |= com;
                }
                else {
                    lines.push(format!("{}{} {}: [{:?}],", tabs(1), "pub", name, field.typ));
                    commented = true;
                }
            }
        }
        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {};\n", comment, record.glib_type_name));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.glib_type_name));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::mem;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    parse_quote, punctuated::Pair, spanned::Spanned, Attribute, Data, DeriveInput, Fields,
    GenericArgument, Path, PathArguments, Type, TypePath,
};

mod wrap_incoming;

use wrap_incoming::Meta;

pub fn expand_derive_outgoing(input: DeriveInput) -> syn::Result<TokenStream> {
    if !input.generics.params.is_empty() {
        return Err(syn::Error::new_spanned(
            input.generics,
            "derive(Outgoing) doesn't currently support types with generics!",
        ));
    }

    let derive_deserialize = if no_deserialize_in_attrs(&input.attrs) {
        TokenStream::new()
    } else {
        quote!(#[derive(ruma_api::exports::serde::Deserialize)])
    };

    let mut fields: Vec<_> = match input.data {
        Data::Enum(_) | Data::Union(_) => {
            panic!("#[derive(Outgoing)] is only supported for structs")
        }
        Data::Struct(s) => match s.fields {
            Fields::Named(fs) => fs.named.into_pairs().map(Pair::into_value).collect(),
            Fields::Unnamed(fs) => fs.unnamed.into_pairs().map(Pair::into_value).collect(),
            Fields::Unit => return Ok(impl_outgoing_with_incoming_self(input.ident)),
        },
    };

    let mut any_attribute = false;

    for field in &mut fields {
        let mut field_meta = None;

        let mut remaining_attrs = Vec::new();
        for attr in mem::replace(&mut field.attrs, Vec::new()) {
            if let Some(meta) = Meta::from_attribute(&attr)? {
                if field_meta.is_some() {
                    return Err(syn::Error::new_spanned(
                        attr,
                        "duplicate #[wrap_incoming] attribute",
                    ));
                }
                field_meta = Some(meta);
                any_attribute = true;
            } else {
                remaining_attrs.push(attr);
            }
        }
        field.attrs = remaining_attrs;

        if let Some(attr) = field_meta {
            if let Some(type_to_wrap) = attr.type_to_wrap {
                wrap_generic_arg(&type_to_wrap, &mut field.ty, attr.wrapper_type.as_ref())?;
            } else {
                wrap_ty(&mut field.ty, attr.wrapper_type)?;
            }
        }
    }

    if !any_attribute {
        return Ok(impl_outgoing_with_incoming_self(input.ident));
    }

    let vis = input.vis;
    let doc = format!("'Incoming' variant of [{ty}](struct.{ty}.html).", ty = input.ident);
    let original_ident = input.ident;
    let incoming_ident = Ident::new(&format!("Incoming{}", original_ident), Span::call_site());

    Ok(quote! {
        #[doc = #doc]
        #derive_deserialize
        #vis struct #incoming_ident {
            #(#fields,)*
        }

        impl ruma_api::Outgoing for #original_ident {
            type Incoming = #incoming_ident;
        }
    })
}

fn no_deserialize_in_attrs(attrs: &[Attribute]) -> bool {
    for attr in attrs {
        match &attr.path {
            Path { leading_colon: None, segments }
                if segments.len() == 1 && segments[0].ident == "incoming_no_deserialize" =>
            {
                return true
            }
            _ => {}
        }
    }

    false
}

fn impl_outgoing_with_incoming_self(ident: Ident) -> TokenStream {
    quote! {
        impl ruma_api::Outgoing for #ident {
            type Incoming = Self;
        }
    }
}

fn wrap_ty(ty: &mut Type, path: Option<Path>) -> syn::Result<()> {
    if let Some(wrap_ty) = path {
        *ty = parse_quote!(#wrap_ty<#ty>);
    } else {
        match ty {
            Type::Path(TypePath { path, .. }) => {
                let ty_ident = &mut path.segments.last_mut().unwrap().ident;
                let ident = Ident::new(&format!("Incoming{}", ty_ident), Span::call_site());
                *ty_ident = parse_quote!(#ident);
            }
            _ => return Err(syn::Error::new_spanned(ty, "Can't wrap this type")),
        }
    }

    Ok(())
}

fn wrap_generic_arg(type_to_wrap: &Type, of: &mut Type, with: Option<&Path>) -> syn::Result<()> {
    let mut span = None;
    wrap_generic_arg_impl(type_to_wrap, of, with, &mut span)?;

    if span.is_some() {
        Ok(())
    } else {
        Err(syn::Error::new_spanned(
            of,
            format!(
                "Couldn't find generic argument `{}` in this type",
                type_to_wrap.to_token_stream()
            ),
        ))
    }
}

fn wrap_generic_arg_impl(
    type_to_wrap: &Type,
    of: &mut Type,
    with: Option<&Path>,
    span: &mut Option<Span>,
) -> syn::Result<()> {
    // TODO: Support things like array types?
    let ty_path = match of {
        Type::Path(TypePath { path, .. }) => path,
        _ => return Ok(()),
    };

    let args = match &mut ty_path.segments.last_mut().unwrap().arguments {
        PathArguments::AngleBracketed(ab) => &mut ab.args,
        _ => return Ok(()),
    };

    for arg in args.iter_mut() {
        let ty = match arg {
            GenericArgument::Type(ty) => ty,
            _ => continue,
        };

        if ty == type_to_wrap {
            if let Some(s) = span {
                let mut error = syn::Error::new(
                    *s,
                    format!(
                        "`{}` found multiple times, this is not currently supported",
                        type_to_wrap.to_token_stream()
                    ),
                );
                error.combine(syn::Error::new_spanned(ty, "second occurrence"));
                return Err(error);
            }

            *span = Some(ty.span());

            if let Some(wrapper_type) = with {
                *ty = parse_quote!(#wrapper_type<#ty>);
            } else if let Type::Path(TypePath { path, .. }) = ty {
                let ty_ident = &mut path.segments.last_mut().unwrap().ident;
                let ident = Ident::new(&format!("Incoming{}", ty_ident), Span::call_site());
                *ty_ident = parse_quote!(#ident);
            } else {
                return Err(syn::Error::new_spanned(ty, "Can't wrap this type"));
            }
        } else {
            wrap_generic_arg_impl(type_to_wrap, ty, with, span)?;
        }
    }

    Ok(())
}
<commit_msg>Fix support for tuple structs in Outgoing derive<commit_after>use std::mem;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
    parse_quote, punctuated::Pair, spanned::Spanned, Attribute, Data, DeriveInput, Fields,
    GenericArgument, Path, PathArguments, Type, TypePath,
};

mod wrap_incoming;

use wrap_incoming::Meta;

enum StructKind {
    Struct,
    Tuple,
}

pub fn expand_derive_outgoing(input: DeriveInput) -> syn::Result<TokenStream> {
    if !input.generics.params.is_empty() {
        return Err(syn::Error::new_spanned(
            input.generics,
            "derive(Outgoing) doesn't currently support types with generics!",
        ));
    }

    let derive_deserialize = if no_deserialize_in_attrs(&input.attrs) {
        TokenStream::new()
    } else {
        quote!(#[derive(ruma_api::exports::serde::Deserialize)])
    };

    let (mut fields, struct_kind): (Vec<_>, _) = match input.data {
        Data::Enum(_) | Data::Union(_) => {
            panic!("#[derive(Outgoing)] is only supported for structs")
        }
        Data::Struct(s) => match s.fields {
            Fields::Named(fs) => {
                (fs.named.into_pairs().map(Pair::into_value).collect(), StructKind::Struct)
            }
            Fields::Unnamed(fs) => {
                (fs.unnamed.into_pairs().map(Pair::into_value).collect(), StructKind::Tuple)
            }
            Fields::Unit => return Ok(impl_outgoing_with_incoming_self(input.ident)),
        },
    };

    let mut any_attribute = false;

    for field in &mut fields {
        let mut field_meta = None;

        let mut remaining_attrs = Vec::new();
        for attr in mem::replace(&mut field.attrs, Vec::new()) {
            if let Some(meta) = Meta::from_attribute(&attr)? {
                if field_meta.is_some() {
                    return Err(syn::Error::new_spanned(
                        attr,
                        "duplicate #[wrap_incoming] attribute",
                    ));
                }
                field_meta = Some(meta);
                any_attribute = true;
            } else {
                remaining_attrs.push(attr);
            }
        }
        field.attrs = remaining_attrs;

        if let Some(attr) = field_meta {
            if let Some(type_to_wrap) = attr.type_to_wrap {
                wrap_generic_arg(&type_to_wrap, &mut field.ty, attr.wrapper_type.as_ref())?;
            } else {
                wrap_ty(&mut field.ty, attr.wrapper_type)?;
            }
        }
    }

    if !any_attribute {
        return Ok(impl_outgoing_with_incoming_self(input.ident));
    }

    let vis = input.vis;
    let doc = format!("'Incoming' variant of [{ty}](struct.{ty}.html).", ty = input.ident);
    let original_ident = input.ident;
    let incoming_ident = Ident::new(&format!("Incoming{}", original_ident), Span::call_site());

    let struct_def = match struct_kind {
        StructKind::Struct => quote! { { #(#fields,)* } },
        StructKind::Tuple => quote! { ( #(#fields,)* ); },
    };

    Ok(quote! {
        #[doc = #doc]
        #derive_deserialize
        #vis struct #incoming_ident #struct_def

        impl ruma_api::Outgoing for #original_ident {
            type Incoming = #incoming_ident;
        }
    })
}

fn no_deserialize_in_attrs(attrs: &[Attribute]) -> bool {
    for attr in attrs {
        match &attr.path {
            Path { leading_colon: None, segments }
                if segments.len() == 1 && segments[0].ident == "incoming_no_deserialize" =>
            {
                return true
            }
            _ => {}
        }
    }

    false
}

fn impl_outgoing_with_incoming_self(ident: Ident) -> TokenStream {
    quote! {
        impl ruma_api::Outgoing for #ident {
            type Incoming = Self;
        }
    }
}

fn wrap_ty(ty: &mut Type, path: Option<Path>) -> syn::Result<()> {
    if let Some(wrap_ty) = path {
        *ty = parse_quote!(#wrap_ty<#ty>);
    } else {
        match ty {
            Type::Path(TypePath { path, .. }) => {
                let ty_ident = &mut path.segments.last_mut().unwrap().ident;
                let ident = Ident::new(&format!("Incoming{}", ty_ident), Span::call_site());
                *ty_ident = parse_quote!(#ident);
            }
            _ => return Err(syn::Error::new_spanned(ty, "Can't wrap this type")),
        }
    }

    Ok(())
}

fn wrap_generic_arg(type_to_wrap: &Type, of: &mut Type, with: Option<&Path>) -> syn::Result<()> {
    let mut span = None;
    wrap_generic_arg_impl(type_to_wrap, of, with, &mut span)?;

    if span.is_some() {
        Ok(())
    } else {
        Err(syn::Error::new_spanned(
            of,
            format!(
                "Couldn't find generic argument `{}` in this type",
                type_to_wrap.to_token_stream()
            ),
        ))
    }
}

fn wrap_generic_arg_impl(
    type_to_wrap: &Type,
    of: &mut Type,
    with: Option<&Path>,
    span: &mut Option<Span>,
) -> syn::Result<()> {
    // TODO: Support things like array types?
    let ty_path = match of {
        Type::Path(TypePath { path, .. }) => path,
        _ => return Ok(()),
    };

    let args = match &mut ty_path.segments.last_mut().unwrap().arguments {
        PathArguments::AngleBracketed(ab) => &mut ab.args,
        _ => return Ok(()),
    };

    for arg in args.iter_mut() {
        let ty = match arg {
            GenericArgument::Type(ty) => ty,
            _ => continue,
        };

        if ty == type_to_wrap {
            if let Some(s) = span {
                let mut error = syn::Error::new(
                    *s,
                    format!(
                        "`{}` found multiple times, this is not currently supported",
                        type_to_wrap.to_token_stream()
                    ),
                );
                error.combine(syn::Error::new_spanned(ty, "second occurrence"));
                return Err(error);
            }

            *span = Some(ty.span());

            if let Some(wrapper_type) = with {
                *ty = parse_quote!(#wrapper_type<#ty>);
            } else if let Type::Path(TypePath { path, .. }) = ty {
                let ty_ident = &mut path.segments.last_mut().unwrap().ident;
                let ident = Ident::new(&format!("Incoming{}", ty_ident), Span::call_site());
                *ty_ident = parse_quote!(#ident);
            } else {
                return Err(syn::Error::new_spanned(ty, "Can't wrap this type"));
            }
        } else {
            wrap_generic_arg_impl(type_to_wrap, ty, with, span)?;
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Rollup merge of #34989 - frewsxcv:fix-set-len-doc-example, r=nagisa<commit_after><|endoftext|>


<commit_before><commit_msg>Cleanup unused feature<commit_after><|endoftext|>


<commit_before><commit_msg>sync: add more oneshot examples (#4153)<commit_after><|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, VM};
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction_and_code(transaction: &RPCTransaction, code: &str) -> Context {
    Context {
        caller: Address::from_str(&transaction.from).unwrap(),
        address: Address::from_str(&transaction.to).unwrap(),
        origin: Address::from_str(&transaction.to).unwrap(),
        value: U256::from_str(&transaction.value).unwrap(),
        code: read_hex(code).unwrap(),
        data: read_hex(&transaction.input).unwrap(),
        gas_limit: Gas::from_str(&transaction.gas).unwrap(),
        gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
    }
}

fn main() {
    let mut client = GethRPCClient::new("http://127.0.0.1:8545");

    println!("net version: {}", client.net_version());

    let block = client.get_block_by_number(format!("0x{:x}", 49439).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());

    for transaction_hash in block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = client.get_transaction_by_hash(&transaction_hash);
        println!("transaction: {:?}", transaction);
        let receipt = client.get_transaction_receipt(&transaction_hash);
        println!("receipt: {:?}", receipt);
        let code = client.get_code(&transaction.to, &block.number);
        println!("code: {:?}", code);

        println!("\ntests after the vm has run:");
        println!("2. test gasUsed == {:?}", receipt.gasUsed);
        println!("3. logs and order is {:?}", receipt.logs);
    }

    println!("\nwhen the block is finished, test:");
    println!("1. balances of all used accounts.");
    println!("2. storage values touched.");

    // let matches = clap_app!(regression_test =>
    //     (version: "0.1")
    //     (author: "Ethereum Classic Contributors")
    //     (about: "Gaslighter - Tests the Ethereum Classic Virtual Machine in 5 different ways.")
    //     (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    //     (@subcommand reg =>
    //         (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth`.\n* Wait for the chain to sync.\n* <ctrl-c>\n* Change directory into the gaslighter directory `$ cd gaslighter`\n* Run this command: `$ RUST_BACKTRACE=1 RUST_LOG=gaslighter cargo run --bin gaslighter -- -k reg -c ~/.ethereum/chaindata/`")
    //         (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r localhost:8888`.")
    //     )
    // ).get_matches();
    // let mut has_regression_test_passed = true;
    // let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    // if let Some(ref matches) = matches.subcommand_matches("reg") {
    //     let path = matches.value_of("RPC").unwrap();
    //     has_regression_test_passed = regression(path);
    // }
    // if has_regression_test_passed {
    //     process::exit(0);
    // } else {
    //     process::exit(1);
    // }
}
<commit_msg>Problem: missing actual convertion to BlockHeader and Context in test<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, VM};
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction_and_code(transaction: &RPCTransaction, code: &str) -> Context {
    Context {
        caller: Address::from_str(&transaction.from).unwrap(),
        address: Address::from_str(&transaction.to).unwrap(),
        origin: Address::from_str(&transaction.to).unwrap(),
        value: U256::from_str(&transaction.value).unwrap(),
        code: read_hex(code).unwrap(),
        data: read_hex(&transaction.input).unwrap(),
        gas_limit: Gas::from_str(&transaction.gas).unwrap(),
        gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
    }
}

fn main() {
    let mut client = GethRPCClient::new("http://127.0.0.1:8545");

    println!("net version: {}", client.net_version());

    let block = client.get_block_by_number(format!("0x{:x}", 49439).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());

    let block_header = from_rpc_block(&block);
    println!("block header: {:?}", block_header);

    for transaction_hash in block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = client.get_transaction_by_hash(&transaction_hash);
        println!("transaction: {:?}", transaction);
        let receipt = client.get_transaction_receipt(&transaction_hash);
        println!("receipt: {:?}", receipt);
        let code = client.get_code(&transaction.to, &block.number);
        println!("code: {:?}", code);

        let context = from_rpc_transaction_and_code(&transaction, &code);
        println!("context: {:?}", context);

        println!("\ntests after the vm has run:");
        println!("2. test gasUsed == {:?}", receipt.gasUsed);
        println!("3. logs and order is {:?}", receipt.logs);
    }

    println!("\nwhen the block is finished, test:");
    println!("1. balances of all used accounts.");
    println!("2. storage values touched.");

    // let matches = clap_app!(regression_test =>
    //     (version: "0.1")
    //     (author: "Ethereum Classic Contributors")
    //     (about: "Gaslighter - Tests the Ethereum Classic Virtual Machine in 5 different ways.")
    //     (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    //     (@subcommand reg =>
    //         (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth`.\n* Wait for the chain to sync.\n* <ctrl-c>\n* Change directory into the gaslighter directory `$ cd gaslighter`\n* Run this command: `$ RUST_BACKTRACE=1 RUST_LOG=gaslighter cargo run --bin gaslighter -- -k reg -c ~/.ethereum/chaindata/`")
    //         (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r localhost:8888`.")
    //     )
    // ).get_matches();
    // let mut has_regression_test_passed = true;
    // let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    // if let Some(ref matches) = matches.subcommand_matches("reg") {
    //     let path = matches.value_of("RPC").unwrap();
    //     has_regression_test_passed = regression(path);
    // }
    // if has_regression_test_passed {
    //     process::exit(0);
    // } else {
    //     process::exit(1);
    // }
}
<|endoftext|>


<commit_before>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::io::{Cursor, Read};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::{Float, Integer, ValueRef};

trait ToUnsigned {
    fn from(v: Self) -> Option<usize> where Self: Sized;
}

impl ToUnsigned for u8 {
    fn from(v: u8) -> Option<usize> {
        // Impossible to panic, since u8 always fits in usize.
        Some(v as usize)
    }
}

impl ToUnsigned for u16 {
    fn from(v: u16) -> Option<usize> {
        // TODO: This can overflow on 8-bit systems.
        Some(v as usize)
    }
}

impl ToUnsigned for u32 {
    fn from(v: u32) -> Option<usize> {
        // TODO: This can overflow on 8- and 16-bit systems.
        Some(v as usize)
    }
}

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),
    /// Failed to cast the length read to machine size.
    InvalidLengthSize,
    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
    /// Failed to read ext type.
    InvalidExtTypeRead(ReadError),
    /// Using Reserved type found.
    TypeMismatch,
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_len<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

fn read_num<'a, R, D>(mut rd: &mut R) -> Result<D, Error<'a>>
    where R: BufRead<'a>,
          D: BigEndianRead
{
    D::read(&mut rd).map_err(|err| Error::InvalidDataRead(From::from(err)))
}

fn read_str<'a, R>(buf: &mut R, len: usize) -> Result<&'a str, Error<'a>>
    where R: BufRead<'a>
{
    let buf = try!(read_bin(buf, len));

    // Try to decode sliced buffer as UTF-8.
    let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

    Ok(res)
}

fn read_bin<'a, R>(rd: &mut R, len: usize) -> Result<&'a [u8], Error<'a>>
    where R: BufRead<'a>
{
    let buf = rd.fill_buf();

    if len > buf.len() {
        return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
    }

    // Take a slice.
    let buf = &buf[..len];
    rd.consume(len);

    Ok(buf)
}

// Helper function that reads a single byte from the given `Read` and interpret it as an Ext type.
fn read_ext_type<R>(rd: &mut R) -> Result<i8, ReadError>
    where R: Read
{
    i8::read(rd).map_err(From::from)
}

fn read_ext<'a, R>(mut buf: &mut R, len: usize) -> Result<(i8, &'a [u8]), Error<'a>>
    where R: BufRead<'a>
{
    let ty  = try!(read_ext_type(&mut buf).map_err(|err| Error::InvalidExtTypeRead(err)));
    let buf = try!(read_bin(buf, len));

    Ok((ty, buf))
}

#[inline]
fn read_str_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_str(buf, len));

    Ok(ValueRef::String(res))
}

#[inline]
fn read_bin_value<'a, R, U>(buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_bin(buf, len));

    Ok(ValueRef::Binary(res))
}

#[inline]
fn read_ext_value<'a, R, U>(mut buf: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let (ty, buf) = try!(read_ext(buf, len));

    Ok(ValueRef::Ext(ty, buf))
}

#[inline]
fn read_array_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let vec = try!(read_array(rd, len));

    Ok(ValueRef::Array(vec))
}

#[inline]
fn read_map_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let map = try!(read_map(rd, len));

    Ok(ValueRef::Map(map))
}

fn read_array<'a, R>(rd: &mut R, len: usize) -> Result<Vec<ValueRef<'a>>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let val = try!(read_value_ref(rd));

        vec.push(val);
    }

    Ok(vec)
}

fn read_map<'a, R>(rd: &mut R, len: usize) -> Result<Vec<(ValueRef<'a>, ValueRef<'a>)>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let key = try!(read_value_ref(rd));
        let val = try!(read_value_ref(rd));

        vec.push((key, val));
    }

    Ok(vec)
}

/// A BufRead is a type of Reader which has an internal buffer.
///
/// This magic trait acts like a standard BufRead but unlike the standard this has an explicit
/// internal buffer lifetime, which allows to borrow from underlying buffer while consuming bytes.
pub trait BufRead<'a>: Read {
    /// Returns the buffer contents.
    ///
    /// This function is a lower-level call. It needs to be paired with the consume method to
    /// function properly. When calling this method, none of the contents will be "read" in the
    /// sense that later calling read may return the same contents. As such, consume must be called
    /// with the number of bytes that are consumed from this buffer to ensure that the bytes are
    /// never returned twice.
    ///
    /// An empty buffer returned indicates that the stream has reached EOF.
    fn fill_buf(&self) -> &'a [u8];

    /// Tells this buffer that len bytes have been consumed from the buffer, so they should no
    /// longer be returned in calls to read.
    fn consume(&mut self, len: usize);
}

impl<'a> BufRead<'a> for &'a [u8] {
    fn fill_buf(&self) -> &'a [u8] {
        self
    }

    fn consume(&mut self, len: usize) {
        *self = &(*self)[len..];
    }
}

/// Useful when you want to know how much bytes has been consumed during ValueRef decoding.
impl<'a> BufRead<'a> for Cursor<&'a [u8]> {
    fn fill_buf(&self) -> &'a [u8] {
        use std::cmp;

        let len = cmp::min(self.position(), self.get_ref().len() as u64);
        &self.get_ref()[len as usize..]
    }

    fn consume(&mut self, len: usize) {
        let pos = self.position();
        self.set_position(pos + len as u64);
    }
}

// TODO: Doc, examples.
pub fn read_value_ref<'a, R>(rd: &mut R) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>
{
    let mut rd = rd;

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `rd`.
    let marker = try!(read_marker(rd));

    let val = match marker {
        Marker::Null => ValueRef::Nil,
        Marker::True => ValueRef::Boolean(true),
        Marker::False => ValueRef::Boolean(false),
        Marker::PositiveFixnum(val) => {
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U8 => {
            let val: u8 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U16 => {
            let val: u16 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U32 => {
            let val: u32 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U64 => {
            let val: u64 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val))
        }
        Marker::NegativeFixnum(val) => {
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I8 => {
            let val: i8 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I16 => {
            let val: i16 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I32 => {
            let val: i32 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I64 => {
            let val: i64 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val))
        }
        Marker::F32 => {
            let val: f32 = try!(read_num(rd));
            ValueRef::Float(Float::F32(val))
        }
        Marker::F64 => {
            let val: f64 = try!(read_num(rd));
            ValueRef::Float(Float::F64(val))
        }
        Marker::FixedString(len) => {
            try!(read_str_value(rd, len))
        }
        Marker::Str8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Bin8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::FixedArray(len) => {
            try!(read_array_value(rd, len))
        }
        Marker::Array16 => {
            let len: u16 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::Array32 => {
            let len: u32 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::FixedMap(len) => {
            try!(read_map_value(rd, len))
        }
        Marker::Map16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::Map32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::FixExt1 => {
            try!(read_ext_value(rd, 1u8))
        }
        Marker::FixExt2 => {
            try!(read_ext_value(rd, 2u8))
        }
        Marker::FixExt4 => {
            try!(read_ext_value(rd, 4u8))
        }
        Marker::FixExt8 => {
            try!(read_ext_value(rd, 8u8))
        }
        Marker::FixExt16 => {
            try!(read_ext_value(rd, 16u8))
        }
        Marker::Ext8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Reserved => return Err(Error::TypeMismatch),
    };

    Ok(val)
}
<commit_msg>Misc: proper variable names.<commit_after>//! This module is UNSTABLE, the reason is - recently added.

use std::convert::From;
use std::io::{Cursor, Read};
use std::str::{from_utf8, Utf8Error};

use super::{read_marker};
use super::{
    ReadError,
    MarkerReadError,
};
use super::{BigEndianRead};

use super::super::init::Marker;
use super::super::value::{Float, Integer, ValueRef};

trait ToUnsigned {
    fn from(v: Self) -> Option<usize> where Self: Sized;
}

impl ToUnsigned for u8 {
    fn from(v: u8) -> Option<usize> {
        // Impossible to panic, since u8 always fits in usize.
        Some(v as usize)
    }
}

impl ToUnsigned for u16 {
    fn from(v: u16) -> Option<usize> {
        // TODO: This can overflow on 8-bit systems.
        Some(v as usize)
    }
}

impl ToUnsigned for u32 {
    fn from(v: u32) -> Option<usize> {
        // TODO: This can overflow on 8- and 16-bit systems.
        Some(v as usize)
    }
}

// TODO: Display trait.
#[derive(Debug)]
pub enum Error<'r> {
    /// Failed to read the type marker value.
    InvalidMarkerRead(ReadError),
    /// Failed to read string/array/map size.
    InvalidLengthRead(ReadError),
    /// Failed to read packed non-marker data.
    InvalidDataRead(ReadError),
    /// Failed to cast the length read to machine size.
    InvalidLengthSize,
    /// Failed to interpret a byte slice as a UTF-8 string.
    ///
    /// Contains untouched bytearray with the underlying decoding error.
    InvalidUtf8(&'r [u8], Utf8Error),
    /// Failed to read ext type.
    InvalidExtTypeRead(ReadError),
    /// Using Reserved type found.
    TypeMismatch,
}

impl<'r> From<MarkerReadError> for Error<'r> {
    fn from(err: MarkerReadError) -> Error<'r> {
        Error::InvalidMarkerRead(From::from(err))
    }
}

fn read_len<R, D>(rd: &mut R) -> Result<D, ReadError>
    where R: Read,
          D: BigEndianRead
{
    D::read(rd).map_err(From::from)
}

fn read_num<'a, R, D>(mut rd: &mut R) -> Result<D, Error<'a>>
    where R: BufRead<'a>,
          D: BigEndianRead
{
    D::read(&mut rd).map_err(|err| Error::InvalidDataRead(From::from(err)))
}

fn read_str<'a, R>(rd: &mut R, len: usize) -> Result<&'a str, Error<'a>>
    where R: BufRead<'a>
{
    let buf = try!(read_bin(rd, len));

    // Try to decode sliced buffer as UTF-8.
    let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err)));

    Ok(res)
}

fn read_bin<'a, R>(rd: &mut R, len: usize) -> Result<&'a [u8], Error<'a>>
    where R: BufRead<'a>
{
    let buf = rd.fill_buf();

    if len > buf.len() {
        return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF));
    }

    // Take a slice.
    let buf = &buf[..len];
    rd.consume(len);

    Ok(buf)
}

// Helper function that reads a single byte from the given `Read` and interpret it as an Ext type.
fn read_ext_type<R>(rd: &mut R) -> Result<i8, ReadError>
    where R: Read
{
    i8::read(rd).map_err(From::from)
}

fn read_ext<'a, R>(mut rd: &mut R, len: usize) -> Result<(i8, &'a [u8]), Error<'a>>
    where R: BufRead<'a>
{
    let ty  = try!(read_ext_type(&mut rd).map_err(|err| Error::InvalidExtTypeRead(err)));
    let buf = try!(read_bin(rd, len));

    Ok((ty, buf))
}

#[inline]
fn read_str_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_str(rd, len));

    Ok(ValueRef::String(res))
}

#[inline]
fn read_bin_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let res = try!(read_bin(rd, len));

    Ok(ValueRef::Binary(res))
}

#[inline]
fn read_ext_value<'a, R, U>(mut rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let (ty, buf) = try!(read_ext(rd, len));

    Ok(ValueRef::Ext(ty, buf))
}

#[inline]
fn read_array_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let vec = try!(read_array(rd, len));

    Ok(ValueRef::Array(vec))
}

#[inline]
fn read_map_value<'a, R, U>(rd: &mut R, len: U) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>,
          U: ToUnsigned
{
    let len = try!(U::from(len).ok_or(Error::InvalidLengthSize));
    let map = try!(read_map(rd, len));

    Ok(ValueRef::Map(map))
}

fn read_array<'a, R>(rd: &mut R, len: usize) -> Result<Vec<ValueRef<'a>>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let val = try!(read_value_ref(rd));

        vec.push(val);
    }

    Ok(vec)
}

fn read_map<'a, R>(rd: &mut R, len: usize) -> Result<Vec<(ValueRef<'a>, ValueRef<'a>)>, Error<'a>>
    where R: BufRead<'a>
{
    let mut vec = Vec::with_capacity(len);

    for _ in 0..len {
        let key = try!(read_value_ref(rd));
        let val = try!(read_value_ref(rd));

        vec.push((key, val));
    }

    Ok(vec)
}

/// A BufRead is a type of Reader which has an internal buffer.
///
/// This magic trait acts like a standard BufRead but unlike the standard this has an explicit
/// internal buffer lifetime, which allows to borrow from underlying buffer while consuming bytes.
pub trait BufRead<'a>: Read {
    /// Returns the buffer contents.
    ///
    /// This function is a lower-level call. It needs to be paired with the consume method to
    /// function properly. When calling this method, none of the contents will be "read" in the
    /// sense that later calling read may return the same contents. As such, consume must be called
    /// with the number of bytes that are consumed from this buffer to ensure that the bytes are
    /// never returned twice.
    ///
    /// An empty buffer returned indicates that the stream has reached EOF.
    fn fill_buf(&self) -> &'a [u8];

    /// Tells this buffer that len bytes have been consumed from the buffer, so they should no
    /// longer be returned in calls to read.
    fn consume(&mut self, len: usize);
}

impl<'a> BufRead<'a> for &'a [u8] {
    fn fill_buf(&self) -> &'a [u8] {
        self
    }

    fn consume(&mut self, len: usize) {
        *self = &(*self)[len..];
    }
}

/// Useful when you want to know how much bytes has been consumed during ValueRef decoding.
impl<'a> BufRead<'a> for Cursor<&'a [u8]> {
    fn fill_buf(&self) -> &'a [u8] {
        use std::cmp;

        let len = cmp::min(self.position(), self.get_ref().len() as u64);
        &self.get_ref()[len as usize..]
    }

    fn consume(&mut self, len: usize) {
        let pos = self.position();
        self.set_position(pos + len as u64);
    }
}

// TODO: Doc, examples.
pub fn read_value_ref<'a, R>(rd: &mut R) -> Result<ValueRef<'a>, Error<'a>>
    where R: BufRead<'a>
{
    let mut rd = rd;

    // Reading the marker involves either 1 byte read or nothing. On success consumes strictly
    // 1 byte from the `rd`.
    let marker = try!(read_marker(rd));

    let val = match marker {
        Marker::Null => ValueRef::Nil,
        Marker::True => ValueRef::Boolean(true),
        Marker::False => ValueRef::Boolean(false),
        Marker::PositiveFixnum(val) => {
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U8 => {
            let val: u8 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U16 => {
            let val: u16 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U32 => {
            let val: u32 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val as u64))
        }
        Marker::U64 => {
            let val: u64 = try!(read_num(rd));
            ValueRef::Integer(Integer::U64(val))
        }
        Marker::NegativeFixnum(val) => {
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I8 => {
            let val: i8 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I16 => {
            let val: i16 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I32 => {
            let val: i32 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val as i64))
        }
        Marker::I64 => {
            let val: i64 = try!(read_num(rd));
            ValueRef::Integer(Integer::I64(val))
        }
        Marker::F32 => {
            let val: f32 = try!(read_num(rd));
            ValueRef::Float(Float::F32(val))
        }
        Marker::F64 => {
            let val: f64 = try!(read_num(rd));
            ValueRef::Float(Float::F64(val))
        }
        Marker::FixedString(len) => {
            try!(read_str_value(rd, len))
        }
        Marker::Str8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Str32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_str_value(rd, len))
        }
        Marker::Bin8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::Bin32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_bin_value(rd, len))
        }
        Marker::FixedArray(len) => {
            try!(read_array_value(rd, len))
        }
        Marker::Array16 => {
            let len: u16 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::Array32 => {
            let len: u32 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_array_value(rd, len))
        }
        Marker::FixedMap(len) => {
            try!(read_map_value(rd, len))
        }
        Marker::Map16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::Map32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_map_value(rd, len))
        }
        Marker::FixExt1 => {
            try!(read_ext_value(rd, 1u8))
        }
        Marker::FixExt2 => {
            try!(read_ext_value(rd, 2u8))
        }
        Marker::FixExt4 => {
            try!(read_ext_value(rd, 4u8))
        }
        Marker::FixExt8 => {
            try!(read_ext_value(rd, 8u8))
        }
        Marker::FixExt16 => {
            try!(read_ext_value(rd, 16u8))
        }
        Marker::Ext8 => {
            let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext16 => {
            let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Ext32 => {
            let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err)));
            try!(read_ext_value(rd, len))
        }
        Marker::Reserved => return Err(Error::TypeMismatch),
    };

    Ok(val)
}
<|endoftext|>