<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate rustc_serialize;

use std::env;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::parse_config;
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};
use rustc_serialize::json;

use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::child_config::ChildConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    try!(try!(scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = try!(parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()));
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            let all_children: BTreeMap<String, ChildConfig>;
            all_children = try!(parse_config(&cfg, &childval, Default::default())
                .map_err(|e| format!("Can't read child config {:?}: {}",
                                     sandbox_config.config_file, e)));
            for child in all_children.values() {
                // Current are always added
                images.insert(sandbox_config.image_dir.join(&child.image));
            }

            let logname = master.config_log_dir
                .join(format!("{}.log", sandbox_name));
            // TODO(tailhook) look in log rotations
            let log = try!(File::open(&logname)
                .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
            let mut configs;
            configs = VecDeque::<(Tm, BTreeMap<String, ChildConfig>)>::new();
            for (line_no, line) in BufReader::new(log).lines().enumerate() {
                let line = try!(line
                    .map_err(|e| format!("Readline error: {}", e)));
                let mut iter = line.splitn(2, " ");
                let (tm, cfg) = match (iter.next(), iter.next()) {
                        (Some(""), None) => continue, // last line, probably
                        (Some(date), Some(config)) => {
                            // TODO(tailhook) remove or check sandbox name
                            (try!(time::strptime(date, "%Y-%m-%dT%H:%M:%SZ")
                                 .map_err(|_| format!("Bad time at {:?}:{}",
                                    logname, line_no))),
                             try!(json::decode(config)
                                 .map_err(|_| format!("Bad config at {:?}:{}",
                                    logname, line_no))),
                            )
                        }
                        _ => {
                            return Err(format!("Bad line at {:?}:{}",
                                               logname, line_no));
                        }
                    };
                if let Some(&(_, ref ocfg)) = configs.back(){
                    if *ocfg == cfg {
                        continue;
                    }
                }
                configs.push_back((tm, cfg));
                if configs.len() >= ver_max as usize {
                    configs.pop_front();
                }
            }
            min_time.map(|min_time| {
                while configs.len() > ver_min as usize {
                    if let Some(&(tm, _)) = configs.front() {
                        if tm > min_time {
                            break;
                        }
                    } else {
                        break;
                    }
                    configs.pop_front();
                }
            });
            for &(_, ref cfg) in configs.iter() {
                for child in cfg.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))));
    Ok((images, image_dirs))
}
<commit_msg>Ignore sandboxes without processes and configs<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate argparse;
extern crate quire;
extern crate lithos;
extern crate time;
extern crate scan_dir;
extern crate rustc_serialize;

use std::env;
use std::io::{BufReader, BufRead};
use std::fs::{File, remove_dir_all};
use std::path::{PathBuf, Path};
use std::process::exit;
use std::collections::HashSet;
use std::collections::BTreeMap;
use std::collections::VecDeque;

use time::{Tm, Duration, now_utc};
use quire::parse_config;
use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, StoreConst};
use argparse::{Print};
use rustc_serialize::json;

use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::child_config::ChildConfig;

#[derive(Clone, Copy, Debug)]
enum Action {
    Used,
    Unused,
    DeleteUnused,
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();
    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut ver_min = 0;
    let mut ver_max = 1000;
    let mut action = Action::Used;
    let mut days = None::<u32>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Show used/unused images and clean if needed");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut days)
          .add_option(&["-D", "--history-days"], ParseOption,
            r"Keep images that used no more than DAYS ago.
              There is no reasonable default, so you should specify this
              argument or --versions-min to get sane behavior.")
          .metavar("DAYS");
        ap.refer(&mut ver_min)
          .add_option(&["--vmin", "--versions-min"], Parse,
            r"Keep minimum NUM versions (even if they are older than DAYS).
              Default is 0 which means keep only current version.")
          .metavar("NUM");
        ap.refer(&mut ver_max)
          .add_option(&["--vmax", "--versions-max"], Parse,
            r"Keep maximum NUM versions
              (even if need to delete images more recent than DAYS).
              Default is 1000.")
          .metavar("NUM");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut action)
          .add_option(&["--used"], StoreConst(Action::Used),
            "Show used images")
          .add_option(&["--unused"], StoreConst(Action::Unused),
            "Show unused images")
          .add_option(&["--delete-unused"], StoreConst(Action::DeleteUnused),
            "Delete unused images");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version of the lithos");
        ap.parse_args_or_exit();
    }
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            exit(1);
        }
    };
    let tm = days.map(|days| now_utc() - Duration::days(days as i64));
    let (used, dirs) = match find_used_images(&master, &config_file,
        tm, ver_min, ver_max)
    {
        Ok((used, dirs)) => (used, dirs),
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    match action {
        Action::Used => {
            for i in used {
                println!("{:?}", i);
            }
        }
        Action::Unused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                println!("{:?}", i);
            }
        }
        Action::DeleteUnused => {
            let unused = find_unused(&used, &dirs)
                .map_err(|e| {
                    error!("Error finding unused images: {:?}", e);
                    exit(2);
                })
                .unwrap();
            for i in unused {
                if verbose {
                    println!("Deleting {:?}", i);
                }
                remove_dir_all(&i)
                    .map_err(|e| error!("Error removing {:?}: {}", i, e)).ok();
            }
        }
    }
}

fn find_unused(used: &HashSet<PathBuf>, dirs: &HashSet<PathBuf>)
    -> Result<Vec<PathBuf>, scan_dir::Error>
{
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        if !dir.exists() {
            warn!("Directory {:?} does not exists", dir);
            continue;
        }
        try!(scan_dir::ScanDir::dirs().skip_symlinks(true).read(dir, |iter| {
            for (entry, _) in iter {
                let path = entry.path().to_path_buf();
                if !used.contains(&path) {
                    unused.push(path);
                }
            }
        }));
    }
    Ok(unused)
}

