<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 rustc_serialize;

use std::env;
use std::io::{BufReader, BufRead};
use std::fs::File;
use std::path::PathBuf;
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 rustc_serialize::json;

use lithos::utils::read_yaml_dir;
use lithos::master_config::MasterConfig;
use lithos::tree_config::TreeConfig;
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.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.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.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_images = match find_used_images(&master, tm, ver_min, ver_max) {
        Ok(images) => images,
        Err(e) => {
            error!("Error finding out used images: {}", e);
            exit(1);
        }
    };
    println!("Used images: {:?}", used_images);
}

fn find_used_images(master: &MasterConfig, min_time: Option<Tm>,
    ver_min: u32, ver_max: u32) -> Result<HashSet<PathBuf>, String>
{
    let mut images = HashSet::new();
    let childval = &*ChildConfig::mapping_validator();
    for (tree_name, tree_fn) in try!(read_yaml_dir(&master.config_dir)
                            .map_err(|e| format!("Read dir error: {}", e)))
    {
        let tree_config: TreeConfig = try!(parse_config(&tree_fn,
            &*TreeConfig::validator(), Default::default()));
        let all_children: BTreeMap<String, ChildConfig>;
        all_children = try!(parse_config(&tree_config.config_file,
            childval, Default::default())
            .map_err(|e| format!("Can't read child config {:?}: {}",
                                 tree_config.config_file, e)));
        for child in all_children.values() {
            // Current are always added
            images.insert(tree_config.image_dir.join(&child.image));
        }
        let logname = master.config_log_dir.join(format!("{}.log", tree_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 tree 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(tree_config.image_dir.join(&child.image));
            }
        }
    }
    Ok(images)
}
<commit_msg>First working version of lithos<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 rustc_serialize;

use std::env;
use std::io::{BufReader, BufRead};
use std::io::Error as IoError;
use std::fs::{File, read_dir, remove_dir_all};
use std::path::PathBuf;
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 rustc_serialize::json;

use lithos::utils::read_yaml_dir;
use lithos::master_config::MasterConfig;
use lithos::tree_config::TreeConfig;
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.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.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.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, 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>, IoError> {
    let mut unused = Vec::new();
    for dir in dirs.iter() {
        for entry in try!(read_dir(dir)) {
            let entry = try!(entry);
            if !try!(entry.file_type()).is_dir() {
                continue;
            }
            if let Some(name) = entry.file_name().as_os_str().to_str() {
                if name.starts_with(".") {
                    continue;
                }
            }
            let path = entry.path().to_path_buf();
            if !used.contains(&path) {
                unused.push(path);
            }
        }
    }
    Ok(unused)
}

fn find_used_images(master: &MasterConfig, min_time: Option<Tm>,
    ver_min: u32, ver_max: u32)
    -> Result<(HashSet<PathBuf>, HashSet<PathBuf>), String>
{
    let mut images = HashSet::new();
    let mut image_dirs = HashSet::new();
    let childval = &*ChildConfig::mapping_validator();
    for (tree_name, tree_fn) in try!(read_yaml_dir(&master.config_dir)
                            .map_err(|e| format!("Read dir error: {}", e)))
    {
        let tree_config: TreeConfig = try!(parse_config(&tree_fn,
            &*TreeConfig::validator(), Default::default()));
        image_dirs.insert(tree_config.image_dir.clone());

        let all_children: BTreeMap<String, ChildConfig>;
        all_children = try!(parse_config(&tree_config.config_file,
            childval, Default::default())
            .map_err(|e| format!("Can't read child config {:?}: {}",
                                 tree_config.config_file, e)));
        for child in all_children.values() {
            // Current are always added
            images.insert(tree_config.image_dir.join(&child.image));
        }

        let logname = master.config_log_dir.join(format!("{}.log", tree_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 tree 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(tree_config.image_dir.join(&child.image));
            }
        }
    }
    Ok((images, image_dirs))
}
<|endoftext|>


<commit_before>use runtime::Runtime;
use std::error::Error;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::fmt::Display;
use std::path::Path;
use std::result::Result;

use module::todo::TodoModule;

mod todo;

#[derive(Debug)]
pub struct ModuleError {
    desc: String,
}

impl ModuleError {
    fn mk(desc: &'static str) -> ModuleError {
        ModuleError {
            desc: desc.to_owned().to_string(),
        }
    }
}

impl Error for ModuleError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for ModuleError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "ModuleError: {}", self.description())
    }
}

pub type ModuleResult = Result<(), ModuleError>;

pub trait Module {

    fn new(rt : &Runtime) -> Self;
    fn callnames() -> &'static [&'static str];
    fn name(&self) -> &'static str;

    fn execute(&self, rt : &Runtime) -> ModuleResult;
    fn shutdown(&self, rt : &Runtime) -> ModuleResult;

}

pub mod file {

    use regex::Regex;

    pub struct ParserError {
        summary: String,
        parsertext: String,
        index: i32,
        explanation: Option<String>,
    }

    impl ParserError {
        fn new(sum: &'static str, text: String, idx: i32, expl: &'static str) -> ParserError {
            ParserError {
                summary: String::from(sum),
                parsertext: text,
                index: idx,
                explanation: Some(String::from(expl)),
            }
        }

        fn short(sum: &'static str, text: String, idx: i32) -> ParserError {
            ParserError {
                summary: String::from(sum),
                parsertext: text,
                index: idx,
                explanation: None
            }
        }
    }

    pub enum FileHeaderSpec {
        Null,
        Bool,
        Integer,
        UInteger,
        Float,
        Text,
        Key { name: String, value_type: Box<FileHeaderSpec> },
        Array { allowed_types: Box<Vec<FileHeaderSpec>> },
    }

    pub enum FileHeaderData {
        Null,
        Bool(bool),
        Integer(i64),
        UInteger(u64),
        Float(f64),
        Text(String),
        Key { name: String, value: Box<FileHeaderData> },
        Array { values: Box<Vec<FileHeaderData>> },
    }

    pub trait FileHeaderParser : Sized {
        fn new(spec: &FileHeaderSpec) -> Self;
        fn read(&self, string: Option<String>) -> Result<FileHeaderData, ParserError>;
        fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
    }

    pub trait FileData : Sized {
        fn get_fulltext(&self) -> String;
        fn get_abbrev(&self) -> String;
    }

    pub trait FileDataParser<FD: FileData + Sized> : Sized {
        fn new() -> Self;
        fn read(&self, string: Option<String>) -> Result<FD, ParserError>;
        fn write(&self, data: &FD) -> Result<String, ParserError>;
    }

    type TextTpl = (Option<String>, Option<String>);

