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

use std::cell::RefCell;
use std::error::Error;
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::process;
use std::rc::Rc;

use ucglib::build;
use ucglib::build::assets::{Cache, MemoryCache};
use ucglib::build::Val;
use ucglib::convert::traits;
use ucglib::convert::ConverterRunner;

// TODO(jwall): List the target output types automatically.
fn do_flags<'a>() -> clap::ArgMatches<'a> {
    clap_app!(
        ucg =>
            (version: crate_version!())
            (author: crate_authors!())
            (about: "Universal Configuration Grammar compiler.")
            (@subcommand inspect =>
             (about: "Inspect a specific symbol in a ucg file.")
             (@arg sym: --sym +takes_value +required "Specify a specific binding in the ucg file to output.")
             (@arg target: --format +required +takes_value "Inspect output type. (flags, json, env, exec)")
             (@arg INPUT: +required "Input ucg file to inspect symbol from.")
            )
            (@subcommand build =>
             (about: "Build a list of ucg files.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse in directories or not.")
             (@arg INPUT: ... "Input ucg files or directories to build. If not provided then build the contents of the current directory.")
            )
            (@subcommand validate =>
             (about: "Check a list of ucg files for errors and run assertions.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse or not.")
             (@arg INPUT: ... "Input ucg files or directories to validate. If not provided it will scan the directories for files with _test.ucg")
            )
            (@subcommand converters =>
             (about: "list the available converters")
             (@arg name: -c --converter-name +takes_value "Optionally print help for the provided converter")
            )
    ).get_matches()
}

fn run_converter(c: ConverterRunner, v: Rc<Val>, f: Option<&str>) -> traits::Result {
    let file: Box<std::io::Write> = match f {
        Some(f) => {
            let mut path_buf = PathBuf::from(f);
            path_buf.set_extension(c.ext());
            let new_path = path_buf.to_str().unwrap();
            Box::new(try!(File::create(&new_path)))
        }
        None => Box::new(io::stdout()),
    };
    c.convert(v, file)
}

fn build_file(
    file: &str,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> {
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    if validate {
        builder.enable_validate_mode();
    }
    try!(builder.build_file(file));
    if validate {
        println!("{}", builder.assert_collector.summary);
    }
    Ok(builder)
}

fn do_validate(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Validating {}", file);
    match build_file(file, true, cache) {
        Ok(_) => println!("{} validates\n\n", file),
        Err(msg) => {
            eprintln!("Err: {}", msg);
            return false;
        }
    }
    return true;
}

fn do_compile(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Building {}", file);
    let builder = match build_file(file, false, cache.clone()) {
        Ok(builder) => builder,
        Err(err) => {
            eprintln!("{:?}", err);
            return false;
        }
    };
    let (typ, val) = match builder.out_lock {
        Some((ref typ, ref val)) => (typ, val.clone()),
        None => {
            eprintln!("Build results in no artifacts.");
            return false;
        }
    };
    match ConverterRunner::new(typ) {
        Ok(converter) => {
            run_converter(converter, val, Some(file)).unwrap();
            eprintln!("Build successful");
            process::exit(0);
        }
        Err(msg) => {
            eprintln!("{}", msg);
            return false;
        }
    }
}

fn visit_ucg_files(
    path: &Path,
    recurse: bool,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<bool, Box<Error>> {
    let our_path = String::from(path.to_string_lossy());
    let mut result = true;
    if path.is_dir() {
        for entry in try!(std::fs::read_dir(path)) {
            let next_item = try!(entry);
            let next_path = next_item.path();
            let path_as_string = String::from(next_path.to_string_lossy());
            if next_path.is_dir() && recurse {
                if let Err(msg) = visit_ucg_files(&next_path, recurse, validate, cache.clone()) {
                    eprintln!("Err 1: {}", msg);
                    result = false;
                }
            } else {
                if validate && path_as_string.ends_with("_test.ucg") {
                    if !do_validate(&path_as_string, cache.clone()) {
                        result = false;
                    }
                } else {
                    if !do_compile(&path_as_string, cache.clone()) {
                        result = false;
                    }
                }
            }
        }
    } else if validate && our_path.ends_with("_test.ucg") {
        if !do_validate(&our_path, cache) {
            result = false;
        }
    } else {
        if !do_compile(&our_path, cache) {
            result = false;
        }
    }
    Ok(result)
}

fn main() {
    let app = do_flags();
    let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
    if let Some(matches) = app.subcommand_matches("inspect") {
        let file = matches.value_of("INPUT").unwrap();
        let sym = matches.value_of("sym");
        let target = matches.value_of("target").unwrap();
        let root = PathBuf::from(file);
        let mut builder = build::Builder::new(root.parent().unwrap(), cache);
        match ConverterRunner::new(target) {
            Ok(converter) => {
                // TODO(jwall): We should warn if this is a test file.
                let result = builder.build_file(file);
                if !result.is_ok() {
                    eprintln!("{:?}", result.err().unwrap());
                    process::exit(1);
                }
                let val = match sym {
                    Some(sym_name) => builder.get_out_by_name(sym_name),
                    None => builder.last,
                };
                match val {
                    Some(value) => {
                        // We use None here because we always output to stdout for an inspect.
                        run_converter(converter, value, None).unwrap();
                        eprintln!("Build successful");
                        process::exit(0);
                    }
                    None => {
                        eprintln!("Build results in no value.");
                        process::exit(1);
                    }
                }
            }
            Err(msg) => {
                eprintln!("{}", msg);
                process::exit(1);
            }
        }
    } else if let Some(matches) = app.subcommand_matches("build") {
        let files = matches.values_of("INPUT");
        let recurse = matches.is_present("recurse");
        let mut ok = true;
        if files.is_none() {
            let curr_dir = std::env::current_dir().unwrap();
            let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone());
            if let Ok(false) = ok {
                process::exit(1)
            }
        }
        for file in files.unwrap() {
            let pb = PathBuf::from(file);
            if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone()) {
                ok = false;
            }
        }
        if !ok {
            process::exit(1)
        }
    } else if let Some(matches) = app.subcommand_matches("validate") {
        let files = matches.values_of("INPUT");
        let recurse = matches.is_present("recurse");
        if files.is_none() {
            let curr_dir = std::env::current_dir().unwrap();
            let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone());
            if let Ok(false) = ok {
                process::exit(1)
            }
        } else {
            let mut ok = true;
            for file in files.unwrap() {
                let pb = PathBuf::from(file);
                if pb.is_dir() {
                    if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, cache.clone()) {
                        ok = false;
                    }
                } else {
                    match build_file(file, true, cache.clone()) {
                        Ok(b) => {
                            if b.assert_collector.success {
                                println!("File {} Validates", file);
                            } else {
                                println!("File {} Fails", file);
                                ok = false;
                            }
                        }
                        Err(msg) => {
                            // We continue to process the other files despite this failure.
                            eprintln!("{}", msg);
                            ok = false;
                        }
                    }
                }
            }
            if !ok {
                process::exit(1)
            }
        }
        process::exit(0);
    } else if let Some(_todo) = app.subcommand_matches("converters") {
        // TODO(jwall): Flesh this command out.
    }
}
<commit_msg>BUGFIX: Properly run only _test.ucg files in validation.<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
#[macro_use]
extern crate clap;
extern crate ucglib;

use std::cell::RefCell;
use std::error::Error;
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::process;
use std::rc::Rc;

use ucglib::build;
use ucglib::build::assets::{Cache, MemoryCache};
use ucglib::build::Val;
use ucglib::convert::traits;
use ucglib::convert::ConverterRunner;