fn find_used_images(master: &MasterConfig, master_file: &Path,
    min_time: Option<Tm>, ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let config_dir = master_file.parent().unwrap().join(&master.sandboxes_dir);
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = ChildConfig::mapping_validator();
    try!(try!(scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, sandbox_fname) in yamls {
            let sandbox_name = &sandbox_fname[..sandbox_fname.len()-5];  // strip .yaml
            let sandbox_config: SandboxConfig = try!(parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()));
            image_dirs.insert(sandbox_config.image_dir.clone());

            let cfg = master_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox_config.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&(sandbox_name.to_string() + ".yaml"))));
            if cfg.exists() {
                let all_children: BTreeMap<String, ChildConfig>;
                all_children = try!(parse_config(&cfg, &childval, Default::default())
                    .map_err(|e| format!("Can't read child config {:?}: {}",
                                         sandbox_config.config_file, e)));
                for child in all_children.values() {
                    // Current are always added
                    images.insert(sandbox_config.image_dir.join(&child.image));
                }
            } else {
                info!("No current processes for {}", sandbox_name);
            }

            let logname = master.config_log_dir
                .join(format!("{}.log", sandbox_name));
            if logname.exists() {
                // TODO(tailhook) look in log rotations
                let log = try!(File::open(&logname)
                    .map_err(|e| format!("Can't read log file {:?}: {}", logname, e)));
                let mut configs;
                configs = VecDeque::<(Tm, BTreeMap<String, ChildConfig>)>::new();
                for (line_no, line) in BufReader::new(log).lines().enumerate() {
                    let line = try!(line
                        .map_err(|e| format!("Readline error: {}", e)));
                    let mut iter = line.splitn(2, " ");
                    let (tm, cfg) = match (iter.next(), iter.next()) {
                            (Some(""), None) => continue, // last line, probably
                            (Some(date), Some(config)) => {
                                // TODO(tailhook) remove or check sandbox name
                                (try!(time::strptime(date, "%Y-%m-%dT%H:%M:%SZ")
                                     .map_err(|_| format!("Bad time at {:?}:{}",
                                        logname, line_no))),
                                 try!(json::decode(config)
                                     .map_err(|_| format!("Bad config at {:?}:{}",
                                        logname, line_no))),
                                )
                            }
                            _ => {
                                return Err(format!("Bad line at {:?}:{}",
                                                   logname, line_no));
                            }
                        };
                    if let Some(&(_, ref ocfg)) = configs.back(){
                        if *ocfg == cfg {
                            continue;
                        }
                    }
                    configs.push_back((tm, cfg));
                    if configs.len() >= ver_max as usize {
                        configs.pop_front();
                    }
                }
                min_time.map(|min_time| {
                    while configs.len() > ver_min as usize {
                        if let Some(&(tm, _)) = configs.front() {
                            if tm > min_time {
                                break;
                            }
                        } else {
                            break;
                        }
                        configs.pop_front();
                    }
                });
                for &(_, ref cfg) in configs.iter() {
                    for child in cfg.values() {
                        // Current are always added
                        images.insert(sandbox_config.image_dir.join(&child.image));
                    }
                }
            } else {
                info!("No log for {} probably never used", sandbox_name);
            }
        }
        Ok(())
    }).map_err(|e| format!("Read dir error: {}", e))));
    Ok((images, image_dirs))
}
<|endoftext|>


<commit_before>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::io;
use inflections::Inflect;

use WriteRust;
use common::Spec;

pub struct MethodModuleWriter<'a> {
    specs: &'a [Spec]
}

impl<'a> MethodModuleWriter<'a> {
    pub fn new(specs: &'a [Spec]) -> Self {
        MethodModuleWriter {
            specs: specs
        }
    }

    fn class_names(&self) -> HashSet<&'a str> {
        self.specs.iter()
            .flat_map(|spec| spec.classes())
            .map(|class| class.name())
            .collect()
    }

    fn class_methods(&'a self) -> BTreeMap<&'a str, BTreeSet<&'a str>> {
        self.class_names().iter()
            .filter_map(|class_name| {
                let method_names = self.specs.iter()
                    .filter_map(|spec| spec.class(class_name))
                    .flat_map(|class| class.methods())
                    .map(|method| method.name())
                    .collect::<BTreeSet<_>>();

                if method_names.is_empty() {
                    None
                } else {
                    Some((*class_name, method_names))
                }
            })
            .collect()
    }

    fn class_method_fields(&'a self) -> BTreeMap<&'a str, BTreeMap<&'a str, BTreeSet<&'a str>>> {
        self.class_methods().into_iter()
            .map(|(class_name, method_names)| {
                // class_name
                let class_methods = method_names.into_iter()
                    .map(|method_name| {
                        // method_name
                        let method_fields = self.specs.iter()
                            .filter_map(|spec| spec.class(class_name))
                            .filter_map(|class| class.method(method_name))
                            .flat_map(|method| method.fields())
                            .filter(|field| !field.is_reserved())
                            .map(|field| field.var_name())
                            .collect();
                        (method_name, method_fields)
                        // end method_name
                    })
                    .collect();
                (class_name, class_methods)
                // end class name
            })
            .collect()
    }
}

impl<'a> WriteRust for MethodModuleWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        for (class_name, class_methods) in self.class_method_fields() {
            let class_snake = class_name.to_snake_case();
            try!(writeln!(writer, "\npub mod {} {{", class_snake));

            for (method_name, field_names) in class_methods {
                let method_pascal = method_name.to_pascal_case();
                if field_names.is_empty() {
                    try!(writeln!(writer, "pub trait {}Method {{}}", method_pascal));
                    continue;
                }

                let has_lifetimes = self.specs.iter()
                    .filter_map(|spec| spec.class(class_name))
                    .filter_map(|class| class.method(method_name))
                    .any(|method| method.has_lifetimes());

                let lifetimes = if has_lifetimes { "<'a>" } else { "" };
                let section = format!("pub trait {}Method{}", method_pascal, lifetimes);

                try!(writeln!(writer, "{} {{", section));
                for field_name in field_names {
                    let tys = self.specs.iter()
                        .filter_map(|spec| spec.class(class_name))
                        .filter_map(|class| class.method(method_name))
                        .flat_map(|method| method.fields())
                        .filter(|field| field.var_name() == field_name)
                        .map(|field| (field.ty()))
                        .map(|ty| (ty.owned_type(), ty))
                        .collect::<HashMap<_, _>>();

                    if tys.len() > 1 {
                        panic!(
                            "Conflicting types for {}::{}::{}",
                            class_name,
                            method_name,
                            field_name
                        );
                    }

                    let var_name = field_name.to_snake_case();
                    let ty = tys.values().next().unwrap();
                    if ty.is_copy() {
                        try!(writeln!(
                                writer,
                            "fn set_{0}(_: {1}) {{}}",
                            var_name,
                            ty.owned_type()
                        ));
                    }
                    else {
                        try!(writeln!(
                            writer,
                            "fn set_{0}<V>(_: V) where V: Into<{1}> {{}}",
                            var_name,
                            ty.cow_definition("a")
                        ));
                    }
                }
                try!(writeln!(writer, "}} // {}\n", section));
            }
            try!(writeln!(writer, "}} // mod {}", class_snake));
        }

        Ok(())
    }
}<commit_msg>[primalgen] Use into_iter() instead of iter() in MethodModuleWriter::class_methods()<commit_after>// Copyright 2016 Alexander Reece
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::io;
use inflections::Inflect;

use WriteRust;
use common::Spec;

pub struct MethodModuleWriter<'a> {
    specs: &'a [Spec]
}

impl<'a> MethodModuleWriter<'a> {
    pub fn new(specs: &'a [Spec]) -> Self {
        MethodModuleWriter {
            specs: specs
        }
    }