    pub struct Parser<HP, DP>
    {
        headerp : HP,
        datap : DP,
    }

    impl<HP, DP> Parser<HP, DP> where
        HP: FileHeaderParser,
    {

        fn new(headerp: HP, datap: DP) -> Parser<HP, DP> {
            Parser {
                headerp: headerp,
                datap: datap,
            }
        }

        fn read<FD>(&self, s: String) -> Result<(FileHeaderData, FD), ParserError>
            where FD: FileData + Sized,
                  DP: FileDataParser<FD>
        {
            let divided = divide_text(&s);

            if divided.is_err() {
                return Err(divided.err().unwrap());
            }

            let (header, data) = divided.ok().unwrap();

            let h_parseres = self.headerp.read(header);
            let d_parseres = self.datap.read(data);

            if h_parseres.is_err() {
                return Err(h_parseres.err().unwrap());
            }

            if d_parseres.is_err() {
                return Err(d_parseres.err().unwrap());
            }

            Ok((h_parseres.ok().unwrap(), d_parseres.ok().unwrap()))
        }
    }

    fn divide_text(text: &String) -> Result<TextTpl, ParserError> {
        let re = Regex::new(r"(?m)^\-\-\-$\n(.*)^\-\-\-$\n(.*)").unwrap();

        let captures = re.captures(&text[..]).unwrap_or(
            return Err(ParserError::new("Cannot run regex on text",
                                        text.clone(), 0,
                                        "Cannot run regex on text to divide it into header and content."))
        );

        if captures.len() != 2 {
            return Err(ParserError::new("Unexpected Regex output",
                                        text.clone(), 0,
                                        "The regex to divide text into header and content had an unexpected output."))
        }

        let header  = captures.at(0).map(|s| String::from(s));
        let content = captures.at(1).map(|s| String::from(s));
        Ok((header, content))
    }

}
<commit_msg>Implement Parser.write()<commit_after>use runtime::Runtime;
use std::error::Error;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::fmt::Display;
use std::path::Path;
use std::result::Result;

use module::todo::TodoModule;

mod todo;

#[derive(Debug)]
pub struct ModuleError {
    desc: String,
}

impl ModuleError {
    fn mk(desc: &'static str) -> ModuleError {
        ModuleError {
            desc: desc.to_owned().to_string(),
        }
    }
}

impl Error for ModuleError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for ModuleError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "ModuleError: {}", self.description())
    }
}

pub type ModuleResult = Result<(), ModuleError>;

pub trait Module {

    fn new(rt : &Runtime) -> Self;
    fn callnames() -> &'static [&'static str];
    fn name(&self) -> &'static str;

    fn execute(&self, rt : &Runtime) -> ModuleResult;
    fn shutdown(&self, rt : &Runtime) -> ModuleResult;

}

pub mod file {

    use regex::Regex;

    pub struct ParserError {
        summary: String,
        parsertext: String,
        index: i32,
        explanation: Option<String>,
    }

    impl ParserError {
        fn new(sum: &'static str, text: String, idx: i32, expl: &'static str) -> ParserError {
            ParserError {
                summary: String::from(sum),
                parsertext: text,
                index: idx,
                explanation: Some(String::from(expl)),
            }
        }

        fn short(sum: &'static str, text: String, idx: i32) -> ParserError {
            ParserError {
                summary: String::from(sum),
                parsertext: text,
                index: idx,
                explanation: None
            }
        }
    }

    pub enum FileHeaderSpec {
        Null,
        Bool,
        Integer,
        UInteger,
        Float,
        Text,
        Key { name: String, value_type: Box<FileHeaderSpec> },
        Array { allowed_types: Box<Vec<FileHeaderSpec>> },
    }

    pub enum FileHeaderData {
        Null,
        Bool(bool),
        Integer(i64),
        UInteger(u64),
        Float(f64),
        Text(String),
        Key { name: String, value: Box<FileHeaderData> },
        Array { values: Box<Vec<FileHeaderData>> },
    }

    pub trait FileHeaderParser : Sized {
        fn new(spec: &FileHeaderSpec) -> Self;
        fn read(&self, string: Option<String>) -> Result<FileHeaderData, ParserError>;
        fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
    }

    pub trait FileData : Sized {
        fn get_fulltext(&self) -> String;
        fn get_abbrev(&self) -> String;
    }

    pub trait FileDataParser<FD: FileData + Sized> : Sized {
        fn new() -> Self;
        fn read(&self, string: Option<String>) -> Result<FD, ParserError>;
        fn write(&self, data: &FD) -> Result<String, ParserError>;
    }

    type TextTpl = (Option<String>, Option<String>);

    pub struct Parser<HP, DP>
    {
        headerp : HP,
        datap : DP,
    }

    impl<HP, DP> Parser<HP, DP> where
        HP: FileHeaderParser,
    {

        fn new(headerp: HP, datap: DP) -> Parser<HP, DP> {
            Parser {
                headerp: headerp,
                datap: datap,
            }
        }

        fn read<FD>(&self, s: String) -> Result<(FileHeaderData, FD), ParserError>
            where FD: FileData + Sized,
                  DP: FileDataParser<FD>
        {
            let divided = divide_text(&s);

            if divided.is_err() {
                return Err(divided.err().unwrap());
            }

            let (header, data) = divided.ok().unwrap();

            let h_parseres = self.headerp.read(header);
            let d_parseres = self.datap.read(data);

            if h_parseres.is_err() {
                return Err(h_parseres.err().unwrap());
            }

            if d_parseres.is_err() {
                return Err(d_parseres.err().unwrap());
            }

            Ok((h_parseres.ok().unwrap(), d_parseres.ok().unwrap()))
        }

        fn write<FD>(&self, tpl : (FileHeaderData, FD)) -> Result<String, ParserError>
            where FD: FileData + Sized,
                  DP: FileDataParser<FD>
        {
            let (header, data) = tpl;
            let h_text = self.headerp.write(&header);
            let d_text = self.datap.write(&data);

            if h_text.is_err() {
                return Err(h_text.err().unwrap());
            }

            if d_text.is_err() {
                return Err(d_text.err().unwrap());
            }

            Ok(h_text.ok().unwrap() + &d_text.ok().unwrap()[..])
        }
    }

    fn divide_text(text: &String) -> Result<TextTpl, ParserError> {
        let re = Regex::new(r"(?m)^\-\-\-$\n(.*)^\-\-\-$\n(.*)").unwrap();

        let captures = re.captures(&text[..]).unwrap_or(
            return Err(ParserError::new("Cannot run regex on text",
                                        text.clone(), 0,
                                        "Cannot run regex on text to divide it into header and content."))
        );

        if captures.len() != 2 {
            return Err(ParserError::new("Unexpected Regex output",
                                        text.clone(), 0,
                                        "The regex to divide text into header and content had an unexpected output."))
        }

        let header  = captures.at(0).map(|s| String::from(s));
        let content = captures.at(1).map(|s| String::from(s));
        Ok((header, content))
    }

}
<|endoftext|>