// TODO(jwall): List the target output types automatically.
fn do_flags<'a>() -> clap::ArgMatches<'a> {
    clap_app!(
        ucg =>
            (version: crate_version!())
            (author: crate_authors!())
            (about: "Universal Configuration Grammar compiler.")
            (@subcommand inspect =>
             (about: "Inspect a specific symbol in a ucg file.")
             (@arg sym: --sym +takes_value +required "Specify a specific binding in the ucg file to output.")
             (@arg target: --format +required +takes_value "Inspect output type. (flags, json, env, exec)")
             (@arg INPUT: +required "Input ucg file to inspect symbol from.")
            )
            (@subcommand build =>
             (about: "Build a list of ucg files.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse in directories or not.")
             (@arg INPUT: ... "Input ucg files or directories to build. If not provided then build the contents of the current directory.")
            )
            (@subcommand validate =>
             (about: "Check a list of ucg files for errors and run assertions.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse or not.")
             (@arg INPUT: ... "Input ucg files or directories to validate. If not provided it will scan the directories for files with _test.ucg")
            )
            (@subcommand converters =>
             (about: "list the available converters")
             (@arg name: -c --converter-name +takes_value "Optionally print help for the provided converter")
            )
    ).get_matches()
}

fn run_converter(c: ConverterRunner, v: Rc<Val>, f: Option<&str>) -> traits::Result {
    let file: Box<std::io::Write> = match f {
        Some(f) => {
            let mut path_buf = PathBuf::from(f);
            path_buf.set_extension(c.ext());
            let new_path = path_buf.to_str().unwrap();
            Box::new(try!(File::create(&new_path)))
        }
        None => Box::new(io::stdout()),
    };
    c.convert(v, file)
}

fn build_file(
    file: &str,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> {
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    if validate {
        builder.enable_validate_mode();
    }
    try!(builder.build_file(file));
    if validate {
        println!("{}", builder.assert_collector.summary);
    }
    Ok(builder)
}

fn do_validate(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Validating {}", file);
    match build_file(file, true, cache) {
        Ok(_) => println!("{} validates\n\n", file),
        Err(msg) => {
            eprintln!("Err: {}", msg);
            return false;
        }
    }
    return true;
}

fn do_compile(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Building {}", file);
    let builder = match build_file(file, false, cache.clone()) {
        Ok(builder) => builder,
        Err(err) => {
            eprintln!("{:?}", err);
            return false;
        }
    };
    let (typ, val) = match builder.out_lock {
        Some((ref typ, ref val)) => (typ, val.clone()),
        None => {
            eprintln!("Build results in no artifacts.");
            return false;
        }
    };
    match ConverterRunner::new(typ) {
        Ok(converter) => {
            run_converter(converter, val, Some(file)).unwrap();
            eprintln!("Build successful");
            process::exit(0);
        }
        Err(msg) => {
            eprintln!("{}", msg);
            return false;
        }
    }
}

fn visit_ucg_files(
    path: &Path,
    recurse: bool,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<bool, Box<Error>> {
    let our_path = String::from(path.to_string_lossy());
    let mut result = true;
    // TODO(jwall): Report the failing files at the bottom.
    let mut summary = String::new();
    if path.is_dir() {
        for entry in try!(std::fs::read_dir(path)) {
            let next_item = try!(entry);
            let next_path = next_item.path();
            let path_as_string = String::from(next_path.to_string_lossy());
            if next_path.is_dir() && recurse {
                if let Err(msg) = visit_ucg_files(&next_path, recurse, validate, cache.clone()) {
                    eprintln!("Err 1: {}", msg);
                    result = false;
                }
            } else {
                if validate && path_as_string.ends_with("_test.ucg") {
                    if !do_validate(&path_as_string, cache.clone()) {
                        result = false;
                        summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
                    } else {
                        summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
                    }
                } else if !validate {
                    if !do_compile(&path_as_string, cache.clone()) {
                        result = false;
                    }
                }
            }
        }
    } else if validate && our_path.ends_with("_test.ucg") {
        if !do_validate(&our_path, cache) {
            result = false;
            summary.push_str(format!("{} - FAIL\n", our_path).as_str())
        } else {
            summary.push_str(format!("{} - PASS\n", &our_path).as_str())
        }
    } else if !validate {
        if !do_compile(&our_path, cache) {
            result = false;
        }
    }
    if validate {
        println!("RESULTS:");
        println!("{}", summary);
    }
    Ok(result)
}

fn main() {
    let app = do_flags();
    let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
    if let Some(matches) = app.subcommand_matches("inspect") {
        let file = matches.value_of("INPUT").unwrap();
        let sym = matches.value_of("sym");
        let target = matches.value_of("target").unwrap();
        let root = PathBuf::from(file);
        let mut builder = build::Builder::new(root.parent().unwrap(), cache);
        match ConverterRunner::new(target) {
            Ok(converter) => {
                // TODO(jwall): We should warn if this is a test file.
                let result = builder.build_file(file);
                if !result.is_ok() {
                    eprintln!("{:?}", result.err().unwrap());
                    process::exit(1);
                }
                let val = match sym {
                    Some(sym_name) => builder.get_out_by_name(sym_name),
                    None => builder.last,
                };
                match val {
                    Some(value) => {
                        // We use None here because we always output to stdout for an inspect.
                        run_converter(converter, value, None).unwrap();
                        eprintln!("Build successful");
                        process::exit(0);
                    }
                    None => {
                        eprintln!("Build results in no value.");
                        process::exit(1);
                    }
                }
            }
            Err(msg) => {
                eprintln!("{}", msg);
                process::exit(1);
            }
        }
    } else if let Some(matches) = app.subcommand_matches("build") {
        let files = matches.values_of("INPUT");
        let recurse = matches.is_present("recurse");
        let mut ok = true;
        if files.is_none() {
            let curr_dir = std::env::current_dir().unwrap();
            let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone());
            if let Ok(false) = ok {
                process::exit(1)
            }
        }
        for file in files.unwrap() {
            let pb = PathBuf::from(file);
            if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone()) {
                ok = false;
            }
        }
        if !ok {
            process::exit(1)
        }
    } else if let Some(matches) = app.subcommand_matches("validate") {
        let files = matches.values_of("INPUT");
        let recurse = matches.is_present("recurse");
        if files.is_none() {
            let curr_dir = std::env::current_dir().unwrap();
            let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone());
            if let Ok(false) = ok {
                process::exit(1)
            }
        } else {
            let mut ok = true;
            for file in files.unwrap() {
                let pb = PathBuf::from(file);
                if pb.is_dir() {
                    if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, cache.clone()) {
                        ok = false;
                    }
                } else {
                    match build_file(file, true, cache.clone()) {
                        Ok(b) => {
                            if b.assert_collector.success {
                                println!("File {} Validates", file);
                            } else {
                                println!("File {} Fails", file);
                                ok = false;
                            }
                        }
                        Err(msg) => {
                            // We continue to process the other files despite this failure.
                            eprintln!("{}", msg);
                            ok = false;
                        }
                    }
                }
            }
            if !ok {
                process::exit(1)
            }
        }
        process::exit(0);
    } else if let Some(_todo) = app.subcommand_matches("converters") {
        // TODO(jwall): Flesh this command out.
    }
}
<|endoftext|>


<commit_before>extern crate getopts;

extern crate otp_cop;

use std::{env, thread};
use std::sync::{mpsc};

use otp_cop::service::{CreateServiceResult, ServiceFactory};

struct ParallelIter<T> {
    count: usize,
    pos: usize,
    rx: mpsc::Receiver<T>
}

impl<T> Iterator for ParallelIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.pos < self.count {
            self.count += 1;
            return self.rx.recv().ok();
        } else {
            return None;
        }
    }
}