    fn class_names(&self) -> HashSet<&'a str> {
        self.specs.iter()
            .flat_map(|spec| spec.classes())
            .map(|class| class.name())
            .collect()
    }

    fn class_methods(&'a self) -> BTreeMap<&'a str, BTreeSet<&'a str>> {
        self.class_names().into_iter()
            .filter_map(|class_name| {
                let method_names = self.specs.iter()
                    .filter_map(|spec| spec.class(class_name))
                    .flat_map(|class| class.methods())
                    .map(|method| method.name())
                    .collect::<BTreeSet<_>>();

                if method_names.is_empty() {
                    None
                } else {
                    Some((class_name, method_names))
                }
            })
            .collect()
    }

    fn class_method_fields(&'a self) -> BTreeMap<&'a str, BTreeMap<&'a str, BTreeSet<&'a str>>> {
        self.class_methods().into_iter()
            .map(|(class_name, method_names)| {
                // class_name
                let class_methods = method_names.into_iter()
                    .map(|method_name| {
                        // method_name
                        let method_fields = self.specs.iter()
                            .filter_map(|spec| spec.class(class_name))
                            .filter_map(|class| class.method(method_name))
                            .flat_map(|method| method.fields())
                            .filter(|field| !field.is_reserved())
                            .map(|field| field.var_name())
                            .collect();
                        (method_name, method_fields)
                        // end method_name
                    })
                    .collect();
                (class_name, class_methods)
                // end class name
            })
            .collect()
    }
}

impl<'a> WriteRust for MethodModuleWriter<'a> {
    fn write_rust_to<W>(&self, writer: &mut W) -> io::Result<()>
        where W: io::Write
    {
        for (class_name, class_methods) in self.class_method_fields() {
            let class_snake = class_name.to_snake_case();
            try!(writeln!(writer, "\npub mod {} {{", class_snake));

            for (method_name, field_names) in class_methods {
                let method_pascal = method_name.to_pascal_case();
                if field_names.is_empty() {
                    try!(writeln!(writer, "pub trait {}Method {{}}", method_pascal));
                    continue;
                }

                let has_lifetimes = self.specs.iter()
                    .filter_map(|spec| spec.class(class_name))
                    .filter_map(|class| class.method(method_name))
                    .any(|method| method.has_lifetimes());

                let lifetimes = if has_lifetimes { "<'a>" } else { "" };
                let section = format!("pub trait {}Method{}", method_pascal, lifetimes);

                try!(writeln!(writer, "{} {{", section));
                for field_name in field_names {
                    let tys = self.specs.iter()
                        .filter_map(|spec| spec.class(class_name))
                        .filter_map(|class| class.method(method_name))
                        .flat_map(|method| method.fields())
                        .filter(|field| field.var_name() == field_name)
                        .map(|field| (field.ty()))
                        .map(|ty| (ty.owned_type(), ty))
                        .collect::<HashMap<_, _>>();

                    if tys.len() > 1 {
                        panic!(
                            "Conflicting types for {}::{}::{}",
                            class_name,
                            method_name,
                            field_name
                        );
                    }

                    let var_name = field_name.to_snake_case();
                    let ty = tys.values().next().unwrap();
                    if ty.is_copy() {
                        try!(writeln!(
                                writer,
                            "fn set_{0}(_: {1}) {{}}",
                            var_name,
                            ty.owned_type()
                        ));
                    }
                    else {
                        try!(writeln!(
                            writer,
                            "fn set_{0}<V>(_: V) where V: Into<{1}> {{}}",
                            var_name,
                            ty.cow_definition("a")
                        ));
                    }
                }
                try!(writeln!(writer, "}} // {}\n", section));
            }
            try!(writeln!(writer, "}} // mod {}", class_snake));
        }

        Ok(())
    }
}<|endoftext|>