<commit_before>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;
    use libc;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use std::ffi::OsString;
    use std::io;
    use std::mem;
    use std::os::windows::prelude::*;
    use std::ptr;

    use self::winapi::shared::basetsd::*;
    use self::winapi::shared::minwindef::*;
    use self::winapi::shared::minwindef::{FALSE, TRUE};
    use self::winapi::um::handleapi::*;
    use self::winapi::um::jobapi2::*;
    use self::winapi::um::jobapi::*;
    use self::winapi::um::processthreadsapi::*;
    use self::winapi::um::psapi::*;
    use self::winapi::um::synchapi::*;
    use self::winapi::um::winbase::*;
    use self::winapi::um::winnt::*;
    use self::winapi::um::winnt::HANDLE;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // This is a litte subtle. By default if we are terminated then all
            // processes in our job object are terminated as well, but we
            // intentionally want to whitelist some processes to outlive our job
            // object (see below).
            //
            // To allow for this, we manually kill processes instead of letting
            // the job object kill them for us. We do this in a loop to handle
            // processes spawning other processes.
            //
            // Finally once this is all done we know that the only remaining
            // ones are ourselves and the whitelisted processes. The destructor
            // here then configures our job object to *not* kill everything on
            // close, then closes the job object.
            unsafe {
                while self.kill_remaining() {
                    info!("killed some, going for more");
                }

                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Setup {
        unsafe fn kill_remaining(&mut self) -> bool {
            #[repr(C)]
            struct Jobs {
                header: JOBOBJECT_BASIC_PROCESS_ID_LIST,
                list: [ULONG_PTR; 1024],
            }

            let mut jobs: Jobs = mem::zeroed();
            let r = QueryInformationJobObject(
                self.job.inner,
                JobObjectBasicProcessIdList,
                &mut jobs as *mut _ as LPVOID,
                mem::size_of_val(&jobs) as DWORD,
                ptr::null_mut(),
            );
            if r == 0 {
                info!("failed to query job object: {}", last_err());
                return false;
            }

            let mut killed = false;
            let list = &jobs.list[..jobs.header.NumberOfProcessIdsInList as usize];
            assert!(!list.is_empty());
            info!("found {} remaining processes", list.len() - 1);

            let list = list.iter()
                .filter(|&&id| {
                    // let's not kill ourselves
                    id as DWORD != GetCurrentProcessId()
                })
                .filter_map(|&id| {
                    // Open the process with the necessary rights, and if this
                    // fails then we probably raced with the process exiting so we
                    // ignore the problem.
                    let flags = PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE;
                    let p = OpenProcess(flags, FALSE, id as DWORD);
                    if p.is_null() {
                        None
                    } else {
                        Some(Handle { inner: p })
                    }
                })
                .filter(|p| {
                    // Test if this process was actually in the job object or not.
                    // If it's not then we likely raced with something else
                    // recycling this PID, so we just skip this step.
                    let mut res = 0;
                    let r = IsProcessInJob(p.inner, self.job.inner, &mut res);
                    if r == 0 {
                        info!("failed to test is process in job: {}", last_err());
                        return false;
                    }
                    res == TRUE
                });

            for p in list {
                // Load the file which this process was spawned from. We then
                // later use this for identification purposes.
                let mut buf = [0; 1024];
                let r = GetProcessImageFileNameW(p.inner, buf.as_mut_ptr(), buf.len() as DWORD);
                if r == 0 {
                    info!("failed to get image name: {}", last_err());
                    continue;
                }
                let s = OsString::from_wide(&buf[..r as usize]);
                info!("found remaining: {:?}", s);

                // And here's where we find the whole purpose for this
                // function!  Currently, our only whitelisted process is
                // `mspdbsrv.exe`, and more details about that can be found
                // here:
                //
                //      https://github.com/rust-lang/rust/issues/33145
                //
                // The gist of it is that all builds on one machine use the
                // same `mspdbsrv.exe` instance. If we were to kill this
                // instance then we could erroneously cause other builds to
                // fail.
                if let Some(s) = s.to_str() {
                    if s.contains("mspdbsrv") {
                        info!("\toops, this is mspdbsrv");
                        continue;
                    }
                }

                // Ok, this isn't mspdbsrv, let's kill the process. After we
                // kill it we wait on it to ensure that the next time around in
                // this function we're not going to see it again.
                let r = TerminateProcess(p.inner, 1);
                if r == 0 {
                    info!("\tfailed to kill subprocess: {}", last_err());
                    info!("\tassuming subprocess is dead...");
                } else {
                    info!("\tterminated subprocess");
                }
                let r = WaitForSingleObject(p.inner, INFINITE);
                if r != 0 {
                    info!("failed to wait for process to die: {}", last_err());
                    return false;
                }
                killed = true;
            }

            killed
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<commit_msg>Auto merge of #5887 - Seeker14491:job-fix, r=alexcrichton<commit_after>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;
    use libc;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use std::io;
    use std::mem;
    use std::ptr;

    use self::winapi::shared::minwindef::*;
    use self::winapi::um::handleapi::*;
    use self::winapi::um::jobapi2::*;
    use self::winapi::um::processthreadsapi::*;
    use self::winapi::um::winnt::*;
    use self::winapi::um::winnt::HANDLE;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // On normal exits (not ctrl-c), we don't want to kill any child
            // processes. The destructor here configures our job object to
            // *not* kill everything on close, then closes the job object.
            unsafe {
                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::{child_properties, function, general, properties, signal, trait_impls};
use crate::{
    analysis::special_functions::Type,
    analysis::{self, rust_type::RustType},
    case::CaseExt,
    codegen::general::{version_condition_no_doc, version_condition_string},
    env::Env,
    library, nameutil,
    traits::IntoString,
};
use std::io::{Result, Write};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    general::start_comments(w, &env.config)?;
    general::uses(w, env, &analysis.imports, analysis.version)?;

    general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        analysis.c_class_type.as_deref(),
        &analysis.get_type,
        analysis.is_interface,
        &analysis.supertypes,
    )?;

    if need_generate_inherent(analysis) {
        writeln!(w)?;
        write!(w, "impl {} {{", analysis.name)?;
        for func_analysis in &analysis.constructors() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                function::generate(
                    w,
                    env,
                    func_analysis,
                    Some(&analysis.specials),
                    analysis.version,
                    false,
                    false,
                    1,
                )?;
            }

            for property in &analysis.properties {
                properties::generate(w, env, property, false, false, 1)?;
            }

            for child_property in &analysis.child_properties {
                child_properties::generate(w, env, child_property, false, false, 1)?;
            }
        }

        for func_analysis in &analysis.functions() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                signal::generate(w, env, signal_analysis, false, false, 1)?;
            }
        }

        writeln!(w, "}}")?;

        general::declare_default_from_new(w, env, &analysis.name, &analysis.functions)?;
    }

    trait_impls::generate(
        w,
        env,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
        analysis.version,
    )?;

    if !analysis.builder_properties.is_empty() {
        writeln!(w)?;
        generate_builder(w, env, analysis)?;
    }

    if analysis.concurrency != library::Concurrency::None {
        writeln!(w)?;
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            writeln!(w, "unsafe impl Send for {} {{}}", analysis.name)?;
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name)?;
            } else {
                writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name)?;
            }

            writeln!(w, "    fn is_unique(&self) -> bool {{")?;
            writeln!(w, "        self.ref_count() == 1")?;
            writeln!(w, "    }}")?;

            writeln!(w, "}}")?;
        }
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name)?;
    }

    if !analysis.final_type {
        writeln!(w)?;
        writeln!(
            w,
            "pub const NONE_{}: Option<&{}> = None;",
            analysis.name.to_snake().to_uppercase(),
            analysis.name
        )?;
    }

    if need_generate_trait(analysis) {
        writeln!(w)?;
        generate_trait(w, env, analysis)?;
    }

    if generate_display_trait && !analysis.specials.has_trait(Type::Display) {
        writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?;
        // Generate Display trait implementation.
        writeln!(
            w,
            "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\tf.write_str(\"{}\")\n\
             \t}}\n\
             }}",
            analysis.name
        )?;
    }

    Ok(())
}