fn parallel<T, U, F1>(objs: Vec<T>, f1: F1) -> ParallelIter<U>
        where F1: 'static + Fn(T) -> U + Send, T: Send, U: Send {
    let (tx, rx) = mpsc::channel();
    let mut threads = vec![];
    for o in objs {
        threads.push(thread::spawn(move || {
            tx.send(f1(o));
        }));
    }

    for t in threads {
        t.join();
    }

    return ParallelIter{count: threads.len(), pos: 0, rx: rx};
}

fn main() {
    let service_factories = vec![
        Box::new(otp_cop::SlackServiceFactory) as Box<ServiceFactory>,
        Box::new(otp_cop::GithubServiceFactory) as Box<ServiceFactory>,
    ];

    let mut opts = getopts::Options::new();

    for factory in service_factories.iter() {
        factory.add_options(&mut opts);
    }

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(matches) => matches,
        Err(e) => panic!(e.to_string()),
    };

    let mut services = vec![];

    for factory in service_factories.iter() {
        match factory.create_service(&matches) {
            CreateServiceResult::Service(s) => services.push(s),
            CreateServiceResult::MissingArguments(arg) => panic!(format!("Missing arguments: {:?}", arg)),
            CreateServiceResult::None => continue,
        }
    }

    if services.is_empty() {
        print!("{}", opts.usage("otp-cop: <args>"));
    }

    for (i, result) in parallel(services, |service| service.get_users()).enumerate() {
        println!("{}", result.service_name);
        println!("{}", "=".chars().cycle().take(result.service_name.len()).collect::<String>());
        println!("");
        for user in result.users {
            let email = match user.email {
                Some(email) => format!(" ({})", email),
                None => "".to_string(),
            };
            let details = match user.details {
                Some(details) => format!(" -- {}", details),
                None => "".to_string(),
            };
            println!("@{}{}{}", user.name, email, details);
        }
        if i + 1 != services.len() {
            println!("");
            println!("");
        }
    }
}
<commit_msg>removed some unnecesary code<commit_after>extern crate getopts;

extern crate otp_cop;

use std::{env, thread};
use std::sync::{mpsc};

use otp_cop::service::{CreateServiceResult, ServiceFactory};

struct ParallelIter<T> {
    count: usize,
    pos: usize,
    rx: mpsc::Receiver<T>
}

impl<T> Iterator for ParallelIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.pos < self.count {
            self.count += 1;
            return self.rx.recv().ok();
        } else {
            return None;
        }
    }
}

fn parallel<T, U, F1>(objs: Vec<T>, f1: F1) -> ParallelIter<U>
        where F1: 'static + Fn(T) -> U + Send, T: Send, U: Send {
    let (tx, rx) = mpsc::channel();
    for o in objs {
        thread::spawn(move || {
            tx.send(f1(o));
        });
    }

    return ParallelIter{count: objs.len(), pos: 0, rx: rx};
}

fn main() {
    let service_factories = vec![
        Box::new(otp_cop::SlackServiceFactory) as Box<ServiceFactory>,
        Box::new(otp_cop::GithubServiceFactory) as Box<ServiceFactory>,
    ];

    let mut opts = getopts::Options::new();

    for factory in service_factories.iter() {
        factory.add_options(&mut opts);
    }

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(matches) => matches,
        Err(e) => panic!(e.to_string()),
    };

    let mut services = vec![];

    for factory in service_factories.iter() {
        match factory.create_service(&matches) {
            CreateServiceResult::Service(s) => services.push(s),
            CreateServiceResult::MissingArguments(arg) => panic!(format!("Missing arguments: {:?}", arg)),
            CreateServiceResult::None => continue,
        }
    }

    if services.is_empty() {
        print!("{}", opts.usage("otp-cop: <args>"));
    }

    for (i, result) in parallel(services, |service| service.get_users()).enumerate() {
        println!("{}", result.service_name);
        println!("{}", "=".chars().cycle().take(result.service_name.len()).collect::<String>());
        println!("");
        for user in result.users {
            let email = match user.email {
                Some(email) => format!(" ({})", email),
                None => "".to_string(),
            };
            let details = match user.details {
                Some(details) => format!(" -- {}", details),
                None => "".to_string(),
            };
            println!("@{}{}{}", user.name, email, details);
        }
        if i + 1 != services.len() {
            println!("");
            println!("");
        }
    }
}
<|endoftext|>


<commit_before>extern crate classreader;

use classreader::*;
use std::env;

fn main() {
    let file_name = env::args().nth(1).expect("usage: javamoose <class file>");
    println!("Loading class file {}", file_name);
    println!("======================================================");
    let class = ClassReader::new_from_path(&file_name).unwrap();

    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:  {}", get_method_size(&method));
        println!("\t\tlines: {}", get_method_lines(&method));
        // println!("{:?}", method);
    }

    println!("======================================================");
    println!("Done!");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => get_opt_line_number(line_table.last()) - get_opt_line_number(line_table.first()),
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

<commit_msg>Compute method complexity<commit_after>extern crate classreader;

use classreader::*;
use std::env;

fn main() {
    let file_name = env::args().nth(1).expect("usage: javamoose <class file>");
    println!("Loading class file {}", file_name);
    println!("======================================================");
    let class = ClassReader::new_from_path(&file_name).unwrap();

    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
        // println!("{:?}", method);
    }

    println!("======================================================");
    println!("Done!");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => get_opt_line_number(line_table.last()) - get_opt_line_number(line_table.first()),
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the starting one for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

pub mod tetris;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Glutin_Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;
use graphics::{ Transformed };
use piston::input::Button::{ Keyboard };
use piston::input::Input;
use piston::input::keyboard::Key;
use std::path::Path;

use tetris::*;

struct App {
    gl: GlGraphics,
    tetris: Tetris,
    elapsed_time: f64,
    cache: GlyphCache<'static>,
    should_redraw: bool
}

const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const DARK_GRAY: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const CYAN: [f32; 4] = [0.0, 1.0, 1.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0];
const ORANGE: [f32; 4] = [1.0, 0.5, 0.0, 1.0];
const YELLOW: [f32; 4] = [1.0, 1.0, 0.0, 1.0];
const PURPLE: [f32; 4] = [0.5, 0.0, 1.0, 1.0];