<commit_before>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::widget_util::{block, text};
use crate::tui::{state::SharedState, widget_util::widget::WrapHelper};
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::{task, time};
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::{Span, Text};
use tui::widgets::Paragraph;
use tui_utils::{
    grid_pos,
    widgets::{Fragment, OverflowMode, SpanOptions, TextFragments},
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn text_display_layout(rect: Rect) -> Vec<Rect> {
        Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(2), Constraint::Percentage(100)])
            .margin(2)
            .split(rect)
    }

    fn draw_text_panel<'a, B, T>(header: Span, body: T, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        T: Into<Text<'a>>,
    {
        let layout = Self::text_display_layout(rect);

        let header_widget = Paragraph::new(header).alignment(Alignment::Center);
        frame.render_widget(header_widget, layout[0]);

        let body_widget = Paragraph::new(body).alignment(Alignment::Center).wrapped();
        frame.render_widget(body_widget, layout[1]);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add an account by pressing 'u' to open".into(),
            "user management and then by pressing tab".into(),
            "to switch to the add user panel.".into(),
            "".into(),
            "Then open the auth URL in your browser".into(),
            "by pressing Ctrl + O, and follow its instructions.".into(),
            "Once you have a token, paste it in with either".into(),
            "Ctrl + Shift + V or Ctrl + V.".into(),
            "".into(),
            "More detailed instructions here:".into(),
            "https://github.com/Acizza/anup#adding-an-account".into(),
        ];

        Self::draw_text_panel(text::bold("No Accounts Added"), body, rect, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add one by pressing the 'a' key.".into(),
            "".into(),
            "The opened panel will require you to specify".into(),
            "a name for the series you want to add.".into(),
            "".into(),
            "For automatic detection, the name should be".into(),
            "similar to the name of the folder the series".into(),
            "is in on disk.".into(),
        ];

        Self::draw_text_panel(text::bold("No Series Found"), body, rect, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));
        let body = text::with_color(err.to_string(), Color::Red);

        Self::draw_text_panel(header, body, rect, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = grid_pos(
                    Rect {
                        x: $x_column,
                        y: $y_column,
                        width: content.width / 3,
                        height: content.height / 3,
                    },
                    content,
                );

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Watch time needed indicator at bottom
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, layout[2]);
        } else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, layout[2]);
        }
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<commit_msg>anup: Move info panel status message rendering to its own function<commit_after>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::widget_util::{block, text};
use crate::tui::{state::SharedState, widget_util::widget::WrapHelper};
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::{task, time};
use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::{Span, Text};
use tui::widgets::Paragraph;
use tui_utils::{
    grid_pos,
    widgets::{Fragment, OverflowMode, SpanOptions, TextFragments},
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn text_display_layout(rect: Rect) -> Vec<Rect> {
        Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(2), Constraint::Percentage(100)])
            .margin(2)
            .split(rect)
    }

    fn draw_text_panel<'a, B, T>(header: Span, body: T, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        T: Into<Text<'a>>,
    {
        let layout = Self::text_display_layout(rect);

        let header_widget = Paragraph::new(header).alignment(Alignment::Center);
        frame.render_widget(header_widget, layout[0]);

        let body_widget = Paragraph::new(body).alignment(Alignment::Center).wrapped();
        frame.render_widget(body_widget, layout[1]);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add an account by pressing 'u' to open".into(),
            "user management and then by pressing tab".into(),
            "to switch to the add user panel.".into(),
            "".into(),
            "Then open the auth URL in your browser".into(),
            "by pressing Ctrl + O, and follow its instructions.".into(),
            "Once you have a token, paste it in with either".into(),
            "Ctrl + Shift + V or Ctrl + V.".into(),
            "".into(),
            "More detailed instructions here:".into(),
            "https://github.com/Acizza/anup#adding-an-account".into(),
        ];

        Self::draw_text_panel(text::bold("No Accounts Added"), body, rect, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let body = vec![
            "Add one by pressing the 'a' key.".into(),
            "".into(),
            "The opened panel will require you to specify".into(),
            "a name for the series you want to add.".into(),
            "".into(),
            "For automatic detection, the name should be".into(),
            "similar to the name of the folder the series".into(),
            "is in on disk.".into(),
        ];

        Self::draw_text_panel(text::bold("No Series Found"), body, rect, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));
        let body = text::with_color(err.to_string(), Color::Red);

        Self::draw_text_panel(header, body, rect, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(rect);

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = grid_pos(
                    Rect {
                        x: $x_column,
                        y: $y_column,
                        width: content.width / 3,
                        height: content.height / 3,
                    },
                    content,
                );

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        self.draw_status_text(state, layout[2], frame);
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    fn draw_status_text<B: Backend>(&self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Remaining time until progression
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
        // Login message
        else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<|endoftext|>


<commit_before>/// Error type
///
/// `cfitsio` passes errors through integer status codes. This struct wraps this and its associated
/// error message.
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    pub status: i32,
    pub message: String,
}

/// Macro for returning a FITS error type
macro_rules! fits_try {
    ($status: ident, $e: expr) => {
        match $status {
            0 => Ok($e),
            _ => {
                Err(Error::Fits(FitsError {
                    status: $status,
                    // unwrap guaranteed to work as we know $status > 0
                    message: stringutils::status_to_string($status).unwrap().unwrap(),
                }))
            }
        }
    }
}
<commit_msg>Add function for checking the fits status<commit_after>use errors::{Error, Result};
use stringutils::status_to_string;

/// Error type
///
/// `cfitsio` passes errors through integer status codes. This struct wraps this and its associated
/// error message.
#[derive(Debug, PartialEq, Eq)]
pub struct FitsError {
    pub status: i32,
    pub message: String,
}

/// Function for chaining result types
pub fn check_status(status: i32) -> Result<()> {
    match status {
        0 => Ok(()),
        _ => Err(Error::Fits(FitsError {
            status: status,
            message: status_to_string(status).unwrap().unwrap(),
        })),
    }
}

/// Macro for returning a FITS error type
macro_rules! fits_try {
    ($status: ident, $e: expr) => {
        match $status {
            0 => Ok($e),
            _ => {
                Err(Error::Fits(FitsError {
                    status: $status,
                    // unwrap guaranteed to work as we know $status > 0
                    message: stringutils::status_to_string($status).unwrap().unwrap(),
                }))
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use library;
use super::general::tabs;

pub fn generate<W: Write>(w: &mut W, analysis: &analysis::functions::Info,
    in_trait: bool, only_declaration: bool, indent: i32) -> Result<()> {

    let comment_prefix = if analysis.comented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };
    let declaration = declaration(analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w, "{}{}{}{}{}", tabs(indent),
        comment_prefix, pub_prefix, declaration, suffix));

    if !only_declaration {
        try!(writeln!(w, "{}unsafe {{", tabs(indent + 1)));
        try!(writeln!(w, "{}TODO: Body", tabs(indent + 2)));
        try!(writeln!(w, "{}}}", tabs(indent + 1)));
        try!(writeln!(w, "{}}}", tabs(indent)));
    }

    Ok(())
}

pub fn declaration(analysis: &analysis::functions::Info) -> String {
    //TODO: Optional constructors if any
    //TODO: return values
    let return_str = if analysis.kind == library::FunctionKind::Constructor {
        " -> Self"  //TODO: actual type
    } else {
        "TODO"
    };
    format!("fn {}(TODO: Params){}", analysis.name, return_str)
}
<commit_msg>Add codegen::function::body()<commit_after>use std::io::{Result, Write};

use analysis;
use library;
use super::general::tabs;

pub fn generate<W: Write>(w: &mut W, analysis: &analysis::functions::Info,
    in_trait: bool, only_declaration: bool, indent: i32) -> Result<()> {

    let comment_prefix = if analysis.comented { "//" } else { "" };
    let pub_prefix = if in_trait { "" } else { "pub " };
    let declaration = declaration(analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    try!(writeln!(w, "{}{}{}{}{}", tabs(indent),
        comment_prefix, pub_prefix, declaration, suffix));

    if !only_declaration {
        let body = body(analysis);
        for s in body {
            try!(writeln!(w, "{}{}", tabs(indent + 1), s));
        }
        try!(writeln!(w, "{}}}", tabs(indent)));
    }

    Ok(())
}

pub fn declaration(analysis: &analysis::functions::Info) -> String {
    //TODO: Optional constructors if any
    //TODO: return values
    let return_str = if analysis.kind == library::FunctionKind::Constructor {
        " -> Self"  //TODO: actual type
    } else {
        "TODO"
    };
    format!("fn {}(TODO: Params){}", analysis.name, return_str)
}

pub fn body(analysis: &analysis::functions::Info) -> Vec<String> {
    let mut v: Vec<String> = Vec::new();
    //TODO: real generation
    //TODO: use trait Write
    v.push("unsafe {".into());
    v.push(format!("{}TODO: call ffi:{}()", tabs(1), analysis.glib_name).into());
    v.push("}".into());
    v
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::hash_map::Entry;

use types;
use types::{AliasData, Type, Generic, TcType, TypeEnv};
use symbol::Symbol;
use fnv::FnvMap;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        SelfRecursive(id: Symbol) {
            description("self recursive")
            display("The use of self recursion in type `{}` could not be unified.", id)
        }
        UndefinedType(id: Symbol) {
            description("undefined type")
            display("Type `{}` does not exist.", id)
        }
    }
}

/// Removes type aliases from `typ` until it is an actual type
pub fn remove_aliases(env: &TypeEnv, mut typ: TcType) -> TcType {
    while let Ok(Some(new)) = maybe_remove_alias(env, &typ) {
        typ = new;
    }
    typ
}

pub fn remove_aliases_cow<'t>(env: &TypeEnv, typ: &'t TcType) -> Cow<'t, TcType> {
    let mut typ = match maybe_remove_alias(env, typ) {
        Ok(Some(new)) => new,
        _ => return Cow::Borrowed(typ),
    };
    while let Ok(Some(new)) = maybe_remove_alias(env, &typ) {
        typ = new;
    }
    Cow::Owned(typ)
}

/// Removes all possible aliases while checking that
pub fn remove_aliases_checked(reduced_aliases: &mut Vec<Symbol>,
                              env: &TypeEnv,
                              typ: &TcType)
                              -> Result<Option<TcType>, Error> {
    if let Some((alias_id, _)) = typ.as_alias() {
        if reduced_aliases.iter().any(|name| name == alias_id) {
            return Err(Error::SelfRecursive(alias_id.clone()));
        }
        reduced_aliases.push(alias_id.clone());
    }
    let mut typ = match try!(maybe_remove_alias(env, typ)) {
        Some(new) => new,
        None => return Ok(None),
    };
    loop {
        if let Some((alias_id, _)) = typ.as_alias() {
            if reduced_aliases.iter().any(|name| name == alias_id) {
                return Err(Error::SelfRecursive(alias_id.clone()));
            }
            reduced_aliases.push(alias_id.clone());
        }
        match try!(maybe_remove_alias(env, &typ)) {
            Some(new) => typ = new,
            None => break,
        }
    }
    Ok(Some(typ))
}

pub fn remove_alias(env: &TypeEnv, typ: TcType) -> TcType {
    maybe_remove_alias(env, &typ).unwrap_or(None).unwrap_or(typ)
}

/// Expand `typ` if it is an alias that can be expanded and return the expanded type.
/// Returns `None` if the type is not an alias or the alias could not be expanded.
pub fn maybe_remove_alias(env: &TypeEnv, typ: &TcType) -> Result<Option<TcType>, Error> {
    let maybe_alias = match **typ {
        Type::Alias(ref alias) if alias.args.is_empty() => Some(alias),
        Type::App(ref alias, ref args) => {
            match **alias {
                Type::Alias(ref alias) if alias.args.len() == args.len() => Some(alias),
                _ => None,
            }
        }
        _ => None,
    };
    let (id, args) = match typ.as_alias() {
        Some(x) => x,
        None => return Ok(None),
    };
    let alias = match maybe_alias {
        Some(alias) => alias,
        None => {
            try!(env.find_type_info(&id)
                .map(|a| &**a)
                .ok_or_else(|| Error::UndefinedType(id.clone())))
        }
    };
    Ok(type_of_alias(alias, args))
}

/// Returns the type which is aliased by `alias` if it was possible to do a substitution on the
/// type arguments of `alias` using `arguments`
///
/// Example:
///     alias = Result e t (| Err e | Ok t)
///     arguments = [Error, Option a]
///     result = | Err Error | Ok (Option a)
pub fn type_of_alias(alias: &AliasData<Symbol, TcType>, arguments: &[TcType]) -> Option<TcType> {
    let args = &alias.args;
    let mut typ = match alias.typ {
        Some(ref typ) => typ.clone(),
        None => return None,
    };
    // It is ok to take the aliased type only if the alias is fully applied or if it
    // the missing argument only appear in order at the end of the alias
    // i.e
    // type Test a b c = Type (a Int) b c
    //
    // Test a b == Type (a Int) b
    // Test a == Type (a Int)
    // Test == ??? (Impossible to do a sane substitution)

    let ok_substitution = match *typ.clone() {
        Type::App(ref d, ref arg_types) => {
            let allowed_missing_args = arg_types.iter()
                .rev()
                .zip(args.iter().rev())
                .take_while(|&(l, r)| {
                    match **l {
                        Type::Generic(ref g) => g == r,
                        _ => false,
                    }
                })
                .count();
            if args.len() - arguments.len() <= allowed_missing_args {
                // Remove the args at the end of the aliased type
                let arg_types: Vec<_> = arg_types.iter()
                    .take(arg_types.len() - (args.len() - arguments.len()))
                    .cloned()
                    .collect();
                typ = Type::app(d.clone(), arg_types);
                true
            } else {
                false
            }
        }
        _ => arguments.len() == args.len(),
    };
    if !ok_substitution {
        return None;
    }
    let typ = instantiate(typ, |gen| {
        // Replace the generic variable with the type from the list
        // or if it is not found the make a fresh variable
        args.iter()
            .zip(arguments)
            .find(|&(arg, _)| arg.id == gen.id)
            .map(|(_, typ)| typ.clone())
    });
    Some(typ)
}

#[derive(Debug, Default)]
pub struct Instantiator {
    pub named_variables: FnvMap<Symbol, TcType>,
}

impl Instantiator {
    pub fn new() -> Instantiator {
        Instantiator { named_variables: FnvMap::default() }
    }

    fn variable_for(&mut self,
                    generic: &Generic<Symbol>,
                    on_unbound: &mut FnMut(&Symbol) -> TcType)
                    -> TcType {
        let var = match self.named_variables.entry(generic.id.clone()) {
            Entry::Vacant(entry) => {
                let t = on_unbound(&generic.id);
                entry.insert(t).clone()
            }
            Entry::Occupied(entry) => entry.get().clone(),
        };
        let mut var = (*var).clone();
        if let Type::Variable(ref mut var) = var {
            var.kind = generic.kind.clone();
        }
        TcType::from(var)
    }

    /// Instantiates a type, replacing all generic variables with fresh type variables
    pub fn instantiate<F>(&mut self, typ: &TcType, on_unbound: F) -> TcType
        where F: FnMut(&Symbol) -> TcType
    {
        self.named_variables.clear();
        self.instantiate_(typ, on_unbound)
    }

    pub fn instantiate_<F>(&mut self, typ: &TcType, mut on_unbound: F) -> TcType
        where F: FnMut(&Symbol) -> TcType
    {
        instantiate(typ.clone(),
                    |id| Some(self.variable_for(id, &mut on_unbound)))
    }
}

pub fn instantiate<F>(typ: TcType, mut f: F) -> TcType
    where F: FnMut(&Generic<Symbol>) -> Option<TcType>
{
    types::walk_move_type(typ,
                          &mut |typ| {
                              match *typ {
                                  Type::Generic(ref x) => f(x),
                                  _ => None,
                              }
                          })
}<commit_msg>style: Add missing newline<commit_after>use std::borrow::Cow;
use std::collections::hash_map::Entry;

use types;
use types::{AliasData, Type, Generic, TcType, TypeEnv};
use symbol::Symbol;
use fnv::FnvMap;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        SelfRecursive(id: Symbol) {
            description("self recursive")
            display("The use of self recursion in type `{}` could not be unified.", id)
        }
        UndefinedType(id: Symbol) {
            description("undefined type")
            display("Type `{}` does not exist.", id)
        }
    }
}