// TODO: instead create a Vec<> inside the Builder instead of Options.
fn generate_builder(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    let mut methods = vec![];
    let mut properties = vec![];
    writeln!(w, "#[derive(Clone, Default)]")?;
    writeln!(w, "pub struct {}Builder {{", analysis.name)?;
    for property in &analysis.builder_properties {
        match RustType::try_new(env, property.typ) {
            Ok(type_string) => {
                let type_string = match type_string.as_str() {
                    s if nameutil::is_gstring(s) => "String",
                    "Vec<GString>" | "Vec<glib::GString>" | "Vec<crate::GString>" => "Vec<String>",
                    typ => typ,
                };
                let direction = if property.is_get {
                    library::ParameterDirection::In
                } else {
                    library::ParameterDirection::Out
                };
                let mut param_type = RustType::builder(env, property.typ)
                    .direction(direction)
                    .ref_mode(property.set_in_ref_mode)
                    .try_build()
                    .into_string();
                let (param_type_override, bounds, conversion) = match &param_type[..] {
                    "&str" => (None, String::new(), ".to_string()"),
                    "&[&str]" => (Some("Vec<String>".to_string()), String::new(), ""),
                    _ if !property.bounds.is_empty() => {
                        let (bounds, _) = function::bounds(&property.bounds, &[], false, false);
                        let alias = property
                            .bounds
                            .get_parameter_alias_info(&property.name)
                            .map(|(alias, _)| format!("&{}", alias));
                        (alias, bounds, ".clone().upcast()")
                    }
                    typ if typ.starts_with('&') => (None, String::new(), ".clone()"),
                    _ => (None, String::new(), ""),
                };
                if let Some(param_type_override) = param_type_override {
                    param_type = param_type_override.to_string();
                }
                let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
                let version_condition_string =
                    version_condition_string(env, property.version, false, 1);
                if let Some(ref version_condition_string) = version_condition_string {
                    writeln!(w, "{}", version_condition_string)?;
                }
                writeln!(w, "    {}: Option<{}>,", name, type_string)?;
                let prefix = version_condition_string
                    .map(|version| format!("{}\n", version))
                    .unwrap_or_default();
                methods.push(format!(
                    "\n{prefix}    pub fn {name}{bounds}(mut self, {name}: {param_type}) -> Self {{
        self.{name} = Some({name}{conversion});
        self
    }}",
                    prefix = prefix,
                    param_type = param_type,
                    name = name,
                    conversion = conversion,
                    bounds = bounds
                ));
                properties.push(property);
            }
            Err(_) => writeln!(w, "    //{}: /*Unknown type*/,", property.name)?,
        }
    }
    writeln!(
        w,
        "}}

impl {}Builder {{
    pub fn new() -> Self {{
        Self::default()
    }}
",
        analysis.name
    )?;

    writeln!(
        w,
        "
    pub fn build(self) -> {} {{
        let mut properties: Vec<(&str, &dyn ToValue)> = vec![];",
        analysis.name
    )?;
    for property in &properties {
        let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
        version_condition_no_doc(w, env, property.version, false, 2)?;
        writeln!(
            w,
            "\
            if let Some(ref {field}) = self.{field} {{
                properties.push((\"{name}\", {field}));
            }}",
            name = property.name,
            field = name
        )?;
    }
    let glib_crate_name = if env.namespaces.is_glib_crate {
        "crate"
    } else {
        "glib"
    };

    // The split allows us to not have clippy::let_and_return lint disabled
    if let Some(code) = analysis.builder_postprocess.as_ref() {
        writeln!(
            w,
            r#"        let ret = {}::Object::new::<{}>(&properties)
                .expect("Failed to create an instance of {}");"#,
            glib_crate_name, analysis.name, analysis.name,
        )?;
        writeln!(w, "        {{\n            {}\n        }}", code)?;
        writeln!(w, "    ret\n    }}")?;
    } else {
        writeln!(
            w,
            r#"        {}::Object::new::<{}>(&properties)
                .expect("Failed to create an instance of {}")"#,
            glib_crate_name, analysis.name, analysis.name,
        )?;
        writeln!(w, "\n    }}")?;
    }

    for method in methods {
        writeln!(w, "{}", method)?;
    }
    writeln!(w, "}}")
}

fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    write!(w, "pub trait {}: 'static {{", analysis.trait_name)?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            true,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, true, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, true, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, true, 1)?;
    }
    writeln!(w, "}}")?;

    writeln!(w)?;
    write!(
        w,
        "impl<O: IsA<{}>> {} for O {{",
        analysis.name, analysis.trait_name,
    )?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            false,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, false, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, false, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, false, 1)?;
    }
    writeln!(w, "}}")?;

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::cfg_deprecated_string(env, analysis.deprecated_version, false, 0) {
        cfgs.push(cfg);
    }

    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);

    let none_type = if !analysis.final_type {
        format!(", NONE_{}", analysis.name.to_snake().to_uppercase())
    } else {
        String::new()
    };

    contents.push(format!(
        "pub use self::{}::{{{}{}}};",
        module_name, analysis.name, none_type
    ));
    if need_generate_trait(analysis) {
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!(
            "\tpub use super::{}::{};",
            module_name, analysis.trait_name
        ));
    }

    if !analysis.builder_properties.is_empty() {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{}Builder;",
            module_name, analysis.name
        ));
    }
}
<commit_msg>codegen/object: Add missing deprecation attribute to builders<commit_after>use super::{child_properties, function, general, properties, signal, trait_impls};
use crate::codegen::general::cfg_deprecated_string;
use crate::{
    analysis::special_functions::Type,
    analysis::{self, rust_type::RustType},
    case::CaseExt,
    codegen::general::{version_condition_no_doc, version_condition_string},
    env::Env,
    library, nameutil,
    traits::IntoString,
};
use std::io::{Result, Write};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    general::start_comments(w, &env.config)?;
    general::uses(w, env, &analysis.imports, analysis.version)?;

    general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        analysis.c_class_type.as_deref(),
        &analysis.get_type,
        analysis.is_interface,
        &analysis.supertypes,
    )?;

    if need_generate_inherent(analysis) {
        writeln!(w)?;
        write!(w, "impl {} {{", analysis.name)?;
        for func_analysis in &analysis.constructors() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                function::generate(
                    w,
                    env,
                    func_analysis,
                    Some(&analysis.specials),
                    analysis.version,
                    false,
                    false,
                    1,
                )?;
            }

            for property in &analysis.properties {
                properties::generate(w, env, property, false, false, 1)?;
            }

            for child_property in &analysis.child_properties {
                child_properties::generate(w, env, child_property, false, false, 1)?;
            }
        }

        for func_analysis in &analysis.functions() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                signal::generate(w, env, signal_analysis, false, false, 1)?;
            }
        }

        writeln!(w, "}}")?;

        general::declare_default_from_new(w, env, &analysis.name, &analysis.functions)?;
    }

    trait_impls::generate(
        w,
        env,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
        analysis.version,
    )?;

    if !analysis.builder_properties.is_empty() {
        writeln!(w)?;
        generate_builder(w, env, analysis)?;
    }

    if analysis.concurrency != library::Concurrency::None {
        writeln!(w)?;
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            writeln!(w, "unsafe impl Send for {} {{}}", analysis.name)?;
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name)?;
            } else {
                writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name)?;
            }

            writeln!(w, "    fn is_unique(&self) -> bool {{")?;
            writeln!(w, "        self.ref_count() == 1")?;
            writeln!(w, "    }}")?;

            writeln!(w, "}}")?;
        }
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name)?;
    }

    if !analysis.final_type {
        writeln!(w)?;
        writeln!(
            w,
            "pub const NONE_{}: Option<&{}> = None;",
            analysis.name.to_snake().to_uppercase(),
            analysis.name
        )?;
    }

    if need_generate_trait(analysis) {
        writeln!(w)?;
        generate_trait(w, env, analysis)?;
    }

    if generate_display_trait && !analysis.specials.has_trait(Type::Display) {
        writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?;
        // Generate Display trait implementation.
        writeln!(
            w,
            "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\tf.write_str(\"{}\")\n\
             \t}}\n\
             }}",
            analysis.name
        )?;
    }

    Ok(())
}