fn get_shape_color(shape_index: i32) -> [f32; 4] {
    match shape_index {
        0 => PURPLE,
        1 => YELLOW,
        2 => RED,
        3 => GREEN,
        4 => ORANGE,
        5 => BLUE,
        6 => CYAN,
        _ => {
            BLACK
        }
    }
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        self.should_redraw = false;

        const CELL_SIZE: f64 = 30.0;
        const LEFT_MARGIN: f64 = 20f64;
        const TOP_MARGIN: f64 = 30f64;

        const STATUS_LEFT_MARGIN: f64 = 400f64;
        const STATUS_TOP_MARGIN: f64 = 100f64;
        const LINE_HEIGHT: f64 = 40f64;
        const STATUS_PREVIEW_GRID_HEIGHT: f64 = CELL_SIZE * 6f64;

        // so that we can access inside clojure
        let use_cache = &mut self.cache;
        let use_tetris = &mut self.tetris;

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the viewport
            graphics::clear(BLACK, gl);

            // render the current score and level
            let mut text = graphics::Text::new(22);
            text.color = ORANGE;
            let mut transform = c.trans(STATUS_LEFT_MARGIN, STATUS_TOP_MARGIN);
            text.draw(&format!("Level: {}", use_tetris.get_level()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            text.draw(&format!("Score: {}", use_tetris.get_score()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            // render the next shape as a preview of what's coming next
            for point in use_tetris.get_next_shape().iter() {
                let square = graphics::rectangle::square(0f64, 0f64, CELL_SIZE - 1f64);
                let color = get_shape_color(use_tetris.get_next_shape_index() as i32);
                // render the shape at col 2 and row 2
                let (x, y) = ((2 as i16 + point.x) as f64 * CELL_SIZE,
                    (2 as i16 + point.y) as f64 * CELL_SIZE);
                //graphics::rectangle(color, square, transform.trans(x, y).transform, gl);
                let mut rectangle = graphics::Rectangle::new(color);
                rectangle.shape = graphics::rectangle::Shape::Round(4.0, 16);
                rectangle.draw(square, &c.draw_state, transform.trans(x, y).transform, gl);
            }
            transform = transform.trans(0f64, STATUS_PREVIEW_GRID_HEIGHT);

            // render GAME OVER text if necessary
            if use_tetris.get_game_over() {
                text.draw(&"GAME OVER", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'N' for a new game", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Use arrow keys to move and rotate", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press spacebar to drop", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&format!("Press 'K' to decrease starting level ({})", use_tetris.get_starting_level()),
                    use_cache, &c.draw_state, transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'L' to increase starting level",
                    use_cache, &c.draw_state, transform.transform, gl);
                // uncomment if drawing additional text in the status area
                //transform = transform.trans(0f64, LINE_HEIGHT);
            }

            // draw a white border around the game board
            let rect_border = graphics::Rectangle::new_border(DARK_GRAY, 1.5);
            rect_border.draw([
                LEFT_MARGIN - 2f64,
                TOP_MARGIN - 2f64,
                (CELL_SIZE * COL_COUNT as f64) + 3f64,
                (CELL_SIZE * ROW_COUNT as f64) + 3f64,
            ], &c.draw_state, c.transform, gl);

            // render each cell in the game board
            for col in 0..COL_COUNT as i32 {
                for row in 0..ROW_COUNT as i32 {
                    let cell = use_tetris.get_grid_cell(col, row);
                    if cell.cell_type != GridCellType::Void {
                        let (x, y) = (col as f64 * CELL_SIZE, row as f64 * CELL_SIZE);
                        let square = graphics::rectangle::square(0.0f64, 0.0f64, CELL_SIZE - 1f64);
                        let color = match cell.cell_type {
                            GridCellType::Shape => get_shape_color(cell.shape_index),
                            GridCellType::Fixed => get_shape_color(cell.shape_index),
                            GridCellType::Ghost => DARK_GRAY,
                            _ => {
                                assert!(false);
                                BLACK
                            }
                        };
                        let transform = c.transform.trans(LEFT_MARGIN, TOP_MARGIN).trans(x, y);

                        //graphics::rectangle(color, square, transform, gl);

                        let mut rectangle = graphics::Rectangle::new(color);
                        rectangle.shape = graphics::rectangle::Shape::Round(4.0, 16);
                        rectangle.draw(square, &c.draw_state, transform, gl);
                    }
                }
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        if self.tetris.get_game_over() {
            self.elapsed_time = 0.0;
        } else {
            // Here we increment the time elapsed between update()'s
            self.elapsed_time += args.dt;
            // if the elapsed time is now greater than the time allotted between ticks, then invoke tetris.tick()
            if self.elapsed_time > self.tetris.get_tick_time() as f64 {
                self.elapsed_time = 0.0;
                self.tetris.tick();
                self.should_redraw = true;
            }
        }
    }

    fn handle_input(&mut self, i: Input) {
        let prev_should_redraw = self.should_redraw;
        self.should_redraw = true;
        match i {
            Input::Press(Keyboard(Key::Left)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col - 1);
            },

            Input::Press(Keyboard(Key::Right)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col + 1);
            },

            Input::Press(Keyboard(Key::Up)) => {
                self.tetris.rotate(true);
            },

            Input::Press(Keyboard(Key::Down)) => {
                let row: i32 = self.tetris.get_row() + 1;
                self.tetris.set_row(row);
            },

            Input::Press(Keyboard(Key::Space)) => {
                let mut row: i32 = self.tetris.get_row() + 1;
                while self.tetris.set_row(row) {
                    row += 1;
                }
                // hard drop immediately spawns next shape
                self.tetris.tick();
            },

            Input::Press(Keyboard(Key::N)) => {
                self.tetris.start_game();
            },

            Input::Press(Keyboard(Key::K)) => {
                if self.tetris.get_starting_level() > 0 {
                    let new_level: u32 = self.tetris.get_starting_level() - 1;
                    self.tetris.set_starting_level(new_level);
                }
            },

            Input::Press(Keyboard(Key::L)) => {
                if self.tetris.get_starting_level() < 30 {
                    let new_level: u32 = self.tetris.get_starting_level() + 1;
                    self.tetris.set_starting_level(new_level);
                }
            },

            _ => {
                self.should_redraw = prev_should_redraw;
            }
        }
    }
}

fn main() {
    start_app();
}

fn start_app() {
    let opengl = OpenGL::_3_2;

    let window = Glutin_Window::new(
        opengl,
        WindowSettings::new("Piston Tetris", [1024, 768])
                       .exit_on_esc(true)
                       .vsync(true)
                       //TODO causes error on ubuntu
                       //.samples(1)
    );

    let font_path = Path::new("FiraMono-Bold.ttf");

    let mut app = App {
        gl: GlGraphics::new(opengl),
        tetris: Tetris::new(),
        elapsed_time: 0.0,
        cache: GlyphCache::new(font_path).unwrap(),
        should_redraw: true,
    };

    for e in window.events().max_fps(60).ups(120) {
        match e {
            Event::Input(i) => {
                app.handle_input(i);
            }

            Event::Render(args) if app.should_redraw => {
                app.render(&args);
            }

            Event::Update(args) => {
                app.update(&args);
            }

            _ => {}
        }
    }
}<commit_msg>changed border color<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;

pub mod tetris;

use piston::window::WindowSettings;
use piston::event::*;
use glutin_window::GlutinWindow as Glutin_Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use opengl_graphics::glyph_cache::GlyphCache;
use graphics::{ Transformed };
use piston::input::Button::{ Keyboard };
use piston::input::Input;
use piston::input::keyboard::Key;
use std::path::Path;

use tetris::*;

struct App {
    gl: GlGraphics,
    tetris: Tetris,
    elapsed_time: f64,
    cache: GlyphCache<'static>,
    should_redraw: bool
}

//const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
const LIGHT_GRAY: [f32; 4] = [0.3, 0.3, 0.3, 1.0];
const DARK_GRAY: [f32; 4] = [0.1, 0.1, 0.1, 1.0];
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const CYAN: [f32; 4] = [0.0, 1.0, 1.0, 1.0];
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const BLUE: [f32; 4] = [0.0, 0.0, 1.0, 1.0];
const ORANGE: [f32; 4] = [1.0, 0.5, 0.0, 1.0];
const YELLOW: [f32; 4] = [1.0, 1.0, 0.0, 1.0];
const PURPLE: [f32; 4] = [0.5, 0.0, 1.0, 1.0];

fn get_shape_color(shape_index: i32) -> [f32; 4] {
    match shape_index {
        0 => PURPLE,
        1 => YELLOW,
        2 => RED,
        3 => GREEN,
        4 => ORANGE,
        5 => BLUE,
        6 => CYAN,
        _ => {
            BLACK
        }
    }
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        self.should_redraw = false;

        const CELL_SIZE: f64 = 30.0;
        const LEFT_MARGIN: f64 = 20f64;
        const TOP_MARGIN: f64 = 30f64;

        const STATUS_LEFT_MARGIN: f64 = 400f64;
        const STATUS_TOP_MARGIN: f64 = 100f64;
        const LINE_HEIGHT: f64 = 40f64;
        const STATUS_PREVIEW_GRID_HEIGHT: f64 = CELL_SIZE * 6f64;

        // so that we can access inside clojure
        let use_cache = &mut self.cache;
        let use_tetris = &mut self.tetris;

        self.gl.draw(args.viewport(), |c, gl| {
            // clear the viewport
            graphics::clear(BLACK, gl);

            // render the current score and level
            let mut text = graphics::Text::new(22);
            text.color = ORANGE;
            let mut transform = c.trans(STATUS_LEFT_MARGIN, STATUS_TOP_MARGIN);
            text.draw(&format!("Level: {}", use_tetris.get_level()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            text.draw(&format!("Score: {}", use_tetris.get_score()), use_cache, &c.draw_state,
                transform.transform, gl);
            transform = transform.trans(0f64, LINE_HEIGHT);

            // render the next shape as a preview of what's coming next
            for point in use_tetris.get_next_shape().iter() {
                let square = graphics::rectangle::square(0f64, 0f64, CELL_SIZE - 1f64);
                let color = get_shape_color(use_tetris.get_next_shape_index() as i32);
                // render the shape at col 2 and row 2
                let (x, y) = ((2 as i16 + point.x) as f64 * CELL_SIZE,
                    (2 as i16 + point.y) as f64 * CELL_SIZE);
                //graphics::rectangle(color, square, transform.trans(x, y).transform, gl);
                let mut rectangle = graphics::Rectangle::new(color);
                rectangle.shape = graphics::rectangle::Shape::Round(4.0, 16);
                rectangle.draw(square, &c.draw_state, transform.trans(x, y).transform, gl);
            }
            transform = transform.trans(0f64, STATUS_PREVIEW_GRID_HEIGHT);

            // render GAME OVER text if necessary
            if use_tetris.get_game_over() {
                text.draw(&"GAME OVER", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'N' for a new game", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Use arrow keys to move and rotate", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press spacebar to drop", use_cache, &c.draw_state,
                    transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&format!("Press 'K' to decrease starting level ({})", use_tetris.get_starting_level()),
                    use_cache, &c.draw_state, transform.transform, gl);
                transform = transform.trans(0f64, LINE_HEIGHT);

                text.draw(&"Press 'L' to increase starting level",
                    use_cache, &c.draw_state, transform.transform, gl);
                // uncomment if drawing additional text in the status area
                //transform = transform.trans(0f64, LINE_HEIGHT);
            }

            // draw a white border around the game board
            let rect_border = graphics::Rectangle::new_border(LIGHT_GRAY, 1.5);
            rect_border.draw([
                LEFT_MARGIN - 2f64,
                TOP_MARGIN - 2f64,
                (CELL_SIZE * COL_COUNT as f64) + 3f64,
                (CELL_SIZE * ROW_COUNT as f64) + 3f64,
            ], &c.draw_state, c.transform, gl);

            // render each cell in the game board
            for col in 0..COL_COUNT as i32 {
                for row in 0..ROW_COUNT as i32 {
                    let cell = use_tetris.get_grid_cell(col, row);
                    if cell.cell_type != GridCellType::Void {
                        let (x, y) = (col as f64 * CELL_SIZE, row as f64 * CELL_SIZE);
                        let square = graphics::rectangle::square(0.0f64, 0.0f64, CELL_SIZE - 1f64);
                        let color = match cell.cell_type {
                            GridCellType::Shape => get_shape_color(cell.shape_index),
                            GridCellType::Fixed => get_shape_color(cell.shape_index),
                            GridCellType::Ghost => DARK_GRAY,
                            _ => {
                                assert!(false);
                                BLACK
                            }
                        };
                        let transform = c.transform.trans(LEFT_MARGIN, TOP_MARGIN).trans(x, y);

                        //graphics::rectangle(color, square, transform, gl);

                        let mut rectangle = graphics::Rectangle::new(color);
                        rectangle.shape = graphics::rectangle::Shape::Round(4.0, 16);
                        rectangle.draw(square, &c.draw_state, transform, gl);
                    }
                }
            }
        });
    }

    fn update(&mut self, args: &UpdateArgs) {
        if self.tetris.get_game_over() {
            self.elapsed_time = 0.0;
        } else {
            // Here we increment the time elapsed between update()'s
            self.elapsed_time += args.dt;
            // if the elapsed time is now greater than the time allotted between ticks, then invoke tetris.tick()
            if self.elapsed_time > self.tetris.get_tick_time() as f64 {
                self.elapsed_time = 0.0;
                self.tetris.tick();
                self.should_redraw = true;
            }
        }
    }

    fn handle_input(&mut self, i: Input) {
        let prev_should_redraw = self.should_redraw;
        self.should_redraw = true;
        match i {
            Input::Press(Keyboard(Key::Left)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col - 1);
            },

            Input::Press(Keyboard(Key::Right)) => {
                let col: i32 = self.tetris.get_col();
                self.tetris.set_col(col + 1);
            },

            Input::Press(Keyboard(Key::Up)) => {
                self.tetris.rotate(true);
            },

            Input::Press(Keyboard(Key::Down)) => {
                let row: i32 = self.tetris.get_row() + 1;
                self.tetris.set_row(row);
            },

            Input::Press(Keyboard(Key::Space)) => {
                let mut row: i32 = self.tetris.get_row() + 1;
                while self.tetris.set_row(row) {
                    row += 1;
                }
                // hard drop immediately spawns next shape
                self.tetris.tick();
            },

            Input::Press(Keyboard(Key::N)) => {
                self.tetris.start_game();
            },

            Input::Press(Keyboard(Key::K)) => {
                if self.tetris.get_starting_level() > 0 {
                    let new_level: u32 = self.tetris.get_starting_level() - 1;
                    self.tetris.set_starting_level(new_level);
                }
            },

            Input::Press(Keyboard(Key::L)) => {
                if self.tetris.get_starting_level() < 30 {
                    let new_level: u32 = self.tetris.get_starting_level() + 1;
                    self.tetris.set_starting_level(new_level);
                }
            },

            _ => {
                self.should_redraw = prev_should_redraw;
            }
        }
    }
}

fn main() {
    start_app();
}

fn start_app() {
    let opengl = OpenGL::_3_2;

    let window = Glutin_Window::new(
        opengl,
        WindowSettings::new("Piston Tetris", [1024, 768])
                       .exit_on_esc(true)
                       .vsync(true)
                       //TODO causes error on ubuntu
                       //.samples(1)
    );

    let font_path = Path::new("FiraMono-Bold.ttf");

    let mut app = App {
        gl: GlGraphics::new(opengl),
        tetris: Tetris::new(),
        elapsed_time: 0.0,
        cache: GlyphCache::new(font_path).unwrap(),
        should_redraw: true,
    };

    for e in window.events().max_fps(60).ups(120) {
        match e {
            Event::Input(i) => {
                app.handle_input(i);
            }

            Event::Render(args) if app.should_redraw => {
                app.render(&args);
            }

            Event::Update(args) => {
                app.update(&args);
            }

            _ => {}
        }
    }
}<|endoftext|>


<commit_before>#![allow(
    // We support older compilers
    clippy::match_like_matches_macro,
)]