/// Removes type aliases from `typ` until it is an actual type
pub fn remove_aliases(env: &TypeEnv, mut typ: TcType) -> TcType {
    while let Ok(Some(new)) = maybe_remove_alias(env, &typ) {
        typ = new;
    }
    typ
}

pub fn remove_aliases_cow<'t>(env: &TypeEnv, typ: &'t TcType) -> Cow<'t, TcType> {
    let mut typ = match maybe_remove_alias(env, typ) {
        Ok(Some(new)) => new,
        _ => return Cow::Borrowed(typ),
    };
    while let Ok(Some(new)) = maybe_remove_alias(env, &typ) {
        typ = new;
    }
    Cow::Owned(typ)
}

/// Removes all possible aliases while checking that
pub fn remove_aliases_checked(reduced_aliases: &mut Vec<Symbol>,
                              env: &TypeEnv,
                              typ: &TcType)
                              -> Result<Option<TcType>, Error> {
    if let Some((alias_id, _)) = typ.as_alias() {
        if reduced_aliases.iter().any(|name| name == alias_id) {
            return Err(Error::SelfRecursive(alias_id.clone()));
        }
        reduced_aliases.push(alias_id.clone());
    }
    let mut typ = match try!(maybe_remove_alias(env, typ)) {
        Some(new) => new,
        None => return Ok(None),
    };
    loop {
        if let Some((alias_id, _)) = typ.as_alias() {
            if reduced_aliases.iter().any(|name| name == alias_id) {
                return Err(Error::SelfRecursive(alias_id.clone()));
            }
            reduced_aliases.push(alias_id.clone());
        }
        match try!(maybe_remove_alias(env, &typ)) {
            Some(new) => typ = new,
            None => break,
        }
    }
    Ok(Some(typ))
}