// TODO: instead create a Vec<> inside the Builder instead of Options.
fn generate_builder(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    let mut methods = vec![];
    let mut properties = vec![];
    writeln!(w, "#[derive(Clone, Default)]")?;
    writeln!(w, "pub struct {}Builder {{", analysis.name)?;
    for property in &analysis.builder_properties {
        match RustType::try_new(env, property.typ) {
            Ok(type_string) => {
                let type_string = match type_string.as_str() {
                    s if nameutil::is_gstring(s) => "String",
                    "Vec<GString>" | "Vec<glib::GString>" | "Vec<crate::GString>" => "Vec<String>",
                    typ => typ,
                };
                let direction = if property.is_get {
                    library::ParameterDirection::In
                } else {
                    library::ParameterDirection::Out
                };
                let mut param_type = RustType::builder(env, property.typ)
                    .direction(direction)
                    .ref_mode(property.set_in_ref_mode)
                    .try_build()
                    .into_string();
                let (param_type_override, bounds, conversion) = match &param_type[..] {
                    "&str" => (None, String::new(), ".to_string()"),
                    "&[&str]" => (Some("Vec<String>".to_string()), String::new(), ""),
                    _ if !property.bounds.is_empty() => {
                        let (bounds, _) = function::bounds(&property.bounds, &[], false, false);
                        let alias = property
                            .bounds
                            .get_parameter_alias_info(&property.name)
                            .map(|(alias, _)| format!("&{}", alias));
                        (alias, bounds, ".clone().upcast()")
                    }
                    typ if typ.starts_with('&') => (None, String::new(), ".clone()"),
                    _ => (None, String::new(), ""),
                };
                if let Some(param_type_override) = param_type_override {
                    param_type = param_type_override.to_string();
                }
                let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
                let version_condition_string =
                    version_condition_string(env, property.version, false, 1);
                let deprecated_string =
                    cfg_deprecated_string(env, property.deprecated_version, false, 1);
                if let Some(ref version_condition_string) = version_condition_string {
                    writeln!(w, "{}", version_condition_string)?;
                }
                if let Some(ref deprecated_string) = deprecated_string {
                    writeln!(w, "{}", deprecated_string)?;
                }
                writeln!(w, "    {}: Option<{}>,", name, type_string)?;
                let version_prefix = version_condition_string
                    .map(|version| format!("{}\n", version))
                    .unwrap_or_default();

                let deprecation_prefix = deprecated_string
                    .map(|version| format!("{}\n", version))
                    .unwrap_or_default();

                methods.push(format!(
                    "\n{version_prefix}{deprecation_prefix}    pub fn {name}{bounds}(mut self, {name}: {param_type}) -> Self {{
        self.{name} = Some({name}{conversion});
        self
    }}",
                    version_prefix = version_prefix,
                    deprecation_prefix = deprecation_prefix,
                    param_type = param_type,
                    name = name,
                    conversion = conversion,
                    bounds = bounds
                ));
                properties.push(property);
            }
            Err(_) => writeln!(w, "    //{}: /*Unknown type*/,", property.name)?,
        }
    }
    writeln!(
        w,
        "}}

impl {}Builder {{
    pub fn new() -> Self {{
        Self::default()
    }}
",
        analysis.name
    )?;

    writeln!(
        w,
        "
    pub fn build(self) -> {} {{
        let mut properties: Vec<(&str, &dyn ToValue)> = vec![];",
        analysis.name
    )?;
    for property in &properties {
        let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
        version_condition_no_doc(w, env, property.version, false, 2)?;
        writeln!(
            w,
            "\
            if let Some(ref {field}) = self.{field} {{
                properties.push((\"{name}\", {field}));
            }}",
            name = property.name,
            field = name
        )?;
    }
    let glib_crate_name = if env.namespaces.is_glib_crate {
        "crate"
    } else {
        "glib"
    };

    // The split allows us to not have clippy::let_and_return lint disabled
    if let Some(code) = analysis.builder_postprocess.as_ref() {
        writeln!(
            w,
            r#"        let ret = {}::Object::new::<{}>(&properties)
                .expect("Failed to create an instance of {}");"#,
            glib_crate_name, analysis.name, analysis.name,
        )?;
        writeln!(w, "        {{\n            {}\n        }}", code)?;
        writeln!(w, "    ret\n    }}")?;
    } else {
        writeln!(
            w,
            r#"        {}::Object::new::<{}>(&properties)
                .expect("Failed to create an instance of {}")"#,
            glib_crate_name, analysis.name, analysis.name,
        )?;
        writeln!(w, "\n    }}")?;
    }

    for method in methods {
        writeln!(w, "{}", method)?;
    }
    writeln!(w, "}}")
}

fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    write!(w, "pub trait {}: 'static {{", analysis.trait_name)?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            true,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, true, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, true, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, true, 1)?;
    }
    writeln!(w, "}}")?;

    writeln!(w)?;
    write!(
        w,
        "impl<O: IsA<{}>> {} for O {{",
        analysis.name, analysis.trait_name,
    )?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            false,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, false, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, false, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, false, 1)?;
    }
    writeln!(w, "}}")?;

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::cfg_deprecated_string(env, analysis.deprecated_version, false, 0) {
        cfgs.push(cfg);
    }

    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);

    let none_type = if !analysis.final_type {
        format!(", NONE_{}", analysis.name.to_snake().to_uppercase())
    } else {
        String::new()
    };

    contents.push(format!(
        "pub use self::{}::{{{}{}}};",
        module_name, analysis.name, none_type
    ));
    if need_generate_trait(analysis) {
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!(
            "\tpub use super::{}::{};",
            module_name, analysis.trait_name
        ));
    }

    if !analysis.builder_properties.is_empty() {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{}Builder;",
            module_name, analysis.name
        ));
    }
}
<|endoftext|>