mod cmd;
mod config;
mod edit;
mod error;
mod fmt;
mod opts;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::str::FromStr;

use atty::Stream::{Stderr, Stdout};
use bat::assets::HighlightingAssets;
use bat::PrettyPrinter;
use quote::quote;
use structopt::StructOpt;
use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::cmd::Line;
use crate::config::Config;
use crate::error::Result;
use crate::opts::Coloring::*;
use crate::opts::{Args, Coloring, Opts};

fn main() {
    let result = cargo_expand_or_run_nightly();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(io::stderr(), "{}", err);
            1
        }
    });
}

fn cargo_expand_or_run_nightly() -> Result<i32> {
    const NO_RUN_NIGHTLY: &str = "CARGO_EXPAND_NO_RUN_NIGHTLY";

    if env::var_os(NO_RUN_NIGHTLY).is_some() || maybe_nightly() || !can_run_plus_nightly() {
        return cargo_expand();
    }

    let mut nightly = Command::new("cargo");
    nightly.arg("+nightly");
    nightly.arg("expand");

    let mut args = env::args_os().peekable();
    args.next().unwrap(); // cargo
    if args.peek().map_or(false, |arg| arg == "expand") {
        args.next().unwrap(); // expand
    }
    nightly.args(args);

    // Hopefully prevent infinite re-run loop.
    nightly.env(NO_RUN_NIGHTLY, "");

    let status = nightly.status()?;

    Ok(match status.code() {
        Some(code) => code,
        None => {
            if status.success() {
                0
            } else {
                1
            }
        }
    })
}