pub fn remove_alias(env: &TypeEnv, typ: TcType) -> TcType {
    maybe_remove_alias(env, &typ).unwrap_or(None).unwrap_or(typ)
}

/// Expand `typ` if it is an alias that can be expanded and return the expanded type.
/// Returns `None` if the type is not an alias or the alias could not be expanded.
pub fn maybe_remove_alias(env: &TypeEnv, typ: &TcType) -> Result<Option<TcType>, Error> {
    let maybe_alias = match **typ {
        Type::Alias(ref alias) if alias.args.is_empty() => Some(alias),
        Type::App(ref alias, ref args) => {
            match **alias {
                Type::Alias(ref alias) if alias.args.len() == args.len() => Some(alias),
                _ => None,
            }
        }
        _ => None,
    };
    let (id, args) = match typ.as_alias() {
        Some(x) => x,
        None => return Ok(None),
    };
    let alias = match maybe_alias {
        Some(alias) => alias,
        None => {
            try!(env.find_type_info(&id)
                .map(|a| &**a)
                .ok_or_else(|| Error::UndefinedType(id.clone())))
        }
    };
    Ok(type_of_alias(alias, args))
}

/// Returns the type which is aliased by `alias` if it was possible to do a substitution on the
/// type arguments of `alias` using `arguments`
///
/// Example:
///     alias = Result e t (| Err e | Ok t)
///     arguments = [Error, Option a]
///     result = | Err Error | Ok (Option a)
pub fn type_of_alias(alias: &AliasData<Symbol, TcType>, arguments: &[TcType]) -> Option<TcType> {
    let args = &alias.args;
    let mut typ = match alias.typ {
        Some(ref typ) => typ.clone(),
        None => return None,
    };
    // It is ok to take the aliased type only if the alias is fully applied or if it
    // the missing argument only appear in order at the end of the alias
    // i.e
    // type Test a b c = Type (a Int) b c
    //
    // Test a b == Type (a Int) b
    // Test a == Type (a Int)
    // Test == ??? (Impossible to do a sane substitution)

    let ok_substitution = match *typ.clone() {
        Type::App(ref d, ref arg_types) => {
            let allowed_missing_args = arg_types.iter()
                .rev()
                .zip(args.iter().rev())
                .take_while(|&(l, r)| {
                    match **l {
                        Type::Generic(ref g) => g == r,
                        _ => false,
                    }
                })
                .count();
            if args.len() - arguments.len() <= allowed_missing_args {
                // Remove the args at the end of the aliased type
                let arg_types: Vec<_> = arg_types.iter()
                    .take(arg_types.len() - (args.len() - arguments.len()))
                    .cloned()
                    .collect();
                typ = Type::app(d.clone(), arg_types);
                true
            } else {
                false
            }
        }
        _ => arguments.len() == args.len(),
    };
    if !ok_substitution {
        return None;
    }
    let typ = instantiate(typ, |gen| {
        // Replace the generic variable with the type from the list
        // or if it is not found the make a fresh variable
        args.iter()
            .zip(arguments)
            .find(|&(arg, _)| arg.id == gen.id)
            .map(|(_, typ)| typ.clone())
    });
    Some(typ)
}

#[derive(Debug, Default)]
pub struct Instantiator {
    pub named_variables: FnvMap<Symbol, TcType>,
}

impl Instantiator {
    pub fn new() -> Instantiator {
        Instantiator { named_variables: FnvMap::default() }
    }

    fn variable_for(&mut self,
                    generic: &Generic<Symbol>,
                    on_unbound: &mut FnMut(&Symbol) -> TcType)
                    -> TcType {
        let var = match self.named_variables.entry(generic.id.clone()) {
            Entry::Vacant(entry) => {
                let t = on_unbound(&generic.id);
                entry.insert(t).clone()
            }
            Entry::Occupied(entry) => entry.get().clone(),
        };
        let mut var = (*var).clone();
        if let Type::Variable(ref mut var) = var {
            var.kind = generic.kind.clone();
        }
        TcType::from(var)
    }

    /// Instantiates a type, replacing all generic variables with fresh type variables
    pub fn instantiate<F>(&mut self, typ: &TcType, on_unbound: F) -> TcType
        where F: FnMut(&Symbol) -> TcType
    {
        self.named_variables.clear();
        self.instantiate_(typ, on_unbound)
    }

    pub fn instantiate_<F>(&mut self, typ: &TcType, mut on_unbound: F) -> TcType
        where F: FnMut(&Symbol) -> TcType
    {
        instantiate(typ.clone(),
                    |id| Some(self.variable_for(id, &mut on_unbound)))
    }
}

pub fn instantiate<F>(typ: TcType, mut f: F) -> TcType
    where F: FnMut(&Generic<Symbol>) -> Option<TcType>
{
    types::walk_move_type(typ,
                          &mut |typ| {
                              match *typ {
                                  Type::Generic(ref x) => f(x),
                                  _ => None,
                              }
                          })
}
<|endoftext|>


<commit_before>#![feature(panic_implementation)] // required for defining the panic handler
#![feature(const_fn)]
#![no_std] // don't link the Rust standard library
#![cfg_attr(not(test), no_main)] // disable all Rust-level entry points

// add the library as dependency (same crate name as executable)
#[macro_use]
extern crate testing;

#[cfg(not(test))]
use core::panic::PanicInfo;
use testing::exit_qemu;

/// This function is the entry point, since the linker looks for a function
/// named `_start_` by default.
#[cfg(not(test))]
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start() -> ! {
    serial_println!("ok");

    unsafe {
        exit_qemu();
    }
    loop {}
}