<commit_before>#![doc(hidden)]

use std::ops::{Div, Mul, Neg};

use complex::Complex;

use {
    Chain, Col, ColMut, ColVec, Mat, Product, Row, RowMut, RowVec, Scaled, Transposed, SubMat,
    SubMatMut,
};
use traits::{Matrix, Slice};

impl<T, M> Div<T> for Scaled<M> where M: Matrix<Elem=T>, T: Div<Output=T> {
    type Output = Scaled<M>;

    fn div(self, rhs: T) -> Scaled<M> {
        Scaled(self.0 / rhs, self.1)
    }
}

impl<T, M> Neg for Scaled<M> where M: Matrix<Elem=T>, T: Neg<Output=T> {
    type Output = Scaled<M>;

    fn neg(self) -> Scaled<M> {
        Scaled(self.0.neg(), self.1)
    }
}

impl<'a, T> Mul<T> for Chain<'a, T> {
    type Output = Scaled<Chain<'a, T>>;

    fn mul(self, rhs: T) -> Scaled<Chain<'a, T>> {
        Scaled(rhs, self)
    }
}

macro_rules! chain {
    ($($t:ty),+) => {
        $(
            impl<'a> Mul<Chain<'a, $t>> for $t {
                type Output = Scaled<Chain<'a, $t>>;

                fn mul(self, rhs: Chain<'a, $t>) -> Scaled<Chain<'a, $t>> {
                    rhs * self
                }
            }
         )+
    };
}

chain!(f32, f64, Complex<f32>, Complex<f64>);

macro_rules! mul {
    ($ty:ident $ty_mut:ident $ty_owned:ident for $($t:ty),+) => {
        // Core implementations
        impl<'a, T> Mul<T> for $ty<'a, T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                Scaled(rhs, self)
            }
        }

        // "Forwarding" implementations
        impl<'a, 'b, T> Mul<T> for &'a $ty_mut<'b, T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                self.slice(..) * rhs
            }
        }

        impl<'a, T> Mul<T> for &'a $ty_owned<T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                self.slice(..) * rhs
            }
        }

        impl<T> Mul<T> for $ty_owned<T> {
            type Output = Scaled<$ty_owned<T>>;

            fn mul(self, rhs: T) -> Scaled<$ty_owned<T>> {
                Scaled(rhs, self)
            }
        }

        // Reverse operations
        $(
            impl<'a> Mul<$ty<'a, $t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: $ty<$t>) -> Scaled<$ty<$t>> {
                    rhs * self
                }
            }

            impl<'a, 'b> Mul<&'a $ty_mut<'b, $t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: &'a $ty_mut<$t>) -> Scaled<$ty<'a, $t>> {
                    rhs * self
                }
            }

            impl<'a> Mul<&'a $ty_owned<$t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: &'a $ty_owned<$t>) -> Scaled<$ty<'a, $t>> {
                    rhs * self
                }
            }

            impl Mul<$ty_owned<$t>> for $t {
                type Output = Scaled<$ty_owned<$t>>;

                fn mul(self, rhs: $ty_owned<$t>) -> Scaled<$ty_owned<$t>> {
                    rhs * self
                }
            }
         )+
    };
}

mul!(Col ColMut ColVec for f32, f64, Complex<f32>, Complex<f64>);
mul!(Row RowMut RowVec for f32, f64, Complex<f32>, Complex<f64>);
mul!(SubMat SubMatMut Mat for f32, f64, Complex<f32>, Complex<f64>);

impl<T, L, R> Mul<T> for Product<L, R> where
    L: Matrix<Elem=T>,
    R: Matrix<Elem=T>,
{
    type Output = Scaled<Product<L, R>>;

    fn mul(self, rhs: T) -> Scaled<Product<L, R>> {
        Scaled(rhs, self)
    }
}

macro_rules! product {
    ($($t:ty),+) => {
        $(
            impl<L, R> Mul<Product<L, R>> for $t where
                L: Matrix<Elem=$t>,
                R: Matrix<Elem=$t>,
            {
                type Output = Scaled<Product<L, R>>;

                fn mul(self, rhs: Product<L, R>) -> Scaled<Product<L, R>> {
                    rhs * self
                }
            }
         )+
    };
}