fn maybe_nightly() -> bool {
    !definitely_not_nightly()
}

fn definitely_not_nightly() -> bool {
    let mut cmd = Command::new(cargo_binary());
    cmd.arg("--version");

    let output = match cmd.output() {
        Ok(output) => output,
        Err(_) => return false,
    };

    let version = match String::from_utf8(output.stdout) {
        Ok(version) => version,
        Err(_) => return false,
    };

    version.starts_with("cargo 1") && !version.contains("nightly")
}

fn can_run_plus_nightly() -> bool {
    Command::new("cargo")
        .arg("+nightly")
        .arg("--version")
        .output()
        .map_or(false, |output| output.status.success())
}

fn cargo_binary() -> OsString {
    env::var_os("CARGO").unwrap_or_else(|| "cargo".to_owned().into())
}

fn cargo_expand() -> Result<i32> {
    let Opts::Expand(args) = Opts::from_args();
    let config = config::deserialize();

    if args.themes {
        for theme in HighlightingAssets::from_binary().themes() {
            let _ = writeln!(io::stdout(), "{}", theme);
        }
        return Ok(0);
    }

    let rustfmt;
    match (&args.item, args.ugly) {
        (Some(item), true) => {
            let _ = writeln!(
                io::stderr(),
                "ERROR: cannot expand single item ({}) in ugly mode.",
                item,
            );
            return Ok(1);
        }
        (Some(item), false) => {
            rustfmt = which_rustfmt();
            if rustfmt.is_none() {
                let _ = writeln!(
                    io::stderr(),
                    "ERROR: cannot expand single item ({}) without rustfmt.",
                    item,
                );
                let _ = writeln!(
                    io::stderr(),
                    "Install rustfmt by running `rustup component add rustfmt --toolchain nightly`.",
                );
                return Ok(1);
            }
        }
        (None, true) => rustfmt = None,
        (None, false) => rustfmt = which_rustfmt(),
    }

    let mut builder = tempfile::Builder::new();
    builder.prefix("cargo-expand");
    let outdir = builder.tempdir().expect("failed to create tmp file");
    let outfile_path = outdir.path().join("expanded");
    let color = get_color(&args, &config);

    // Run cargo
    let mut cmd = Command::new(cargo_binary());
    apply_args(&mut cmd, &args, &color, &outfile_path);
    let code = filter_err(&mut cmd, ignore_cargo_err)?;

    if !outfile_path.exists() {
        return Ok(1);
    }

    let mut content = fs::read_to_string(&outfile_path)?;
    if content.is_empty() {
        let _ = writeln!(io::stderr(), "ERROR: rustc produced no expanded output");
        return Ok(if code == 0 { 1 } else { code });
    }

    // Run rustfmt
    if let Some(rustfmt) = rustfmt {
        // Work around rustfmt not being able to parse paths containing $crate.
        // This placeholder should be the same width as $crate to preserve
        // alignments.
        const DOLLAR_CRATE_PLACEHOLDER: &str = "Ξcrate";
        content = content.replace("$crate", DOLLAR_CRATE_PLACEHOLDER);

        // Support cargo-expand built with panic=abort, as otherwise proc-macro2
        // ends up using a catch_unwind.
        proc_macro2::fallback::force();

        // Discard comments, which are misplaced by the compiler
        if let Ok(mut syntax_tree) = syn::parse_file(&content) {
            edit::sanitize(&mut syntax_tree);
            if let Some(filter) = args.item {
                syntax_tree.shebang = None;
                syntax_tree.attrs.clear();
                syntax_tree.items = filter.apply_to(&syntax_tree);
                if syntax_tree.items.is_empty() {
                    let _ = writeln!(io::stderr(), "WARNING: no such item: {}", filter);
                    return Ok(1);
                }
            }
            content = quote!(#syntax_tree).to_string();
        }
        fs::write(&outfile_path, content)?;

        fmt::write_rustfmt_config(&outdir)?;

        let output = Command::new(&rustfmt)
            .arg("--edition=2018")
            .arg(&outfile_path)
            .stderr(Stdio::null())
            .output();
        if let Ok(output) = output {
            if !output.status.success() {
                // Probably was the wrong edition.
                let _status = Command::new(&rustfmt)
                    .arg("--edition=2015")
                    .arg(&outfile_path)
                    .stderr(Stdio::null())
                    .status();
            }
        }

        content = fs::read_to_string(&outfile_path)?;
        content = content.replace(DOLLAR_CRATE_PLACEHOLDER, "$crate");
    }

    // Run pretty printer
    let theme = args.theme.or(config.theme);
    let none_theme = theme.as_deref() == Some("none");
    let do_color = match color {
        Always => true,
        Never => false,
        Auto => !none_theme && atty::is(Stdout),
    };
    let _ = writeln!(io::stderr());
    if do_color {
        let mut pretty_printer = PrettyPrinter::new();
        pretty_printer
            .input_from_bytes(content.as_bytes())
            .language("rust")
            .tab_width(Some(4))
            .true_color(false)
            .header(false)
            .line_numbers(false)
            .grid(false);
        if let Some(theme) = theme {
            pretty_printer.theme(theme);
        }

        // Ignore any errors.
        let _ = pretty_printer.print();
    } else {
        let _ = write!(io::stdout(), "{}", content);
    }

    Ok(0)
}

fn which_rustfmt() -> Option<PathBuf> {
    match env::var_os("RUSTFMT") {
        Some(which) => {
            if which.is_empty() {
                None
            } else {
                Some(PathBuf::from(which))
            }
        }
        None => toolchain_find::find_installed_component("rustfmt"),
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn apply_args(cmd: &mut Command, args: &Args, color: &Coloring, outfile: &Path) {
    let mut line = Line::new("cargo");

    line.arg("rustc");

    if args.tests && args.test.is_none() {
        line.arg("--profile=test");
    } else {
        line.arg("--profile=check");
    }

    if args.release {
        line.arg("--release");
    }

    if let Some(features) = &args.features {
        line.arg("--features");
        line.arg(features);
    }

    if args.all_features {
        line.arg("--all-features");
    }

    if args.no_default_features {
        line.arg("--no-default-features");
    }

    if args.lib {
        line.arg("--lib");
    }

    if let Some(bin) = &args.bin {
        line.arg("--bin");
        line.arg(bin);
    }

    if let Some(example) = &args.example {
        line.arg("--example");
        line.arg(example);
    }

    if let Some(test) = &args.test {
        line.arg("--test");
        line.arg(test);
    }

    if let Some(bench) = &args.bench {
        line.arg("--bench");
        line.arg(bench);
    }

    if let Some(target) = &args.target {
        line.arg("--target");
        line.arg(target);
    }

    if let Some(target_dir) = &args.target_dir {
        line.arg("--target-dir");
        line.arg(target_dir);
    }

    if let Some(manifest_path) = &args.manifest_path {
        line.arg("--manifest-path");
        line.arg(manifest_path);
    }

    if let Some(package) = &args.package {
        line.arg("--package");
        line.arg(package);
    }

    if let Some(jobs) = args.jobs {
        line.arg("--jobs");
        line.arg(jobs.to_string());
    }

    if args.verbose {
        line.arg("--verbose");
    }

    line.arg("--color");
    match color {
        Coloring::Auto => line.arg(if atty::is(Stderr) { "always" } else { "never" }),
        color => line.arg(color.to_string()),
    }

    if args.frozen {
        line.arg("--frozen");
    }

    if args.locked {
        line.arg("--locked");
    }

    for unstable_flag in &args.unstable_flags {
        line.arg("-Z");
        line.arg(unstable_flag);
    }

    line.arg("--");

    line.arg("-o");
    line.arg(outfile);
    line.arg("-Zunstable-options");
    line.arg("--pretty=expanded");

    if args.verbose {
        let mut display = line.clone();
        display.insert(0, "+nightly");
        print_command(display, color);
    }

    cmd.args(line);
}

fn print_command(line: Line, color: &Coloring) {
    let color_choice = match color {
        Coloring::Auto => ColorChoice::Auto,
        Coloring::Always => ColorChoice::Always,
        Coloring::Never => ColorChoice::Never,
    };

    let mut stream = StandardStream::stderr(color_choice);
    let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green)));
    let _ = write!(stream, "{:>12}", "Running");
    let _ = stream.reset();
    let _ = writeln!(stream, " `{}`", line);
}

