<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagutil;

use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagentrylink::external::ExternalLinker;
use libimagutil::warn_result::*;
use libimagutil::warn_exit::warn_exit;
use libimagutil::info_result::*;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-link",
                                    &version!()[..],
                                    "Link entries",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => warn_exit("No commandline call", 1)
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(Some(e)) => {
                    e.get_internal_links()
                        .map(|links| {
                            let i = links
                                .filter_map(|l| {
                                    l.to_str()
                                        .map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
                                        .ok()
                                })
                                .enumerate();

                            for (i, link) in i {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err_trace()
                        .ok();
                },

                Ok(None) => {
                    warn!("Entry not found: {:?}", entry);
                    break;
                }

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn_exit("No 'from' entry", 1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn_exit("No 'to' entry", 1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(Some(e)) => Some(e),
                Ok(None)    => None,
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(Some(e)) => v.push(e),
                    Ok(None) => warn!("Entry not found: {:?}", v),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
    use std::path::PathBuf;
    use libimagstore::storeid::StoreId;

    StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
        .and_then(|id| rt.store().get(id))
}

fn handle_external_linking(rt: &Runtime) {
    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error_exit(&entry.unwrap_err(), 1);
    }
    let entry = entry.unwrap();
    if entry.is_none() {
        warn!("Entry not found: {:?}", entry_name);
        return;
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("add").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.add_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("remove").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.remove_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    entry.set_external_links(store, links)
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.enumerate() {
                println!("{: <3}: {}", i, link);
            }
            Ok(())
        })
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

<commit_msg>imag-link: Fix iterator usage<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagutil;

use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagentrylink::external::ExternalLinker;
use libimagutil::warn_result::*;
use libimagutil::warn_exit::warn_exit;
use libimagutil::info_result::*;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-link",
                                    &version!()[..],
                                    "Link entries",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => warn_exit("No commandline call", 1)
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(Some(e)) => {
                    e.get_internal_links()
                        .map(|links| {
                            let i = links
                                .filter_map(|l| {
                                    l.to_str()
                                        .map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
                                        .ok()
                                })
                                .enumerate();

                            for (i, link) in i {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err_trace()
                        .ok();
                },

                Ok(None) => {
                    warn!("Entry not found: {:?}", entry);
                    break;
                }

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn_exit("No 'from' entry", 1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn_exit("No 'to' entry", 1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(Some(e)) => Some(e),
                Ok(None)    => None,
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(Some(e)) => v.push(e),
                    Ok(None) => warn!("Entry not found: {:?}", v),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
    use std::path::PathBuf;
    use libimagstore::storeid::StoreId;

    StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
        .and_then(|id| rt.store().get(id))
}

fn handle_external_linking(rt: &Runtime) {
    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error_exit(&entry.unwrap_err(), 1);
    }
    let entry = entry.unwrap();
    if entry.is_none() {
        warn!("Entry not found: {:?}", entry_name);
        return;
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("add").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.add_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("remove").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.remove_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    entry.set_external_links(store, links)
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.enumerate() {
                match link {
                    Ok(link) => println!("{: <3}: {}", i, link),
                    Err(e)   => trace_error(&e),
                }
            }
            Ok(())
        })
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

<|endoftext|>


<commit_before>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;
use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagutil::trace::trace_error;
use libimagentrylink::external::ExternalLinker;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-link";
    let version = &version!()[..];
    let about = "Link entries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.unwrap_err());
            exit(1);
        }
    };

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => {
                    warn!("No commandline call");
                    exit(1);
                },
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;
    use libimagutil::trace::trace_error;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(e) => {
                    e.get_internal_links()
                        .map(|links| {
                            for (i, link) in links.iter().map(|l| l.to_str()).filter_map(|x| x).enumerate() {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err(|e| trace_error(&e))
                        .ok();
                },

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn!("No 'from' entry");
                exit(1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn!("No 'to' entry");
                exit(1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(e) => Some(e),
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(e) => v.push(e),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<FileLockEntry<'a>, StoreError> {
    use libimagstore::storeid::build_entry_path;
    build_entry_path(rt.store(), name)
        .and_then(|path| rt.store().retrieve(path))
}

fn handle_external_linking(rt: &Runtime) {
    use libimagutil::trace::trace_error;

    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error(&entry.unwrap_err());
        exit(1);
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("add").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        debug!("URL parsing error...");
        trace_error(&link.unwrap_err());
        debug!("Exiting");
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.add_external_link(store, link) {
        debug!("Error while adding external link...");
        trace_error(&e);
    } else {
        debug!("Everything worked well");
        info!("Ok");
    }
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("remove").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        trace_error(&link.unwrap_err());
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.remove_external_link(store, link) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    if let Err(e) = entry.set_external_links(store, links) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    let res = entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.enumerate() {
                println!("{: <3}: {}", i, link);
            }
            Ok(())
        });

    match res {
        Err(e) => {
            trace_error(&e);
        },
        Ok(_) => {
            info!("Ok");
        },
    }
}

<commit_msg>fixed imag-link iterator error<commit_after>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;
use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagutil::trace::trace_error;
use libimagentrylink::external::ExternalLinker;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let name = "imag-link";
    let version = &version!()[..];
    let about = "Link entries";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.unwrap_err());
            exit(1);
        }
    };

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => {
                    warn!("No commandline call");
                    exit(1);
                },
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;
    use libimagutil::trace::trace_error;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(e) => {
                    e.get_internal_links()
                        .map(|links| {
                            for (i, link) in links.iter().map(|l| l.to_str()).filter_map(|x| x).enumerate() {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err(|e| trace_error(&e))
                        .ok();
                },

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn!("No 'from' entry");
                exit(1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn!("No 'to' entry");
                exit(1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error(&e);
                        exit(1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(e) => Some(e),
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(e) => v.push(e),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<FileLockEntry<'a>, StoreError> {
    use libimagstore::storeid::build_entry_path;
    build_entry_path(rt.store(), name)
        .and_then(|path| rt.store().retrieve(path))
}

fn handle_external_linking(rt: &Runtime) {
    use libimagutil::trace::trace_error;

    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error(&entry.unwrap_err());
        exit(1);
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("add").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        debug!("URL parsing error...");
        trace_error(&link.unwrap_err());
        debug!("Exiting");
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.add_external_link(store, link) {
        debug!("Error while adding external link...");
        trace_error(&e);
    } else {
        debug!("Everything worked well");
        info!("Ok");
    }
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("remove").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        trace_error(&link.unwrap_err());
        exit(1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.remove_external_link(store, link) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    if let Err(e) = entry.set_external_links(store, links) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    let res = entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.iter().enumerate() {
                println!("{: <3}: {}", i, link);
            }
            Ok(())
        });

    match res {
        Err(e) => {
            trace_error(&e);
        },
        Ok(_) => {
            info!("Ok");
        },
    }
}

<|endoftext|>


<commit_before>use groupable::Groupable;
use hyper::uri::RequestUri;
use hyper::uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority};
use std::collections::HashMap;
use url::{form_urlencoded, Url};

type QueryStore = HashMap<String, Vec<String>>;

#[derive(Debug, PartialEq, Eq)]
pub struct Params(QueryStore);

// TODO: remove it in favor of Params
pub type Query = Params;

impl Params {
    /// Retrieves the first value from the query for `key`, or `None` if not present.
    ///
    /// # Notes
    /// There may be multiple values per key, if all of the values for a given
    /// `key` are required, then use `all`.
    //FIXME: Implement via Indexing whenever IndexGet is supported
    pub fn get(&self, key: &str) -> Option<&str> {
        self.0.get(key).and_then(|v| v.first().map(|s| &**s))
    }

    /// Retrieve all values from the query for `key`, or `None` if none are present.
    pub fn all(&self, key: &str) -> Option<&[String]> {
        self.0.get(key).map(|v| &**v)
    }
}

pub fn parse(encoded_string : &str) -> Params {
    Params(form_urlencoded::parse(encoded_string.as_bytes()).into_owned().group())
}

pub fn parse_uri(origin: &RequestUri) -> Params {
    let f = |query: Option<&str>| query.map(|q| parse(&*q));

    let result = match *origin {
        AbsoluteUri(ref url) => f(url.query()),
        AbsolutePath(ref s) => f(s.splitn(2, '?').nth(1)),
        Star | Authority(..) => None
    };

    result.unwrap_or_else(|| Params(HashMap::new()))
}

#[test]
fn parses_encoded_string_with_duplicate_keys() {
    let store = parse("foo=bar&message=hello&message=world");
    assert_eq!(
        store.all("foo"),
        Some(&["bar".to_string()][..])
    );
    assert_eq!(store.get("message"), Some("hello"));
    // Ensure the ordering is correct
    assert_eq!(
        store.all("message"),
        Some(&["hello".to_string(), "world".to_string()][..])
    );
}

#[test]
fn parses_urlencoded_characters() {
    let store = parse("message=hello%20world");
    assert_eq!(store.get("message"), Some("hello world"));
}

#[test]
fn splits_and_parses_an_url() {
    use url::Url;
    let t = |url| {
        let store = parse_uri(&url);
        assert_eq!(store.get("foo"), Some("bar"));
        assert_eq!(store.get("foo").unwrap_or("other"), "bar");
        assert_eq!(store.get("bar").unwrap_or("other"), "other");
        assert_eq!(store.all("message"),
                        Some(&["hello".to_string(), "world".to_string()][..]));
        assert_eq!(store.all("car"), None);
    };

    let raw = "http://www.foo.bar/query/test?foo=bar&message=hello&message=world";
    t(AbsoluteUri(Url::parse(raw).unwrap()));

    t(AbsolutePath("/query/test?foo=bar&message=hello&message=world".to_string()));

    assert_eq!(parse_uri(&Star), Params(HashMap::new()));

    let store = parse_uri(&Authority("host.com".to_string()));
    assert_eq!(store, Params(HashMap::new()));
}
<commit_msg>feat(request): expose query params as a map<commit_after>use groupable::Groupable;
use hyper::uri::RequestUri;
use hyper::uri::RequestUri::{Star, AbsoluteUri, AbsolutePath, Authority};
use std::collections::HashMap;
use url::{form_urlencoded, Url};

type QueryStore = HashMap<String, Vec<String>>;

#[derive(Debug, PartialEq, Eq)]
pub struct Params(QueryStore);

// TODO: remove it in favor of Params
pub type Query = Params;

impl Params {
    /// Retrieves the first value from the query for `key`, or `None` if not present.
    ///
    /// # Notes
    /// There may be multiple values per key, if all of the values for a given
    /// `key` are required, then use `all`.
    //FIXME: Implement via Indexing whenever IndexGet is supported
    pub fn get(&self, key: &str) -> Option<&str> {
        self.0.get(key).and_then(|v| v.first().map(|s| &**s))
    }

    /// Retrieve all values from the query for `key`, or `None` if none are present.
    pub fn all(&self, key: &str) -> Option<&[String]> {
        self.0.get(key).map(|v| &**v)
    }

    /// Retrieve the entire query map.
    pub fn map(&self) -> &HashMap<String, Vec<String>> {
        &self.0
    }
}

pub fn parse(encoded_string : &str) -> Params {
    Params(form_urlencoded::parse(encoded_string.as_bytes()).into_owned().group())
}

pub fn parse_uri(origin: &RequestUri) -> Params {
    let f = |query: Option<&str>| query.map(|q| parse(&*q));

    let result = match *origin {
        AbsoluteUri(ref url) => f(url.query()),
        AbsolutePath(ref s) => f(s.splitn(2, '?').nth(1)),
        Star | Authority(..) => None
    };

    result.unwrap_or_else(|| Params(HashMap::new()))
}

#[test]
fn parses_encoded_string_with_duplicate_keys() {
    let store = parse("foo=bar&message=hello&message=world");
    assert_eq!(
        store.all("foo"),
        Some(&["bar".to_string()][..])
    );
    assert_eq!(store.get("message"), Some("hello"));
    // Ensure the ordering is correct
    assert_eq!(
        store.all("message"),
        Some(&["hello".to_string(), "world".to_string()][..])
    );

    let map = store.map();
    assert_eq!(map.len(), 2);
    assert_eq!(map.get("foo"), Some(&vec!["bar".to_string()]));
    assert_eq!(
        map.get("message"),
        Some(&vec!["hello".to_string(), "world".to_string()])
    );
}

#[test]
fn parses_urlencoded_characters() {
    let store = parse("message=hello%20world");
    assert_eq!(store.get("message"), Some("hello world"));

    let map = store.map();
    assert_eq!(map.len(), 1);
    assert_eq!(map.get("message"), Some(&vec!["hello world".to_string()]));
}

#[test]
fn splits_and_parses_an_url() {
    use url::Url;
    let t = |url| {
        let store = parse_uri(&url);
        assert_eq!(store.get("foo"), Some("bar"));
        assert_eq!(store.get("foo").unwrap_or("other"), "bar");
        assert_eq!(store.get("bar").unwrap_or("other"), "other");
        assert_eq!(store.all("message"),
                        Some(&["hello".to_string(), "world".to_string()][..]));
        assert_eq!(store.all("car"), None);
    };

    let raw = "http://www.foo.bar/query/test?foo=bar&message=hello&message=world";
    t(AbsoluteUri(Url::parse(raw).unwrap()));

    t(AbsolutePath("/query/test?foo=bar&message=hello&message=world".to_string()));

    assert_eq!(parse_uri(&Star), Params(HashMap::new()));

    let store = parse_uri(&Authority("host.com".to_string()));
    assert_eq!(store, Params(HashMap::new()));
}
<|endoftext|>


<commit_before>use tls::*;

/// Error types for the state machine
pub enum StateChangeError {
    InvalidTransition,
    ParseError,
}

/// TLS machine possible states
#[derive(Copy,Clone,Debug,PartialEq)]
pub enum TlsState {
    None,
    ClientHello,
    AskResumeSession,
    ResumeSession,
    ServerHello,
    Certificate,
    CertificateSt,
    ServerKeyExchange,
    ServerHelloDone,
    ClientKeyExchange,
    ClientChangeCipherSpec,

    CRCertRequest,
    CRHelloDone,
    CRCert,
    CRClientKeyExchange,
    CRCertVerify,

    NoCertSKE,
    NoCertHelloDone,
    NoCertCKE,

    PskHelloDone,
    PskCKE,

    SessionEncrypted,

    Alert,

    Invalid,
}

fn tls_state_transition_handshake(state: TlsState, msg: &TlsMessageHandshake) -> Result<TlsState,StateChangeError> {
    match (state,msg) {
        (TlsState::None,             &TlsMessageHandshake::ClientHello(ref msg)) => {
            match msg.session_id {
                Some(_) => Ok(TlsState::AskResumeSession),
                _       => Ok(TlsState::ClientHello)
            }
        },
        // Server certificate
        (TlsState::ClientHello,      &TlsMessageHandshake::ServerHello(_))       => Ok(TlsState::ServerHello),
        (TlsState::ServerHello,      &TlsMessageHandshake::Certificate(_))       => Ok(TlsState::Certificate),
        // Server certificate, no client certificate requested
        (TlsState::Certificate,      &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::ServerKeyExchange),
        (TlsState::Certificate,      &TlsMessageHandshake::CertificateStatus(_)) => Ok(TlsState::CertificateSt),
        (TlsState::CertificateSt,    &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::ServerKeyExchange),
        (TlsState::ServerKeyExchange,&TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::ServerHelloDone),
        (TlsState::ServerHelloDone  ,&TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::ClientKeyExchange),
        // Server certificate, client certificate requested
        (TlsState::Certificate,      &TlsMessageHandshake::CertificateRequest(_))=> Ok(TlsState::CRCertRequest),
        (TlsState::ServerKeyExchange,&TlsMessageHandshake::CertificateRequest(_))=> Ok(TlsState::CRCertRequest),
        (TlsState::CRCertRequest,    &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::CRHelloDone),
        (TlsState::CRHelloDone,      &TlsMessageHandshake::Certificate(_))       => Ok(TlsState::CRCert),
        (TlsState::CRCert,           &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::CRClientKeyExchange),
        (TlsState::CRClientKeyExchange, &TlsMessageHandshake::CertificateVerify(_)) => Ok(TlsState::CRCertVerify),
        // Server has no certificate (but accepts anonymous)
        (TlsState::ServerHello,      &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::NoCertSKE),
        (TlsState::NoCertSKE,        &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::NoCertHelloDone),
        (TlsState::NoCertHelloDone,  &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::NoCertCKE),
        // PSK
        (TlsState::Certificate,      &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::PskHelloDone),
        (TlsState::PskHelloDone,     &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::PskCKE),
        // Resuming session
        (TlsState::AskResumeSession, &TlsMessageHandshake::ServerHello(_))       => Ok(TlsState::ResumeSession),
        // TLS 1.3 Draft 18 1-RTT
        // Re-use the ClientChangeCipherSpec state to indicate the next message will be encrypted
        (TlsState::ClientHello,      &TlsMessageHandshake::ServerHelloV13Draft18(_))    => Ok(TlsState::ClientChangeCipherSpec),
        // Hello requests must be accepted at any time (except start), but ignored [RFC5246] 7.4.1.1
        (TlsState::None,             &TlsMessageHandshake::HelloRequest)         => Err(StateChangeError::InvalidTransition),
        (s,                          &TlsMessageHandshake::HelloRequest)         => Ok(s),
        // All other transitions are considered invalid
        _ => Err(StateChangeError::InvalidTransition),
    }
}

/// Update the TLS state machine, doing one transition
///
/// Given the previous state and the parsed message, return the new state or a state machine error.
///
/// This state machine only implements the TLS handshake.
///
/// Some transitions only check the new message type, while some others must match the content
/// (for example, to check if the client asked to resume a session).
///
/// If the previous state is `Invalid`, the state machine will not return an error, but keep the
/// same `Invalid` state. This is used to raise error only once if the state machine keeps being
/// updated by new messages.
pub fn tls_state_transition(state: TlsState, msg: &TlsMessage) -> Result<TlsState,StateChangeError> {
    match (state,msg) {
        (TlsState::Invalid,_) => Ok(TlsState::Invalid),
        (_,&TlsMessage::Handshake(ref m)) => tls_state_transition_handshake(state,m),
        // Server certificate
        (TlsState::ClientKeyExchange,     &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        (TlsState::ClientChangeCipherSpec,&TlsMessage::ChangeCipherSpec) => Ok(TlsState::SessionEncrypted),
        // Server certificate, client certificate requested
        (TlsState::CRClientKeyExchange,   &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        (TlsState::CRCertVerify,          &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // No server certificate
        (TlsState::NoCertCKE,             &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // PSK
        (TlsState::PskCKE,                &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // Resume session
        (TlsState::ResumeSession,         &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        (_,_) => Err(StateChangeError::InvalidTransition),
    }
}
<commit_msg>Improve state machine, handle resumption failure, and non-fatal alerts<commit_after>use tls::*;
use tls_alert::TlsAlertSeverity;

/// Error types for the state machine
pub enum StateChangeError {
    InvalidTransition,
    ParseError,
}

/// TLS machine possible states
#[derive(Copy,Clone,Debug,PartialEq)]
pub enum TlsState {
    None,
    ClientHello,
    AskResumeSession,
    ResumeSession,
    ServerHello,
    Certificate,
    CertificateSt,
    ServerKeyExchange,
    ServerHelloDone,
    ClientKeyExchange,
    ClientChangeCipherSpec,

    CRCertRequest,
    CRHelloDone,
    CRCert,
    CRClientKeyExchange,
    CRCertVerify,

    NoCertSKE,
    NoCertHelloDone,
    NoCertCKE,

    PskHelloDone,
    PskCKE,

    SessionEncrypted,

    Alert,

    Invalid,
}

fn tls_state_transition_handshake(state: TlsState, msg: &TlsMessageHandshake) -> Result<TlsState,StateChangeError> {
    match (state,msg) {
        (TlsState::None,             &TlsMessageHandshake::ClientHello(ref msg)) => {
            match msg.session_id {
                Some(_) => Ok(TlsState::AskResumeSession),
                _       => Ok(TlsState::ClientHello)
            }
        },
        // Server certificate
        (TlsState::ClientHello,      &TlsMessageHandshake::ServerHello(_))       => Ok(TlsState::ServerHello),
        (TlsState::ServerHello,      &TlsMessageHandshake::Certificate(_))       => Ok(TlsState::Certificate),
        // Server certificate, no client certificate requested
        (TlsState::Certificate,      &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::ServerKeyExchange),
        (TlsState::Certificate,      &TlsMessageHandshake::CertificateStatus(_)) => Ok(TlsState::CertificateSt),
        (TlsState::CertificateSt,    &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::ServerKeyExchange),
        (TlsState::ServerKeyExchange,&TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::ServerHelloDone),
        (TlsState::ServerHelloDone  ,&TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::ClientKeyExchange),
        // Server certificate, client certificate requested
        (TlsState::Certificate,      &TlsMessageHandshake::CertificateRequest(_))=> Ok(TlsState::CRCertRequest),
        (TlsState::ServerKeyExchange,&TlsMessageHandshake::CertificateRequest(_))=> Ok(TlsState::CRCertRequest),
        (TlsState::CRCertRequest,    &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::CRHelloDone),
        (TlsState::CRHelloDone,      &TlsMessageHandshake::Certificate(_))       => Ok(TlsState::CRCert),
        (TlsState::CRCert,           &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::CRClientKeyExchange),
        (TlsState::CRClientKeyExchange, &TlsMessageHandshake::CertificateVerify(_)) => Ok(TlsState::CRCertVerify),
        // Server has no certificate (but accepts anonymous)
        (TlsState::ServerHello,      &TlsMessageHandshake::ServerKeyExchange(_)) => Ok(TlsState::NoCertSKE),
        (TlsState::NoCertSKE,        &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::NoCertHelloDone),
        (TlsState::NoCertHelloDone,  &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::NoCertCKE),
        // PSK
        (TlsState::Certificate,      &TlsMessageHandshake::ServerDone(_))        => Ok(TlsState::PskHelloDone),
        (TlsState::PskHelloDone,     &TlsMessageHandshake::ClientKeyExchange(_)) => Ok(TlsState::PskCKE),
        // Resuming session
        (TlsState::AskResumeSession, &TlsMessageHandshake::ServerHello(_))       => Ok(TlsState::ResumeSession),
        // Resume session failed
        (TlsState::ResumeSession,    &TlsMessageHandshake::Certificate(_))       => Ok(TlsState::Certificate),
        // TLS 1.3 Draft 18 1-RTT
        // Re-use the ClientChangeCipherSpec state to indicate the next message will be encrypted
        (TlsState::ClientHello,      &TlsMessageHandshake::ServerHelloV13Draft18(_))    => Ok(TlsState::ClientChangeCipherSpec),
        // Hello requests must be accepted at any time (except start), but ignored [RFC5246] 7.4.1.1
        (TlsState::None,             &TlsMessageHandshake::HelloRequest)         => Err(StateChangeError::InvalidTransition),
        (s,                          &TlsMessageHandshake::HelloRequest)         => Ok(s),
        // All other transitions are considered invalid
        _ => Err(StateChangeError::InvalidTransition),
    }
}

/// Update the TLS state machine, doing one transition
///
/// Given the previous state and the parsed message, return the new state or a state machine error.
///
/// This state machine only implements the TLS handshake.
///
/// Some transitions only check the new message type, while some others must match the content
/// (for example, to check if the client asked to resume a session).
///
/// If the previous state is `Invalid`, the state machine will not return an error, but keep the
/// same `Invalid` state. This is used to raise error only once if the state machine keeps being
/// updated by new messages.
pub fn tls_state_transition(state: TlsState, msg: &TlsMessage) -> Result<TlsState,StateChangeError> {
    match (state,msg) {
        (TlsState::Invalid,_) => Ok(TlsState::Invalid),
        (_,&TlsMessage::Handshake(ref m)) => tls_state_transition_handshake(state,m),
        // Server certificate
        (TlsState::ClientKeyExchange,     &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        (TlsState::ClientChangeCipherSpec,&TlsMessage::ChangeCipherSpec) => Ok(TlsState::SessionEncrypted),
        // Server certificate, client certificate requested
        (TlsState::CRClientKeyExchange,   &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        (TlsState::CRCertVerify,          &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // No server certificate
        (TlsState::NoCertCKE,             &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // PSK
        (TlsState::PskCKE,                &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // Resume session
        (TlsState::ResumeSession,         &TlsMessage::ChangeCipherSpec) => Ok(TlsState::ClientChangeCipherSpec),
        // non-fatal alerts
        (s,                               &TlsMessage::Alert(ref a)) => {
            if a.severity == TlsAlertSeverity::Warning { Ok(s) } else { Err(StateChangeError::InvalidTransition) }
        },
        (_,_) => Err(StateChangeError::InvalidTransition),
    }
}
<|endoftext|>


<commit_before>use phi::{Phi,View,ViewAction};
use sdl2::pixels::Color;
use sdl2::rect::Rect as SdlRect;

const PLAYER_SPEED: f64 = 180.0;

struct Ship {
    rect: Rectangle
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rectangle {
    pub x: f64,
    pub y: f64,
    pub w: f64,
    pub h: f64,
}

impl Rectangle {
    pub fn toSdl( self ) -> Option<SdlRect> {
        assert!(self.w >= 0.0 && self.h >= 0.0 );
        SdlRect::new( self.x as i32, self.y as i32, self.w as u32, self.h as u32 ).unwrap()
    }
}

pub struct ShipView {
    player: Ship
}

impl ShipView {
    pub fn new(phi: &mut Phi) -> Self {
        ShipView {
            player: Ship {
                rect: {
                    x: 64.0,
                    y: 64.0,
                    w: 32.0,
                    h: 32.0
                }
            }
        }
    }
}

impl View for ShipView {
    fn render(&mut self, context: &mut Phi, elapsed:f64) -> ViewAction{
        let renderer = &mut context.renderer;
        let events   = &context.events;
        let traveled = PLAYER_SPEED * elapsed

        if events.now.quit || events.now.key_escape == Some(true){
            return ViewAction::Quit
        }

        // view logic

        renderer.set_draw_color(Color::RGB(0,0,0));
        renderer.clear();

        // rendering logic
        renderer.set_draw_color(Color::RGB(200, 200, 50));
        renderer.fill_rect( self.player.rect.to_sdl().unwrap());

        ViewAction::None
    }
}
<commit_msg>add moveemnt to direction keys<commit_after>use phi::{Phi,View,ViewAction};
use sdl2::pixels::Color;
use sdl2::rect::Rect as SdlRect;

const PLAYER_SPEED: f64 = 180.0;

struct Ship {
    rect: Rectangle
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rectangle {
    pub x: f64,
    pub y: f64,
    pub w: f64,
    pub h: f64,
}

impl Rectangle {
    pub fn to_sdl( self ) -> Option<SdlRect> {
        assert!(self.w >= 0.0 && self.h >= 0.0 );
        SdlRect::new( self.x as i32, self.y as i32, self.w as u32, self.h as u32 ).unwrap()
    }
}

pub struct ShipView {
    player: Ship
}

impl ShipView {
    pub fn new(phi: &mut Phi) -> Self {
        ShipView {
            player: Ship {
                rect: Rectangle {
                    x: 64.0,
                    y: 64.0,
                    w: 32.0,
                    h: 32.0
                }
            }
        }
    }
}

impl View for ShipView {
    fn render(&mut self, context: &mut Phi, elapsed:f64) -> ViewAction{
        let renderer = &mut context.renderer;
        let events   = &context.events;
        let traveled = PLAYER_SPEED * elapsed;

        if events.now.quit || events.now.key_escape == Some(true){
            return ViewAction::Quit
        }

        let diagonal =
            ( context.events.key_up ^ context.events.key_down) &&
            ( context.events.key_left ^ context.events.key_right);

        let moved =
            if diagonal { 1.0 / 2.0f64.sqrt() }
            else { 1.0 } * PLAYER_SPEED * elapsed;

        let dy = match ( context.events.key_up, context.events.key_down ) {
            ( true, true ) | ( false, false ) => 0.0,
            ( true, false ) => -moved,
            ( false, true ) => moved,
        };

        let dx = match ( context.events.key_left, context.events.key_right ) {
            ( true, true ) | ( false, false ) => 0.0,
            ( true, false ) => -moved,
            ( false, true ) => moved,
        };

        self.player.rect.x += dx;
        self.player.rect.y += dy;

        // view logic

        renderer.set_draw_color(Color::RGB(0,0,0));
        renderer.clear();

        // rendering logic
        renderer.set_draw_color(Color::RGB(200, 200, 50));
        renderer.fill_rect( self.player.rect.to_sdl().unwrap());

        ViewAction::None
    }
}
<|endoftext|>


<commit_before>// id             number            A unique numeric ID that can be used to identify and reference an action.
// status         string            The current status of the action. This can be "in-progress", "completed", or "errored".
// type           string            This is the type of action that the object represents. For example, this could be "transfer" to represent the state of an image transfer action.
// started_at     string            A time value given in ISO8601 combined date and time format that represents when the action was initiated.
// completed_at   string            A time value given in ISO8601 combined date and time format that represents when the action was completed.
// resource_id    number            A unique identifier for the resource that the action is associated with.
// resource_type  string            The type of resource that the action is associated with.
// region         nullable string   (deprecated) A slug representing the region where the action occurred.
// region_slug    nullable string   A slug representing the region where the action occurred.

use std::fmt;
use std::borrow::Cow;

use response::region::Region;
use response::NamedResponse;

#[derive(Deserialize, Debug)]
pub struct Action {
    id: f64,
    status: String,
    #[serde(rename="type")]
    action_type: String,
    started_at: String,
    completed_at: String,
    resource_id: f64,
    resource_type: String,
    region: Region,
    region_slug: Option<String>
}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
       write!(f, "ID: {}\n\
                  Status: {}\n\
                  Type: {}\n\
                  Started At: {}\n\
                  Completed At: {}\n\
                  Resource ID: {}\n\
                  Resource Type: {}\n\
                  Region Slug: {}\n\
                     {}",
                self.id,
                self.status,
                self.action_type,
                self.started_at,
                self.completed_at,
                self.resource_id,
                self.resource_type,
                if self.region_slug.is_some() { self.region_slug.clone().unwrap() } else { "None".to_owned() },
                self.region.to_string().replace("\n", "\n\t"))
    }
}

pub type Actions = Vec<Action>;

impl NamedResponse for Action {
    fn name<'a>() -> Cow<'a, str> {
        "action".into()
    }
}
<commit_msg>imp: fixes indentation of action responses<commit_after>// id             number            A unique numeric ID that can be used to identify and reference an action.
// status         string            The current status of the action. This can be "in-progress", "completed", or "errored".
// type           string            This is the type of action that the object represents. For example, this could be "transfer" to represent the state of an image transfer action.
// started_at     string            A time value given in ISO8601 combined date and time format that represents when the action was initiated.
// completed_at   string            A time value given in ISO8601 combined date and time format that represents when the action was completed.
// resource_id    number            A unique identifier for the resource that the action is associated with.
// resource_type  string            The type of resource that the action is associated with.
// region         nullable string   (deprecated) A slug representing the region where the action occurred.
// region_slug    nullable string   A slug representing the region where the action occurred.

use std::fmt;
use std::borrow::Cow;

use response::region::Region;
use response::NamedResponse;

#[derive(Deserialize, Debug)]
pub struct Action {
    id: f64,
    status: String,
    #[serde(rename="type")]
    action_type: String,
    started_at: String,
    completed_at: String,
    resource_id: f64,
    resource_type: String,
    region: Region,
    region_slug: Option<String>
}

impl fmt::Display for Action {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
       write!(f, "ID: {}\n\
                  Status: {}\n\
                  Type: {}\n\
                  Started At: {}\n\
                  Completed At: {}\n\
                  Resource ID: {}\n\
                  Resource Type: {}\n\
                  Region Slug: {}\n\
                  Region:\n\t{}",
                self.id,
                self.status,
                self.action_type,
                self.started_at,
                self.completed_at,
                self.resource_id,
                self.resource_type,
                if self.region_slug.is_some() { self.region_slug.clone().unwrap() } else { "None".to_owned() },
                self.region.to_string().replace("\n", "\n\t"))
    }
}

pub type Actions = Vec<Action>;

impl NamedResponse for Action {
    fn name<'a>() -> Cow<'a, str> {
        "action".into()
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types,dead_code)]

extern crate libc;
extern crate time;

use std::io;

use time::Duration;

use self::libc::{c_int,c_short};

#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

#[cfg(not(target_os = "linux"))]
type nfds_t = libc::c_uint;

#[derive(Debug)]
#[repr(C)]
struct PollFd {
  fd: c_int,
  events: c_short,
  revents: c_short
}

const POLLIN:   c_short = 0x0001;
const POLLPRI:  c_short = 0x0002;
const POLLOUT:  c_short = 0x0004;

const POLLERR:  c_short = 0x0008;
const POLLHUP:  c_short = 0x0010;
const POLLNVAL: c_short = 0x0020;

#[repr(C)]
struct sigset_t;

extern "C" {
  fn poll(fds: *mut PollFd, nfds: nfds_t, timeout: c_int) -> c_int;

  #[cfg(target_os = "linux")]
  fn ppoll(fds: *mut PollFd, nfds: nfds_t, timeout_ts: *mut self::libc::timespec, sigmask: *const sigset_t) -> c_int;
}

pub fn wait_read_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
  wait_fd(fd, POLLIN, timeout)
}

pub fn wait_write_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
  wait_fd(fd, POLLOUT, timeout)
}

fn wait_fd(fd: c_int, events: c_short, timeout: Duration) -> io::Result<()> {
  let mut fds = vec!(PollFd { fd: fd, events: events, revents: 0 });

  let wait = do_poll(&mut fds, timeout);

  if wait < 0 {
    return Err(io::Error::last_os_error());
  }

  if wait == 0 {
    return Err(io::Error::new(io::ErrorKind::TimedOut, "operation timed out"));
  }

  if fds[0].revents & events != 0 {
    return Ok(());
  }

  if fds[0].revents & POLLHUP != 0 {
    return Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"));
  }

  if fds[0].revents & POLLNVAL != 0 {
    return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid input"))
  }

  Err(io::Error::new(io::ErrorKind::Other, "unknown I/O error"))
}

#[cfg(target_os = "linux")]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
  use std::ptr;

  let seconds     = timeout.num_seconds();
  let nanoseconds = ((timeout - Duration::seconds(seconds)) * 1_000_000_000).num_seconds();

  let mut timeout_ts = self::libc::timespec {
    tv_sec: seconds as libc::time_t,
    tv_nsec: nanoseconds as libc::c_long
  };

  unsafe {
    ppoll((&mut fds[..]).as_mut_ptr(),
          fds.len() as nfds_t,
          &mut timeout_ts,
          ptr::null())
  }
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
  unsafe {
    poll((&mut fds[..]).as_mut_ptr(),
         fds.len() as nfds_t,
         timeout.num_milliseconds() as c_int)
  }
}
<commit_msg>style updates<commit_after>#![allow(non_camel_case_types,dead_code)]

extern crate libc;
extern crate time;

use std::io;

use time::Duration;

use self::libc::{c_int,c_short};

#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

#[cfg(not(target_os = "linux"))]
type nfds_t = libc::c_uint;

#[derive(Debug)]
#[repr(C)]
struct PollFd {
    fd: c_int,
    events: c_short,
    revents: c_short
}

const POLLIN:   c_short = 0x0001;
const POLLPRI:  c_short = 0x0002;
const POLLOUT:  c_short = 0x0004;

const POLLERR:  c_short = 0x0008;
const POLLHUP:  c_short = 0x0010;
const POLLNVAL: c_short = 0x0020;

pub fn wait_read_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLIN, timeout)
}

pub fn wait_write_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLOUT, timeout)
}

fn wait_fd(fd: c_int, events: c_short, timeout: Duration) -> io::Result<()> {
    let mut fds = vec!(PollFd { fd: fd, events: events, revents: 0 });

    let wait = do_poll(&mut fds, timeout);

    if wait < 0 {
        return Err(io::Error::last_os_error());
    }

    if wait == 0 {
        return Err(io::Error::new(io::ErrorKind::TimedOut, "operation timed out"));
    }

    if fds[0].revents & events != 0 {
        return Ok(());
    }

    if fds[0].revents & POLLHUP != 0 {
        return Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"));
    }

    if fds[0].revents & POLLNVAL != 0 {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid input"))
    }

    Err(io::Error::new(io::ErrorKind::Other, "unknown I/O error"))
}

#[cfg(target_os = "linux")]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    use std::ptr;

    #[repr(C)]
    struct sigset_t;

    extern "C" {
        fn ppoll(fds: *mut PollFd, nfds: nfds_t, timeout_ts: *mut self::libc::timespec, sigmask: *const sigset_t) -> c_int;
    }

    let seconds     = timeout.num_seconds();
    let nanoseconds = ((timeout - Duration::seconds(seconds)) * 1_000_000_000).num_seconds();

    let mut timeout_ts = self::libc::timespec {
        tv_sec: seconds as libc::time_t,
        tv_nsec: nanoseconds as libc::c_long
    };

    unsafe {
        ppoll((&mut fds[..]).as_mut_ptr(),
              fds.len() as nfds_t,
              &mut timeout_ts,
              ptr::null())
    }
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    extern "C" {
        fn poll(fds: *mut PollFd, nfds: nfds_t, timeout: c_int) -> c_int;
    }

    unsafe {
        poll((&mut fds[..]).as_mut_ptr(),
             fds.len() as nfds_t,
             timeout.num_milliseconds() as c_int)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::hash::Hash;
use std::result::Result;
use std::fmt;
use std::rc::Rc;

pub trait Id: Clone + Eq + Ord + Sized + Hash + VariableDisplay {}

impl<T> Id for T where T: Clone + Eq + Ord + Sized + Hash + VariableDisplay {}

#[derive(Clone, Default, Eq)]
#[repr(C)]
pub struct Monomial<I> where I: Id {
    pub coefficient : i64,
    pub powers : Vec<(Composite<I>, u8)>
}

#[derive(Clone, Default, Eq)]
#[repr(C)]
pub struct Polynomial<I> where I: Id {
    pub monomials: Vec<Monomial<I>>
}

#[derive(Clone, PartialEq, Eq)]
#[repr(C)]
pub enum Composite<I> where I: Id {
    Variable(I),
    Floor(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Ceil(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Min(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Max(Rc<Polynomial<I>>, Rc<Polynomial<I>>)
}

/// A common trait for expressions which can be evaluated.
///
/// Evaluation is done via a mapping between a variables representation
/// (e.g. `I` for `Composite::Variable(I)`)
/// and the numeric values to be assigned to them.
///
/// If there are expressions which require a variable which has not been assigned a value
/// an `Err` with the first such variable is returned.
///
/// # Examples
/// ```
/// # use symints::*;
/// # use std::collections::HashMap;
/// let a = primitive(0);
/// let b = primitive(1);
/// let a_square_plus_b_plus_1 = &(&a * &a) + &(&b + 1);
/// let mut values: HashMap<u16, i64> = HashMap::new();
/// values.insert(0, 20);
/// assert!(a_square_plus_b_plus_1.evaluate(&values) == Err(1));
/// values.insert(1, 3);
/// assert!(a_square_plus_b_plus_1.evaluate(&values) == Ok(404));
/// ```
pub trait Evaluable<I> where I: Id {
    fn evaluate(&self, values: &HashMap<I, i64>) -> Result<i64, I>;
}

/// A common trait for expressions which can be constants.
///
/// # Examples
/// ```
/// # use symints::*;
/// let a = primitive(0);
/// let zero = &a - &a;
/// assert!(!a.is_constant());
/// assert!(zero.is_constant());
/// ```
pub trait IsConstant {
    fn is_constant(&self) -> bool;
}

/// A common trait for expressions which can perform checked division.
///
/// Computes `self / other`, returning `None` if `other == 0` or
/// if the symbolic expression in `self` is not divisible by `other`.
///
/// # Examples
/// ```
/// # use symints::*;
/// let a = primitive(0);
/// let b = primitive(1);
/// let a_plus_b = &a + &b;
/// let a_plus_b_square = &a_plus_b * &a_plus_b;
/// assert!(a_plus_b_square.checked_div(&a_plus_b) == Some(a_plus_b));
/// assert!(a_plus_b_square.checked_div(&a).is_none());
/// ```
pub trait CheckedDiv<RHS = Self> {
    type Output;
    fn checked_div(&self, other: RHS) -> Option<Self::Output>;
}

pub trait VariableDisplay {
    fn var_fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>;
}

/// Returns a symbolic integer expression representing
/// the primitive variable `Composite::Variable(id)`.
pub fn primitive<I>(id: I) -> Polynomial<I> where I: Id {
    Polynomial{
        monomials: vec![Monomial{
            coefficient: 1,
            powers: vec![(Composite::Variable(id), 1)]
        }]
    }
}

/// Computes a symbolic `max` between two symbolic integer expressions.
pub fn max<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap();
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap();
        Polynomial::from( if v1 > v2 {v1} else {v2})
    } else {
        Polynomial {
            monomials: vec![Monomial {
                coefficient: 1,
                powers: vec![(Composite::Max(Rc::new(left.clone()),
                                             Rc::new(right.clone())), 1)]
            }]
        }
    }
}

/// Computes a symbolic `min` between two symbolic integer expressions.
pub fn min<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap();
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap();
        Polynomial::from( if v1 < v2 {v1} else {v2})
    } else {
        Polynomial {
            monomials: vec![Monomial {
                coefficient: 1,
                powers: vec![(Composite::Min(Rc::new(left.clone()),
                                             Rc::new(right.clone())), 1)]
            }]
        }
    }
}

/// Computes a symbolic `ceil` between two symbolic integer expressions.
pub fn ceil<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap() as f64;
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap() as f64;
        Polynomial::from( (v1 / v2).ceil() as i64)
    } else {
        match left.checked_div(right) {
            Some(polynomial) => polynomial,
            None => Polynomial {
                monomials: vec![Monomial {
                    coefficient: 1,
                    powers: vec![(Composite::Ceil(Rc::new(left.clone()),
                                                  Rc::new(right.clone())), 1)]
                }]
            }
        }
    }
}

/// Computes a symbolic `floor` between two symbolic integer expressions.
pub fn floor<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap() as f64;
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap() as f64;
        Polynomial::from( (v1 / v2).floor() as i64)
    } else {
        match left.checked_div(right) {
            Some(polynomial) => polynomial,
            None => Polynomial {
                monomials: vec![Monomial {
                    coefficient: 1,
                    powers: vec![(Composite::Floor(Rc::new(left.clone()),
                                                   Rc::new(right.clone())), 1)]
                }]
            }
        }
    }
}<commit_msg>Fixed trait bounds for Id.<commit_after>use std::collections::HashMap;
use std::hash::Hash;
use std::result::Result;
use std::fmt;
use std::rc::Rc;

pub trait Id: Clone + Ord + Hash + VariableDisplay {}

impl<T> Id for T where T: Clone + Ord + Hash + VariableDisplay {}

#[derive(Clone, Default, Eq)]
#[repr(C)]
pub struct Monomial<I> where I: Id {
    pub coefficient : i64,
    pub powers : Vec<(Composite<I>, u8)>
}

#[derive(Clone, Default, Eq)]
#[repr(C)]
pub struct Polynomial<I> where I: Id {
    pub monomials: Vec<Monomial<I>>
}

#[derive(Clone, PartialEq, Eq)]
#[repr(C)]
pub enum Composite<I> where I: Id {
    Variable(I),
    Floor(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Ceil(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Min(Rc<Polynomial<I>>, Rc<Polynomial<I>>),
    Max(Rc<Polynomial<I>>, Rc<Polynomial<I>>)
}

/// A common trait for expressions which can be evaluated.
///
/// Evaluation is done via a mapping between a variables representation
/// (e.g. `I` for `Composite::Variable(I)`)
/// and the numeric values to be assigned to them.
///
/// If there are expressions which require a variable which has not been assigned a value
/// an `Err` with the first such variable is returned.
///
/// # Examples
/// ```
/// # use symints::*;
/// # use std::collections::HashMap;
/// let a = primitive(0);
/// let b = primitive(1);
/// let a_square_plus_b_plus_1 = &(&a * &a) + &(&b + 1);
/// let mut values: HashMap<u16, i64> = HashMap::new();
/// values.insert(0, 20);
/// assert!(a_square_plus_b_plus_1.evaluate(&values) == Err(1));
/// values.insert(1, 3);
/// assert!(a_square_plus_b_plus_1.evaluate(&values) == Ok(404));
/// ```
pub trait Evaluable<I> where I: Id {
    fn evaluate(&self, values: &HashMap<I, i64>) -> Result<i64, I>;
}

/// A common trait for expressions which can be constants.
///
/// # Examples
/// ```
/// # use symints::*;
/// let a = primitive(0);
/// let zero = &a - &a;
/// assert!(!a.is_constant());
/// assert!(zero.is_constant());
/// ```
pub trait IsConstant {
    fn is_constant(&self) -> bool;
}

/// A common trait for expressions which can perform checked division.
///
/// Computes `self / other`, returning `None` if `other == 0` or
/// if the symbolic expression in `self` is not divisible by `other`.
///
/// # Examples
/// ```
/// # use symints::*;
/// let a = primitive(0);
/// let b = primitive(1);
/// let a_plus_b = &a + &b;
/// let a_plus_b_square = &a_plus_b * &a_plus_b;
/// assert!(a_plus_b_square.checked_div(&a_plus_b) == Some(a_plus_b));
/// assert!(a_plus_b_square.checked_div(&a).is_none());
/// ```
pub trait CheckedDiv<RHS = Self> {
    type Output;
    fn checked_div(&self, other: RHS) -> Option<Self::Output>;
}

pub trait VariableDisplay {
    fn var_fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>;
}

/// Returns a symbolic integer expression representing
/// the primitive variable `Composite::Variable(id)`.
pub fn primitive<I>(id: I) -> Polynomial<I> where I: Id {
    Polynomial{
        monomials: vec![Monomial{
            coefficient: 1,
            powers: vec![(Composite::Variable(id), 1)]
        }]
    }
}

/// Computes a symbolic `max` between two symbolic integer expressions.
pub fn max<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap();
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap();
        Polynomial::from( if v1 > v2 {v1} else {v2})
    } else {
        Polynomial {
            monomials: vec![Monomial {
                coefficient: 1,
                powers: vec![(Composite::Max(Rc::new(left.clone()),
                                             Rc::new(right.clone())), 1)]
            }]
        }
    }
}

/// Computes a symbolic `min` between two symbolic integer expressions.
pub fn min<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap();
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap();
        Polynomial::from( if v1 < v2 {v1} else {v2})
    } else {
        Polynomial {
            monomials: vec![Monomial {
                coefficient: 1,
                powers: vec![(Composite::Min(Rc::new(left.clone()),
                                             Rc::new(right.clone())), 1)]
            }]
        }
    }
}

/// Computes a symbolic `ceil` between two symbolic integer expressions.
pub fn ceil<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap() as f64;
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap() as f64;
        Polynomial::from( (v1 / v2).ceil() as i64)
    } else {
        match left.checked_div(right) {
            Some(polynomial) => polynomial,
            None => Polynomial {
                monomials: vec![Monomial {
                    coefficient: 1,
                    powers: vec![(Composite::Ceil(Rc::new(left.clone()),
                                                  Rc::new(right.clone())), 1)]
                }]
            }
        }
    }
}

/// Computes a symbolic `floor` between two symbolic integer expressions.
pub fn floor<I>(left: &Polynomial<I>, right: &Polynomial<I>) -> Polynomial<I> where I: Id {
    if left.is_constant() && right.is_constant() {
        let v1 = left.evaluate(&HashMap::default()).ok().unwrap() as f64;
        let v2 = right.evaluate(&HashMap::default()).ok().unwrap() as f64;
        Polynomial::from( (v1 / v2).floor() as i64)
    } else {
        match left.checked_div(right) {
            Some(polynomial) => polynomial,
            None => Polynomial {
                monomials: vec![Monomial {
                    coefficient: 1,
                    powers: vec![(Composite::Floor(Rc::new(left.clone()),
                                                   Rc::new(right.clone())), 1)]
                }]
            }
        }
    }
}<|endoftext|>