product!(f32, f64, Complex<f32>, Complex<f64>);

impl<T, M> Mul<T> for Scaled<M> where
    M: Matrix<Elem=T>,
    T: Mul<Output=T>,
{
    type Output = Scaled<M>;

    fn mul(self, rhs: T) -> Scaled<M> {
        Scaled(self.0 * rhs, self.1)
    }
}

macro_rules! scaled {
    ($($t:ty),+) => {
        $(
            impl<M> Mul<Scaled<M>> for $t where M: Matrix<Elem=$t> {
                type Output = Scaled<M>;

                fn mul(self, rhs: Scaled<M>) -> Scaled<M> {
                    Scaled(self * rhs.0, rhs.1)
                }
            }
         )+
    };
}

scaled!(f32, f64, Complex<f32>, Complex<f64>);

impl<T> Mul<T> for Transposed<Mat<T>> {
    type Output = Scaled<Transposed<Mat<T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<Mat<T>>> {
        Scaled(rhs, self)
    }
}

impl<'a, T> Mul<T> for &'a Transposed<Mat<T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        self.slice(..) * rhs
    }
}

impl<'a, T> Mul<T> for Transposed<SubMat<'a, T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        Scaled(rhs, self)
    }
}

impl<'a, 'b, T> Mul<T> for &'a Transposed<SubMatMut<'b, T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        self.slice(..) * rhs
    }
}

macro_rules! transposed {
    ($($t:ty),+) => {
        $(
            impl Mul<Transposed<Mat<$t>>> for $t {
                type Output = Scaled<Transposed<Mat<$t>>>;

                fn mul(self, rhs: Transposed<Mat<$t>>) -> Scaled<Transposed<Mat<$t>>> {
                    rhs * self
                }
            }

            impl<'a> Mul<&'a Transposed<Mat<$t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(self, rhs: &'a Transposed<Mat<$t>>) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }

            impl<'a> Mul<Transposed<SubMat<'a, $t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(
                    self,
                    rhs: Transposed<SubMat<'a, $t>>,
                ) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }

            impl<'a, 'b> Mul<&'a Transposed<SubMatMut<'b, $t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(
                    self,
                    rhs: &'a Transposed<SubMatMut<'b, $t>>,
                ) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }
         )+
    };
}

transposed!(f32, f64, Complex<f32>, Complex<f64>);
<commit_msg>feat: add `A * B / beta`<commit_after>#![doc(hidden)]

use std::ops::{Div, Mul, Neg};

use complex::Complex;
use onezero::One;

use {
    Chain, Col, ColMut, ColVec, Mat, Product, Row, RowMut, RowVec, Scaled, Transposed, SubMat,
    SubMatMut,
};
use traits::{Matrix, Slice};

// NOTE Core
impl<T, M> Div<T> for Scaled<M> where M: Matrix<Elem=T>, T: Div<Output=T> {
    type Output = Scaled<M>;

    fn div(self, rhs: T) -> Scaled<M> {
        Scaled(self.0 / rhs, self.1)
    }
}

// NOTE Core
impl<T, L, R> Div<T> for Product<L, R> where
    L: Matrix<Elem=T>,
    R: Matrix<Elem=T>,
    T: Div<Output=T> + One,
{
    type Output = Scaled<Product<L, R>>;

    fn div(self, rhs: T) -> Scaled<Product<L, R>> {
        Scaled(T::one() / rhs, self)
    }
}

// NOTE Core
impl<T, M> Neg for Scaled<M> where M: Matrix<Elem=T>, T: Neg<Output=T> {
    type Output = Scaled<M>;

    fn neg(self) -> Scaled<M> {
        Scaled(self.0.neg(), self.1)
    }
}

// NOTE Core
impl<'a, T> Mul<T> for Chain<'a, T> {
    type Output = Scaled<Chain<'a, T>>;

    fn mul(self, rhs: T) -> Scaled<Chain<'a, T>> {
        Scaled(rhs, self)
    }
}

macro_rules! chain {
    ($($t:ty),+) => {
        $(
            // NOTE Secondary
            impl<'a> Mul<Chain<'a, $t>> for $t {
                type Output = Scaled<Chain<'a, $t>>;

                fn mul(self, rhs: Chain<'a, $t>) -> Scaled<Chain<'a, $t>> {
                    rhs * self
                }
            }
         )+
    };
}

chain!(f32, f64, Complex<f32>, Complex<f64>);

macro_rules! mul {
    ($ty:ident $ty_mut:ident $ty_owned:ident for $($t:ty),+) => {
        // Core implementations
        impl<'a, T> Mul<T> for $ty<'a, T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                Scaled(rhs, self)
            }
        }

        // "Forwarding" implementations
        impl<'a, 'b, T> Mul<T> for &'a $ty_mut<'b, T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                self.slice(..) * rhs
            }
        }

        impl<'a, T> Mul<T> for &'a $ty_owned<T> {
            type Output = Scaled<$ty<'a, T>>;

            fn mul(self, rhs: T) -> Scaled<$ty<'a, T>> {
                self.slice(..) * rhs
            }
        }

        impl<T> Mul<T> for $ty_owned<T> {
            type Output = Scaled<$ty_owned<T>>;

            fn mul(self, rhs: T) -> Scaled<$ty_owned<T>> {
                Scaled(rhs, self)
            }
        }

        // Reverse operations
        $(
            impl<'a> Mul<$ty<'a, $t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: $ty<$t>) -> Scaled<$ty<$t>> {
                    rhs * self
                }
            }

            impl<'a, 'b> Mul<&'a $ty_mut<'b, $t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: &'a $ty_mut<$t>) -> Scaled<$ty<'a, $t>> {
                    rhs * self
                }
            }

            impl<'a> Mul<&'a $ty_owned<$t>> for $t {
                type Output = Scaled<$ty<'a, $t>>;

                fn mul(self, rhs: &'a $ty_owned<$t>) -> Scaled<$ty<'a, $t>> {
                    rhs * self
                }
            }

            impl Mul<$ty_owned<$t>> for $t {
                type Output = Scaled<$ty_owned<$t>>;

                fn mul(self, rhs: $ty_owned<$t>) -> Scaled<$ty_owned<$t>> {
                    rhs * self
                }
            }
         )+
    };
}

mul!(Col ColMut ColVec for f32, f64, Complex<f32>, Complex<f64>);
mul!(Row RowMut RowVec for f32, f64, Complex<f32>, Complex<f64>);
mul!(SubMat SubMatMut Mat for f32, f64, Complex<f32>, Complex<f64>);