fn filter_err(cmd: &mut Command, ignore: fn(&str) -> bool) -> io::Result<i32> {
    let mut child = cmd.stderr(Stdio::piped()).spawn()?;
    let mut stderr = io::BufReader::new(child.stderr.take().unwrap());
    let mut line = String::new();
    while let Ok(n) = stderr.read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore(&line) {
            let _ = write!(io::stderr(), "{}", line);
        }
        line.clear();
    }
    let code = child.wait()?.code().unwrap_or(1);
    Ok(code)
}

fn ignore_cargo_err(line: &str) -> bool {
    if line.trim().is_empty() {
        return true;
    }

    let discarded_lines = [
        "ignoring specified output filename because multiple outputs were \
         requested",
        "ignoring specified output filename for 'link' output because multiple \
         outputs were requested",
        "ignoring --out-dir flag due to -o flag",
        "ignoring -C extra-filename flag due to -o flag",
        "due to multiple output types requested, the explicitly specified \
         output file name will be adapted for each output type",
        "warning emitted",
        "warnings emitted",
    ];
    for s in &discarded_lines {
        if line.contains(s) {
            return true;
        }
    }

    false
}

fn get_color(args: &Args, config: &Config) -> Coloring {
    if let Some(value) = args.color {
        return value;
    }

    if let Some(value) = config.color.as_ref() {
        match Coloring::from_str(value.as_str()) {
            Ok(color) => return color,
            Err(err) => {
                let _ = writeln!(
                    io::stderr(),
                    "WARNING: invalid color in cargo config: {}",
                    err
                );
            }
        }
    }

    Coloring::Auto // default
}
<commit_msg>Suppress clippy manual_strip lint<commit_after>#![allow(
    // We support older compilers
    clippy::manual_strip,
    clippy::match_like_matches_macro,
)]

mod cmd;
mod config;
mod edit;
mod error;
mod fmt;
mod opts;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use std::str::FromStr;

use atty::Stream::{Stderr, Stdout};
use bat::assets::HighlightingAssets;
use bat::PrettyPrinter;
use quote::quote;
use structopt::StructOpt;
use termcolor::{Color::Green, ColorChoice, ColorSpec, StandardStream, WriteColor};

use crate::cmd::Line;
use crate::config::Config;
use crate::error::Result;
use crate::opts::Coloring::*;
use crate::opts::{Args, Coloring, Opts};

fn main() {
    let result = cargo_expand_or_run_nightly();
    process::exit(match result {
        Ok(code) => code,
        Err(err) => {
            let _ = writeln!(io::stderr(), "{}", err);
            1
        }
    });
}

fn cargo_expand_or_run_nightly() -> Result<i32> {
    const NO_RUN_NIGHTLY: &str = "CARGO_EXPAND_NO_RUN_NIGHTLY";

    if env::var_os(NO_RUN_NIGHTLY).is_some() || maybe_nightly() || !can_run_plus_nightly() {
        return cargo_expand();
    }

    let mut nightly = Command::new("cargo");
    nightly.arg("+nightly");
    nightly.arg("expand");

    let mut args = env::args_os().peekable();
    args.next().unwrap(); // cargo
    if args.peek().map_or(false, |arg| arg == "expand") {
        args.next().unwrap(); // expand
    }
    nightly.args(args);

    // Hopefully prevent infinite re-run loop.
    nightly.env(NO_RUN_NIGHTLY, "");

    let status = nightly.status()?;

    Ok(match status.code() {
        Some(code) => code,
        None => {
            if status.success() {
                0
            } else {
                1
            }
        }
    })
}

fn maybe_nightly() -> bool {
    !definitely_not_nightly()
}

fn definitely_not_nightly() -> bool {
    let mut cmd = Command::new(cargo_binary());
    cmd.arg("--version");

    let output = match cmd.output() {
        Ok(output) => output,
        Err(_) => return false,
    };

    let version = match String::from_utf8(output.stdout) {
        Ok(version) => version,
        Err(_) => return false,
    };

    version.starts_with("cargo 1") && !version.contains("nightly")
}

fn can_run_plus_nightly() -> bool {
    Command::new("cargo")
        .arg("+nightly")
        .arg("--version")
        .output()
        .map_or(false, |output| output.status.success())
}

fn cargo_binary() -> OsString {
    env::var_os("CARGO").unwrap_or_else(|| "cargo".to_owned().into())
}