/// This function is called on panic.
#[cfg(not(test))]
#[panic_implementation]
#[no_mangle]
pub fn panic(info: &PanicInfo) -> ! {
    serial_println!("failed");

    serial_println!("{}", info);

    unsafe {
        exit_qemu();
    }
    loop {}
}
<commit_msg>Switch to new panic_handler attribute (#41)<commit_after>#![feature(const_fn)]
#![no_std] // don't link the Rust standard library
#![cfg_attr(not(test), no_main)] // disable all Rust-level entry points

// add the library as dependency (same crate name as executable)
#[macro_use]
extern crate testing;

#[cfg(not(test))]
use core::panic::PanicInfo;
use testing::exit_qemu;

/// This function is the entry point, since the linker looks for a function
/// named `_start_` by default.
#[cfg(not(test))]
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn _start() -> ! {
    serial_println!("ok");

    unsafe {
        exit_qemu();
    }
    loop {}
}

/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    serial_println!("failed");

    serial_println!("{}", info);

    unsafe {
        exit_qemu();
    }
    loop {}
}
<|endoftext|>


<commit_before>#![allow(arithmetic_overflow)]

pub fn main() {
    // This tests that do (not) do sign extension properly when loading integers
    assert_eq!(u32::MAX as i64, 4294967295);
    assert_eq!(i32::MIN as i64, -2147483648);

    assert_eq!(i8::MAX, 127);
    assert_eq!(i8::MIN, -128);

    assert_eq!(i32::from_str_radix("A", 16), Ok(10));

    let n = -0b1000_0000i8;
    assert_eq!(n.count_ones(), 1);

    let n = -0b1000_0000i8;
    assert_eq!(n.count_zeros(), 7);

    let n = -1i16;
    assert_eq!(n.leading_zeros(), 0);

    let n = -4i8;
    assert_eq!(n.trailing_zeros(), 2);

    let n = 0x0123456789ABCDEFi64;
    let m = -0x76543210FEDCBA99i64;
    assert_eq!(n.rotate_left(32), m);

    let n = 0x0123456789ABCDEFi64;
    let m = -0xFEDCBA987654322i64;
    assert_eq!(n.rotate_right(4), m);

    let n =  0x0123456789ABCDEFi64;
    let m = -0x1032547698BADCFFi64;
    assert_eq!(n.swap_bytes(), m);

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "big") {
        assert_eq!(i64::from_be(n), n)
    } else {
        assert_eq!(i64::from_be(n), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "little") {
        assert_eq!(i64::from_le(n), n)
    } else {
        assert_eq!(i64::from_le(n), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "big") {
        assert_eq!(n.to_be(), n)
    } else {
        assert_eq!(n.to_be(), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "little") {
        assert_eq!(n.to_le(), n)
    } else {
        assert_eq!(n.to_le(), n.swap_bytes())
    }

    assert_eq!(7i16.checked_add(32760), Some(32767));
    assert_eq!(8i16.checked_add(32760), None);

    assert_eq!((-127i8).checked_sub(1), Some(-128));
    assert_eq!((-128i8).checked_sub(1), None);

    assert_eq!(6i8.checked_mul(21), Some(126));
    assert_eq!(6i8.checked_mul(22), None);

    assert_eq!((-127i8).checked_div(-1), Some(127));
    assert_eq!((-128i8).checked_div(-1), None);
    assert_eq!((1i8).checked_div(0), None);

    assert_eq!(5i32.checked_rem(2), Some(1));
    assert_eq!(5i32.checked_rem(0), None);
    assert_eq!(i32::MIN.checked_rem(-1), None);

    assert_eq!(5i32.checked_neg(), Some(-5));
    assert_eq!(i32::MIN.checked_neg(), None);

    assert_eq!(0x10i32.checked_shl(4), Some(0x100));
    assert_eq!(0x10i32.checked_shl(33), None);

    assert_eq!(0x10i32.checked_shr(4), Some(0x1));
    assert_eq!(0x10i32.checked_shr(33), None);

    assert_eq!((-5i32).checked_abs(), Some(5));
    assert_eq!(i32::MIN.checked_abs(), None);

    assert_eq!(100i8.saturating_add(1), 101);
    assert_eq!(100i8.saturating_add(127), 127);

    assert_eq!(100i8.saturating_sub(127), -27);
    assert_eq!((-100i8).saturating_sub(127), -128);

    assert_eq!(100i32.saturating_mul(127), 12700);
    assert_eq!((1i32 << 23).saturating_mul(1 << 23), i32::MAX);
    assert_eq!((-1i32 << 23).saturating_mul(1 << 23), i32::MIN);

    assert_eq!(100i8.wrapping_add(27), 127);
    assert_eq!(100i8.wrapping_add(127), -29);

    assert_eq!(0i8.wrapping_sub(127), -127);
    assert_eq!((-2i8).wrapping_sub(127), 127);

    assert_eq!(10i8.wrapping_mul(12), 120);
    assert_eq!(11i8.wrapping_mul(12), -124);

    assert_eq!(100u8.wrapping_div(10), 10);
    assert_eq!((-128i8).wrapping_div(-1), -128);

    assert_eq!(100i8.wrapping_rem(10), 0);
    assert_eq!((-128i8).wrapping_rem(-1), 0);

    assert_eq!(i32::MIN.wrapping_div(-1), i32::MIN);
    assert_eq!(i32::MIN.wrapping_rem(-1), 0);

    assert_eq!(100i8.wrapping_neg(), -100);
    assert_eq!((-128i8).wrapping_neg(), -128);

    assert_eq!((-1i8).wrapping_shl(7), -128);
    assert_eq!((-1i8).wrapping_shl(8), -1);

    assert_eq!((-128i8).wrapping_shr(7), -1);
    assert_eq!((-128i8).wrapping_shr(8), -128);

    assert_eq!(100i8.wrapping_abs(), 100);
    assert_eq!((-100i8).wrapping_abs(), 100);
    assert_eq!((-128i8).wrapping_abs(), -128);
    assert_eq!((-128i8).wrapping_abs() as u8, 128);

    assert_eq!(5i32.overflowing_add(2), (7, false));
    assert_eq!(i32::MAX.overflowing_add(1), (i32::MIN, true));

    assert_eq!(5i32.overflowing_sub(2), (3, false));
    assert_eq!(i32::MIN.overflowing_sub(1), (i32::MAX, true));

    assert_eq!(5i32.overflowing_mul(2), (10, false));
    assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));

    assert_eq!(5i32.overflowing_div(2), (2, false));
    assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));

    assert_eq!(5i32.overflowing_rem(2), (1, false));
    assert_eq!(i32::MIN.overflowing_rem(-1), (0, true));

    assert_eq!(2i32.overflowing_neg(), (-2, false));
    assert_eq!(i32::MIN.overflowing_neg(), (i32::MIN, true));

    assert_eq!(0x10i32.overflowing_shl(4), (0x100, false));
    assert_eq!(0x10i32.overflowing_shl(36), (0x100, true));

    assert_eq!(0x10i32.overflowing_shr(4), (0x1, false));
    assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));

    assert_eq!(10i8.overflowing_abs(), (10,false));
    assert_eq!((-10i8).overflowing_abs(), (10,false));
    assert_eq!((-128i8).overflowing_abs(), (-128,true));
}
<commit_msg>add extra tests for shifts with negative offsets<commit_after>// compile-flags: -Coverflow-checks=off
#![allow(arithmetic_overflow)]