impl<T, L, R> Mul<T> for Product<L, R> where
    L: Matrix<Elem=T>,
    R: Matrix<Elem=T>,
{
    type Output = Scaled<Product<L, R>>;

    fn mul(self, rhs: T) -> Scaled<Product<L, R>> {
        Scaled(rhs, self)
    }
}

macro_rules! product {
    ($($t:ty),+) => {
        $(
            impl<L, R> Mul<Product<L, R>> for $t where
                L: Matrix<Elem=$t>,
                R: Matrix<Elem=$t>,
            {
                type Output = Scaled<Product<L, R>>;

                fn mul(self, rhs: Product<L, R>) -> Scaled<Product<L, R>> {
                    rhs * self
                }
            }
         )+
    };
}

product!(f32, f64, Complex<f32>, Complex<f64>);

impl<T, M> Mul<T> for Scaled<M> where
    M: Matrix<Elem=T>,
    T: Mul<Output=T>,
{
    type Output = Scaled<M>;

    fn mul(self, rhs: T) -> Scaled<M> {
        Scaled(self.0 * rhs, self.1)
    }
}

macro_rules! scaled {
    ($($t:ty),+) => {
        $(
            impl<M> Mul<Scaled<M>> for $t where M: Matrix<Elem=$t> {
                type Output = Scaled<M>;

                fn mul(self, rhs: Scaled<M>) -> Scaled<M> {
                    Scaled(self * rhs.0, rhs.1)
                }
            }
         )+
    };
}

scaled!(f32, f64, Complex<f32>, Complex<f64>);

impl<T> Mul<T> for Transposed<Mat<T>> {
    type Output = Scaled<Transposed<Mat<T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<Mat<T>>> {
        Scaled(rhs, self)
    }
}

impl<'a, T> Mul<T> for &'a Transposed<Mat<T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        self.slice(..) * rhs
    }
}

impl<'a, T> Mul<T> for Transposed<SubMat<'a, T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        Scaled(rhs, self)
    }
}

impl<'a, 'b, T> Mul<T> for &'a Transposed<SubMatMut<'b, T>> {
    type Output = Scaled<Transposed<SubMat<'a, T>>>;

    fn mul(self, rhs: T) -> Scaled<Transposed<SubMat<'a, T>>> {
        self.slice(..) * rhs
    }
}

macro_rules! transposed {
    ($($t:ty),+) => {
        $(
            impl Mul<Transposed<Mat<$t>>> for $t {
                type Output = Scaled<Transposed<Mat<$t>>>;

                fn mul(self, rhs: Transposed<Mat<$t>>) -> Scaled<Transposed<Mat<$t>>> {
                    rhs * self
                }
            }

            impl<'a> Mul<&'a Transposed<Mat<$t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(self, rhs: &'a Transposed<Mat<$t>>) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }

            impl<'a> Mul<Transposed<SubMat<'a, $t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(
                    self,
                    rhs: Transposed<SubMat<'a, $t>>,
                ) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }

            impl<'a, 'b> Mul<&'a Transposed<SubMatMut<'b, $t>>> for $t {
                type Output = Scaled<Transposed<SubMat<'a, $t>>>;

                fn mul(
                    self,
                    rhs: &'a Transposed<SubMatMut<'b, $t>>,
                ) -> Scaled<Transposed<SubMat<'a, $t>>> {
                    rhs * self
                }
            }
         )+
    };
}

transposed!(f32, f64, Complex<f32>, Complex<f64>);
<|endoftext|>


<commit_before>use crate::Msg;
use chrono::{NaiveDate};
use seed::prelude::*;

const NUM_NEWS_ITEMS :usize = 1;

struct NewsItem {
    pub content: String,
    pub date: NaiveDate,
}

fn get_news_items() -> [NewsItem; NUM_NEWS_ITEMS] {
    let news_items: [NewsItem; NUM_NEWS_ITEMS] = [
        NewsItem{
            content: String::from(
                "Ethereum bounty contractshave now been added for anyone that would like to try to push the boundaries of human knowledge. The first contract is a competition for the correct solution to a³+b³+c³ = 33. Tweet me if you need assistance using these contracts!"),
            date: NaiveDate::from_ymd(2019, 1, 22)
        }
    ];
    news_items
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let news_items = get_news_items();
    let mut table_rows = vec![div![]; NUM_NEWS_ITEMS];
    for i in 0..news_items.len() {
        let news_item = &news_items[i];
        table_rows[i] = tr![
            td![news_item.date.to_string()],
            td![news_item.content]
        ];
    };

    table![table_rows]
}<commit_msg>Attempting to render html in news item<commit_after>use crate::Msg;
use chrono::{NaiveDate};
use seed::prelude::*;

struct NewsItem {
    pub content: Vec<Node<Msg>>,
    pub date: NaiveDate,
}

const NUM_NEWS_ITEMS :usize = 2;
fn get_news_items() -> [NewsItem; NUM_NEWS_ITEMS] {
    let news_items: [NewsItem; NUM_NEWS_ITEMS] = [
        NewsItem{
            content: vec![
                a!["Ethereum bounty contracts", attrs!{At::Class => "link", At::Href => "https://github.com/craigmayhew/ethereum-contracts"}],
                span!["have now been added for anyone that would like to try to push the boundaries of human knowledge.
                The first contract is a competition for the correct solution to a³+b³+c³ = 33.
                Tweet me if you need assistance using these contracts!"]
            ],
            date: NaiveDate::from_ymd(2019, 1, 22)
        },
        NewsItem{
            content: vec![
                div!["huh"]
            ],
            date: NaiveDate::from_ymd(2019, 1, 22)
        }
    ];
    news_items
}

pub fn render() -> seed::dom_types::Node<Msg> {
    let news_items = get_news_items();
    let mut table_rows = vec![div![]; NUM_NEWS_ITEMS];
    for i in 0..news_items.len() {
        let news_item = &news_items[i];
        table_rows[i] = tr![
            td![news_item.date.to_string()],
            td![news_item.content.to_vec()]
        ];
    };

    table![table_rows]
}<|endoftext|>


<commit_before>
pub mod ast;
pub mod ast_builder;
pub mod char_utils;
pub mod input_stream;
pub mod precedence;
pub mod tokenizer;
pub mod token_kind;

pub use parser::input_stream::StreamPosition;
pub use parser::input_stream::InputStream;
pub use parser::input_stream::VecInputStream;

pub use parser::ast_builder::AstBuilder;
<commit_msg>Removed useless "pub use" in parser container module.<commit_after>
pub mod ast;
pub mod ast_builder;
pub mod char_utils;
pub mod input_stream;
pub mod precedence;
pub mod tokenizer;
pub mod token_kind;

<|endoftext|>