fn cargo_expand() -> Result<i32> {
    let Opts::Expand(args) = Opts::from_args();
    let config = config::deserialize();

    if args.themes {
        for theme in HighlightingAssets::from_binary().themes() {
            let _ = writeln!(io::stdout(), "{}", theme);
        }
        return Ok(0);
    }

    let rustfmt;
    match (&args.item, args.ugly) {
        (Some(item), true) => {
            let _ = writeln!(
                io::stderr(),
                "ERROR: cannot expand single item ({}) in ugly mode.",
                item,
            );
            return Ok(1);
        }
        (Some(item), false) => {
            rustfmt = which_rustfmt();
            if rustfmt.is_none() {
                let _ = writeln!(
                    io::stderr(),
                    "ERROR: cannot expand single item ({}) without rustfmt.",
                    item,
                );
                let _ = writeln!(
                    io::stderr(),
                    "Install rustfmt by running `rustup component add rustfmt --toolchain nightly`.",
                );
                return Ok(1);
            }
        }
        (None, true) => rustfmt = None,
        (None, false) => rustfmt = which_rustfmt(),
    }

    let mut builder = tempfile::Builder::new();
    builder.prefix("cargo-expand");
    let outdir = builder.tempdir().expect("failed to create tmp file");
    let outfile_path = outdir.path().join("expanded");
    let color = get_color(&args, &config);

    // Run cargo
    let mut cmd = Command::new(cargo_binary());
    apply_args(&mut cmd, &args, &color, &outfile_path);
    let code = filter_err(&mut cmd, ignore_cargo_err)?;

    if !outfile_path.exists() {
        return Ok(1);
    }

    let mut content = fs::read_to_string(&outfile_path)?;
    if content.is_empty() {
        let _ = writeln!(io::stderr(), "ERROR: rustc produced no expanded output");
        return Ok(if code == 0 { 1 } else { code });
    }

    // Run rustfmt
    if let Some(rustfmt) = rustfmt {
        // Work around rustfmt not being able to parse paths containing $crate.
        // This placeholder should be the same width as $crate to preserve
        // alignments.
        const DOLLAR_CRATE_PLACEHOLDER: &str = "Ξcrate";
        content = content.replace("$crate", DOLLAR_CRATE_PLACEHOLDER);

        // Support cargo-expand built with panic=abort, as otherwise proc-macro2
        // ends up using a catch_unwind.
        proc_macro2::fallback::force();

        // Discard comments, which are misplaced by the compiler
        if let Ok(mut syntax_tree) = syn::parse_file(&content) {
            edit::sanitize(&mut syntax_tree);
            if let Some(filter) = args.item {
                syntax_tree.shebang = None;
                syntax_tree.attrs.clear();
                syntax_tree.items = filter.apply_to(&syntax_tree);
                if syntax_tree.items.is_empty() {
                    let _ = writeln!(io::stderr(), "WARNING: no such item: {}", filter);
                    return Ok(1);
                }
            }
            content = quote!(#syntax_tree).to_string();
        }
        fs::write(&outfile_path, content)?;

        fmt::write_rustfmt_config(&outdir)?;

        let output = Command::new(&rustfmt)
            .arg("--edition=2018")
            .arg(&outfile_path)
            .stderr(Stdio::null())
            .output();
        if let Ok(output) = output {
            if !output.status.success() {
                // Probably was the wrong edition.
                let _status = Command::new(&rustfmt)
                    .arg("--edition=2015")
                    .arg(&outfile_path)
                    .stderr(Stdio::null())
                    .status();
            }
        }

        content = fs::read_to_string(&outfile_path)?;
        content = content.replace(DOLLAR_CRATE_PLACEHOLDER, "$crate");
    }

    // Run pretty printer
    let theme = args.theme.or(config.theme);
    let none_theme = theme.as_deref() == Some("none");
    let do_color = match color {
        Always => true,
        Never => false,
        Auto => !none_theme && atty::is(Stdout),
    };
    let _ = writeln!(io::stderr());
    if do_color {
        let mut pretty_printer = PrettyPrinter::new();
        pretty_printer
            .input_from_bytes(content.as_bytes())
            .language("rust")
            .tab_width(Some(4))
            .true_color(false)
            .header(false)
            .line_numbers(false)
            .grid(false);
        if let Some(theme) = theme {
            pretty_printer.theme(theme);
        }

        // Ignore any errors.
        let _ = pretty_printer.print();
    } else {
        let _ = write!(io::stdout(), "{}", content);
    }

    Ok(0)
}

fn which_rustfmt() -> Option<PathBuf> {
    match env::var_os("RUSTFMT") {
        Some(which) => {
            if which.is_empty() {
                None
            } else {
                Some(PathBuf::from(which))
            }
        }
        None => toolchain_find::find_installed_component("rustfmt"),
    }
}

// Based on https://github.com/rsolomo/cargo-check
fn apply_args(cmd: &mut Command, args: &Args, color: &Coloring, outfile: &Path) {
    let mut line = Line::new("cargo");

    line.arg("rustc");

    if args.tests && args.test.is_none() {
        line.arg("--profile=test");
    } else {
        line.arg("--profile=check");
    }

    if args.release {
        line.arg("--release");
    }

    if let Some(features) = &args.features {
        line.arg("--features");
        line.arg(features);
    }

    if args.all_features {
        line.arg("--all-features");
    }

    if args.no_default_features {
        line.arg("--no-default-features");
    }

    if args.lib {
        line.arg("--lib");
    }

    if let Some(bin) = &args.bin {
        line.arg("--bin");
        line.arg(bin);
    }

    if let Some(example) = &args.example {
        line.arg("--example");
        line.arg(example);
    }

    if let Some(test) = &args.test {
        line.arg("--test");
        line.arg(test);
    }

    if let Some(bench) = &args.bench {
        line.arg("--bench");
        line.arg(bench);
    }

    if let Some(target) = &args.target {
        line.arg("--target");
        line.arg(target);
    }

    if let Some(target_dir) = &args.target_dir {
        line.arg("--target-dir");
        line.arg(target_dir);
    }

    if let Some(manifest_path) = &args.manifest_path {
        line.arg("--manifest-path");
        line.arg(manifest_path);
    }

    if let Some(package) = &args.package {
        line.arg("--package");
        line.arg(package);
    }

    if let Some(jobs) = args.jobs {
        line.arg("--jobs");
        line.arg(jobs.to_string());
    }

    if args.verbose {
        line.arg("--verbose");
    }

    line.arg("--color");
    match color {
        Coloring::Auto => line.arg(if atty::is(Stderr) { "always" } else { "never" }),
        color => line.arg(color.to_string()),
    }

    if args.frozen {
        line.arg("--frozen");
    }

    if args.locked {
        line.arg("--locked");
    }

    for unstable_flag in &args.unstable_flags {
        line.arg("-Z");
        line.arg(unstable_flag);
    }

    line.arg("--");

    line.arg("-o");
    line.arg(outfile);
    line.arg("-Zunstable-options");
    line.arg("--pretty=expanded");

    if args.verbose {
        let mut display = line.clone();
        display.insert(0, "+nightly");
        print_command(display, color);
    }

    cmd.args(line);
}

fn print_command(line: Line, color: &Coloring) {
    let color_choice = match color {
        Coloring::Auto => ColorChoice::Auto,
        Coloring::Always => ColorChoice::Always,
        Coloring::Never => ColorChoice::Never,
    };

    let mut stream = StandardStream::stderr(color_choice);
    let _ = stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(Green)));
    let _ = write!(stream, "{:>12}", "Running");
    let _ = stream.reset();
    let _ = writeln!(stream, " `{}`", line);
}

fn filter_err(cmd: &mut Command, ignore: fn(&str) -> bool) -> io::Result<i32> {
    let mut child = cmd.stderr(Stdio::piped()).spawn()?;
    let mut stderr = io::BufReader::new(child.stderr.take().unwrap());
    let mut line = String::new();
    while let Ok(n) = stderr.read_line(&mut line) {
        if n == 0 {
            break;
        }
        if !ignore(&line) {
            let _ = write!(io::stderr(), "{}", line);
        }
        line.clear();
    }
    let code = child.wait()?.code().unwrap_or(1);
    Ok(code)
}

fn ignore_cargo_err(line: &str) -> bool {
    if line.trim().is_empty() {
        return true;
    }

    let discarded_lines = [
        "ignoring specified output filename because multiple outputs were \
         requested",
        "ignoring specified output filename for 'link' output because multiple \
         outputs were requested",
        "ignoring --out-dir flag due to -o flag",
        "ignoring -C extra-filename flag due to -o flag",
        "due to multiple output types requested, the explicitly specified \
         output file name will be adapted for each output type",
        "warning emitted",
        "warnings emitted",
    ];
    for s in &discarded_lines {
        if line.contains(s) {
            return true;
        }
    }

    false
}

fn get_color(args: &Args, config: &Config) -> Coloring {
    if let Some(value) = args.color {
        return value;
    }

    if let Some(value) = config.color.as_ref() {
        match Coloring::from_str(value.as_str()) {
            Ok(color) => return color,
            Err(err) => {
                let _ = writeln!(
                    io::stderr(),
                    "WARNING: invalid color in cargo config: {}",
                    err
                );
            }
        }
    }

    Coloring::Auto // default
}
<|endoftext|>