pub fn main() {
    // This tests that we do (not) do sign extension properly when loading integers
    assert_eq!(u32::MAX as i64, 4294967295);
    assert_eq!(i32::MIN as i64, -2147483648);

    assert_eq!(i8::MAX, 127);
    assert_eq!(i8::MIN, -128);

    // Shifts with negative offsets are subtle.
    assert_eq!(13 << -2i8, 13 << 254);
    assert_eq!(13 << i8::MIN, 13);
    assert_eq!(13 << -1i16, 13 << u16::MAX);
    assert_eq!(13 << i16::MIN, 13);
    assert_eq!(13i128 << -2i8, 13i128 << 254);
    assert_eq!(13i128 << i8::MIN, 13);
    assert_eq!(13i128 << -1i16, 13i128 << u16::MAX);
    assert_eq!(13i128 << i16::MIN, 13);

    assert_eq!(i32::from_str_radix("A", 16), Ok(10));

    let n = -0b1000_0000i8;
    assert_eq!(n.count_ones(), 1);

    let n = -0b1000_0000i8;
    assert_eq!(n.count_zeros(), 7);

    let n = -1i16;
    assert_eq!(n.leading_zeros(), 0);

    let n = -4i8;
    assert_eq!(n.trailing_zeros(), 2);

    let n = 0x0123456789ABCDEFi64;
    let m = -0x76543210FEDCBA99i64;
    assert_eq!(n.rotate_left(32), m);

    let n = 0x0123456789ABCDEFi64;
    let m = -0xFEDCBA987654322i64;
    assert_eq!(n.rotate_right(4), m);

    let n =  0x0123456789ABCDEFi64;
    let m = -0x1032547698BADCFFi64;
    assert_eq!(n.swap_bytes(), m);

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "big") {
        assert_eq!(i64::from_be(n), n)
    } else {
        assert_eq!(i64::from_be(n), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "little") {
        assert_eq!(i64::from_le(n), n)
    } else {
        assert_eq!(i64::from_le(n), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "big") {
        assert_eq!(n.to_be(), n)
    } else {
        assert_eq!(n.to_be(), n.swap_bytes())
    }

    let n = 0x0123456789ABCDEFi64;
    if cfg!(target_endian = "little") {
        assert_eq!(n.to_le(), n)
    } else {
        assert_eq!(n.to_le(), n.swap_bytes())
    }

    assert_eq!(7i16.checked_add(32760), Some(32767));
    assert_eq!(8i16.checked_add(32760), None);

    assert_eq!((-127i8).checked_sub(1), Some(-128));
    assert_eq!((-128i8).checked_sub(1), None);

    assert_eq!(6i8.checked_mul(21), Some(126));
    assert_eq!(6i8.checked_mul(22), None);

    assert_eq!((-127i8).checked_div(-1), Some(127));
    assert_eq!((-128i8).checked_div(-1), None);
    assert_eq!((1i8).checked_div(0), None);

    assert_eq!(5i32.checked_rem(2), Some(1));
    assert_eq!(5i32.checked_rem(0), None);
    assert_eq!(i32::MIN.checked_rem(-1), None);

    assert_eq!(5i32.checked_neg(), Some(-5));
    assert_eq!(i32::MIN.checked_neg(), None);

    assert_eq!(0x10i32.checked_shl(4), Some(0x100));
    assert_eq!(0x10i32.checked_shl(33), None);

    assert_eq!(0x10i32.checked_shr(4), Some(0x1));
    assert_eq!(0x10i32.checked_shr(33), None);

    assert_eq!((-5i32).checked_abs(), Some(5));
    assert_eq!(i32::MIN.checked_abs(), None);

    assert_eq!(100i8.saturating_add(1), 101);
    assert_eq!(100i8.saturating_add(127), 127);

    assert_eq!(100i8.saturating_sub(127), -27);
    assert_eq!((-100i8).saturating_sub(127), -128);

    assert_eq!(100i32.saturating_mul(127), 12700);
    assert_eq!((1i32 << 23).saturating_mul(1 << 23), i32::MAX);
    assert_eq!((-1i32 << 23).saturating_mul(1 << 23), i32::MIN);

    assert_eq!(100i8.wrapping_add(27), 127);
    assert_eq!(100i8.wrapping_add(127), -29);

    assert_eq!(0i8.wrapping_sub(127), -127);
    assert_eq!((-2i8).wrapping_sub(127), 127);

    assert_eq!(10i8.wrapping_mul(12), 120);
    assert_eq!(11i8.wrapping_mul(12), -124);

    assert_eq!(100u8.wrapping_div(10), 10);
    assert_eq!((-128i8).wrapping_div(-1), -128);

    assert_eq!(100i8.wrapping_rem(10), 0);
    assert_eq!((-128i8).wrapping_rem(-1), 0);

    assert_eq!(i32::MIN.wrapping_div(-1), i32::MIN);
    assert_eq!(i32::MIN.wrapping_rem(-1), 0);

    assert_eq!(100i8.wrapping_neg(), -100);
    assert_eq!((-128i8).wrapping_neg(), -128);

    assert_eq!((-1i8).wrapping_shl(7), -128);
    assert_eq!((-1i8).wrapping_shl(8), -1);

    assert_eq!((-128i8).wrapping_shr(7), -1);
    assert_eq!((-128i8).wrapping_shr(8), -128);

    assert_eq!(100i8.wrapping_abs(), 100);
    assert_eq!((-100i8).wrapping_abs(), 100);
    assert_eq!((-128i8).wrapping_abs(), -128);
    assert_eq!((-128i8).wrapping_abs() as u8, 128);

    assert_eq!(5i32.overflowing_add(2), (7, false));
    assert_eq!(i32::MAX.overflowing_add(1), (i32::MIN, true));

    assert_eq!(5i32.overflowing_sub(2), (3, false));
    assert_eq!(i32::MIN.overflowing_sub(1), (i32::MAX, true));

    assert_eq!(5i32.overflowing_mul(2), (10, false));
    assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));

    assert_eq!(5i32.overflowing_div(2), (2, false));
    assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));

    assert_eq!(5i32.overflowing_rem(2), (1, false));
    assert_eq!(i32::MIN.overflowing_rem(-1), (0, true));

    assert_eq!(2i32.overflowing_neg(), (-2, false));
    assert_eq!(i32::MIN.overflowing_neg(), (i32::MIN, true));

    assert_eq!(0x10i32.overflowing_shl(4), (0x100, false));
    assert_eq!(0x10i32.overflowing_shl(36), (0x100, true));

    assert_eq!(0x10i32.overflowing_shr(4), (0x1, false));
    assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));

    assert_eq!(10i8.overflowing_abs(), (10,false));
    assert_eq!((-10i8).overflowing_abs(), (10,false));
    assert_eq!((-128i8).overflowing_abs(), (-128,true));
}
<|endoftext|>