<commit_before>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use clap::{App, Arg};
use toml;
use rustc_serialize::Decodable;
use rr_result::{RrResult, err_message, rr_error_message};
use version_kind::VersionKind;
use utils::map_file;
use cargo_proj::CargoProj;

/// The configuration used to run `rusty-release`.
#[derive(Debug)]
pub struct Config {
    /// which part of the project version should be incremented
    pub version_kind: VersionKind,

    /// start directory for the search of the 'Cargo.toml'
    pub start_dir: PathBuf,

    /// publish to crates.io
    pub cargo_publish: bool,

    /// push to git remote repository
    pub git_push: bool,

    /// string template for the commit message
    commit_message: String,

    /// string template for the tag name
    tag_name: String,

    /// the editor command, used for opening of the changelog
    editor: String
}

/// Helper macro to apply the settings from ConfigFromFile to Config
macro_rules! config {
    ( $file_config:ident, [ $( $field_name:ident ),* ]) => {{
        let mut config = try!(Config::default());
        $(
            if let Some(f) = $file_config.$field_name {
                config.$field_name = f;
            }
        )*

        config
    }}
}

impl Config {
   pub fn from_file_and_command_args() -> RrResult<Config> {
       let matches = try!(App::new("rusty-release")
           .about("Make a release for a cargo project")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg_from_usage("<VERSION_KIND> 'Which version number gets increased (major, minor or patch)'")
           .arg(Arg::with_name("start-dir")
                .short("s")
                .long("start-dir")
                .value_names(&["DIR"])
                .help("Start directory for the search of the Cargo.toml (default: current working directory)")
                .takes_value(true))
           .arg_from_usage("-n --no-cargo-publish 'Do not publish to crates.io'")
           .arg_from_usage("-N --no-git-push 'Do not push to remote git repository'")
           .get_matches_safe());

       let start_dir = matches.value_of("start-dir")
           .map(PathBuf::from)
           .unwrap_or(try!(env::current_dir()));

       if ! start_dir.is_dir() {
           return err_message(format!("Invalid directory given to '--start-dir': '{}'!", start_dir.display()));
       }

       let mut config = try!(Config::from_file());
       config.version_kind = value_t_or_exit!(matches.value_of("VERSION_KIND"), VersionKind);
       config.start_dir = start_dir;

       if matches.is_present("no-cargo-publish") {
           config.cargo_publish = ! matches.is_present("no-cargo-publish");
       }

       if matches.is_present("no-git-push") {
           config.git_push = ! matches.is_present("no-git-push");
       }

       try!(config.check());
       Ok(config)
   }

   pub fn commit_message(&self, proj: &CargoProj) -> String {
       Template(&self.commit_message).render(proj)
   }

   pub fn tag_name(&self, proj: &CargoProj) -> String {
       Template(&self.tag_name).render(proj)
   }

   pub fn editor(&self) -> Command {
       let editor_and_args = self.editor.split(' ').collect::<Vec<&str>>();
       let mut cmd = Command::new(editor_and_args[0]);
       let args = editor_and_args.iter().skip(1);
       for arg in args {
           cmd.arg(arg);
       }

       cmd
   }

   fn from_file() -> RrResult<Config> {
       let curr_file_config = try!(ConfigFromFile::load_from_current_dir());
       let home_file_config = try!(ConfigFromFile::load_from_home_dir());

       let file_config = match (curr_file_config, home_file_config) {
           (Some(cfc), Some(hfc)) => cfc.combine(&hfc),
           (Some(cfc), None)      => cfc,
           (None     , Some(hfc)) => hfc,
           (None     , None)      => ConfigFromFile::default()
       };

       let config = config!(file_config, [
           cargo_publish,
           git_push,
           commit_message,
           tag_name,
           editor
       ]);

       Ok(config)
   }

   fn default() -> RrResult<Config> {
       Ok(Config {
           version_kind: VersionKind::Patch,
           start_dir: try!(env::current_dir()),
           cargo_publish: true,
           git_push: true,
           commit_message: "<PROJ_NAME> <NEW_VERSION>".to_string(),
           tag_name: "<PROJ_NAME>-<NEW_VERSION>".to_string(),
           editor: "gvim -o".to_string()
       })
   }

   fn check(&self) -> RrResult<()> {
       if self.commit_message.is_empty() {
           return err_message("Invalid, empty commit message!");
       }

       if self.tag_name.is_empty() {
           return err_message("Invalid empty tag name!");
       }

       if self.editor.is_empty() {
           return err_message("Invalid, empty editor command!");
       }

       Ok(())
   }
}

/// Represents a string template that contains placeholders that can be replaced.
/// Currently only the placeholders '<PROJ_NAME>' - representing the name of the
/// cargo project and '<NEW_VERSION>' - representing the version of the release -
/// are supported.
#[derive(Debug)]
struct Template<'a>(&'a str);

impl<'a> Template<'a> {
    fn render(&self, proj: &CargoProj) -> String {
        self.0.replace("<PROJ_NAME>", &proj.name().to_string())
            .replace("<NEW_VERSION>", &proj.version().to_string())
    }
}

/// Represents the data from a `.rusty-release.toml` configuration file.
#[derive(RustcDecodable, Debug, Default)]
struct ConfigFromFile {
    cargo_publish: Option<bool>,
    git_push: Option<bool>,
    commit_message: Option<String>,
    tag_name: Option<String>,
    editor: Option<String>
}

impl ConfigFromFile {
    fn load_from_current_dir() -> RrResult<Option<ConfigFromFile>> {
        let path = try!(env::current_dir()).join(config_file_name());
        if ! path.is_file() {
            return Ok(None);
        }

        Ok(Some(try!(ConfigFromFile::load_from_file(&path))))
    }

    fn load_from_home_dir() -> RrResult<Option<ConfigFromFile>> {
        if let Some(path) = env::home_dir().map(|d| d.join(config_file_name())) {
            if path.is_file() {
                return Ok(Some(try!(ConfigFromFile::load_from_file(&path))));
            }
        }

        Ok(None)
    }

    fn load_from_file(path: &Path) -> RrResult<ConfigFromFile> {
        map_file(path, |contents| {
            let mut parser = toml::Parser::new(&contents);
            let value = try!(parser.parse()
                .ok_or_else(|| rr_error_message(format!("Couldn't parse toml file '{}': {:?}",
                                                        path.display(), parser.errors))));

            let mut decoder = toml::Decoder::new(toml::Value::Table(value));
            Ok(try!(ConfigFromFile::decode(&mut decoder)))
        })
    }

    fn combine(&self, other: &ConfigFromFile) -> ConfigFromFile {
        ConfigFromFile {
            cargo_publish: self.cargo_publish.or(other.cargo_publish),
            git_push: self.git_push.or(other.git_push),
            commit_message: self.commit_message.as_ref().or(other.commit_message.as_ref()).cloned(),
            tag_name: self.tag_name.as_ref().or(other.tag_name.as_ref()).cloned(),
            editor: self.editor.as_ref().or(other.editor.as_ref()).cloned()
        }
    }
}

fn config_file_name() -> &'static str {
    ".rusty-release.toml"
}
<commit_msg>Support $EDITOR and $VISUAL as editor commands<commit_after>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use clap::{App, Arg};
use toml;
use rustc_serialize::Decodable;
use rr_result::{RrResult, err_message, rr_error_message};
use version_kind::VersionKind;
use utils::map_file;
use cargo_proj::CargoProj;

/// The configuration used to run `rusty-release`.
#[derive(Debug)]
pub struct Config {
    /// which part of the project version should be incremented
    pub version_kind: VersionKind,

    /// start directory for the search of the 'Cargo.toml'
    pub start_dir: PathBuf,

    /// publish to crates.io
    pub cargo_publish: bool,

    /// push to git remote repository
    pub git_push: bool,

    /// string template for the commit message
    commit_message: String,

    /// string template for the tag name
    tag_name: String,

    /// the editor command, used for opening of the changelog
    editor: String
}

/// Helper macro to apply the settings from ConfigFromFile to Config
macro_rules! config {
    ( $file_config:ident, [ $( $field_name:ident ),* ]) => {{
        let mut config = try!(Config::default());
        $(
            if let Some(f) = $file_config.$field_name {
                config.$field_name = f;
            }
        )*

        config
    }}
}

impl Config {
   pub fn from_file_and_command_args() -> RrResult<Config> {
       let matches = try!(App::new("rusty-release")
           .about("Make a release for a cargo project")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg_from_usage("<VERSION_KIND> 'Which version number gets increased (major, minor or patch)'")
           .arg(Arg::with_name("start-dir")
                .short("s")
                .long("start-dir")
                .value_names(&["DIR"])
                .help("Start directory for the search of the Cargo.toml (default: current working directory)")
                .takes_value(true))
           .arg_from_usage("-n --no-cargo-publish 'Do not publish to crates.io'")
           .arg_from_usage("-N --no-git-push 'Do not push to remote git repository'")
           .get_matches_safe());

       let start_dir = matches.value_of("start-dir")
           .map(PathBuf::from)
           .unwrap_or(try!(env::current_dir()));

       if ! start_dir.is_dir() {
           return err_message(format!("Invalid directory given to '--start-dir': '{}'!", start_dir.display()));
       }

       let mut config = try!(Config::from_file());
       config.version_kind = value_t_or_exit!(matches.value_of("VERSION_KIND"), VersionKind);
       config.start_dir = start_dir;

       if matches.is_present("no-cargo-publish") {
           config.cargo_publish = ! matches.is_present("no-cargo-publish");
       }

       if matches.is_present("no-git-push") {
           config.git_push = ! matches.is_present("no-git-push");
       }

       try!(config.check());
       Ok(config)
   }

   pub fn commit_message(&self, proj: &CargoProj) -> String {
       Template(&self.commit_message).render(proj)
   }

   pub fn tag_name(&self, proj: &CargoProj) -> String {
       Template(&self.tag_name).render(proj)
   }

   pub fn editor(&self) -> Command {
       let editor_and_args = self.editor.split(' ').collect::<Vec<&str>>();
       let mut cmd = Command::new(editor_and_args[0]);
       let args = editor_and_args.iter().skip(1);
       for arg in args {
           cmd.arg(arg);
       }

       cmd
   }

   fn from_file() -> RrResult<Config> {
       let curr_file_config = try!(ConfigFromFile::load_from_current_dir());
       let home_file_config = try!(ConfigFromFile::load_from_home_dir());

       let file_config = match (curr_file_config, home_file_config) {
           (Some(cfc), Some(hfc)) => cfc.combine(&hfc),
           (Some(cfc), None)      => cfc,
           (None     , Some(hfc)) => hfc,
           (None     , None)      => ConfigFromFile::default()
       };

       let config = config!(file_config, [
           cargo_publish,
           git_push,
           commit_message,
           tag_name,
           editor
       ]);

       Ok(config)
   }

   fn default() -> RrResult<Config> {
       Ok(Config {
           version_kind: VersionKind::Patch,
           start_dir: try!(env::current_dir()),
           cargo_publish: true,
           git_push: true,
           commit_message: "<PROJ_NAME> <NEW_VERSION>".to_string(),
           tag_name: "<PROJ_NAME>-<NEW_VERSION>".to_string(),
           editor: {
               if let Ok(editor) = env::var("EDITOR") {
                   editor
               } else if let Ok(visual) = env::var("VISUAL") {
                   visual
               } else {
                   "gvim -o".to_string()
               }
           }
       })
   }

   fn check(&self) -> RrResult<()> {
       if self.commit_message.is_empty() {
           return err_message("Invalid, empty commit message!");
       }

       if self.tag_name.is_empty() {
           return err_message("Invalid empty tag name!");
       }

       if self.editor.is_empty() {
           return err_message("Invalid, empty editor command!");
       }

       Ok(())
   }
}

/// Represents a string template that contains placeholders that can be replaced.
/// Currently only the placeholders '<PROJ_NAME>' - representing the name of the
/// cargo project and '<NEW_VERSION>' - representing the version of the release -
/// are supported.
#[derive(Debug)]
struct Template<'a>(&'a str);

impl<'a> Template<'a> {
    fn render(&self, proj: &CargoProj) -> String {
        self.0.replace("<PROJ_NAME>", &proj.name().to_string())
            .replace("<NEW_VERSION>", &proj.version().to_string())
    }
}

/// Represents the data from a `.rusty-release.toml` configuration file.
#[derive(RustcDecodable, Debug, Default)]
struct ConfigFromFile {
    cargo_publish: Option<bool>,
    git_push: Option<bool>,
    commit_message: Option<String>,
    tag_name: Option<String>,
    editor: Option<String>
}

impl ConfigFromFile {
    fn load_from_current_dir() -> RrResult<Option<ConfigFromFile>> {
        let path = try!(env::current_dir()).join(config_file_name());
        if ! path.is_file() {
            return Ok(None);
        }

        Ok(Some(try!(ConfigFromFile::load_from_file(&path))))
    }

    fn load_from_home_dir() -> RrResult<Option<ConfigFromFile>> {
        if let Some(path) = env::home_dir().map(|d| d.join(config_file_name())) {
            if path.is_file() {
                return Ok(Some(try!(ConfigFromFile::load_from_file(&path))));
            }
        }

        Ok(None)
    }

    fn load_from_file(path: &Path) -> RrResult<ConfigFromFile> {
        map_file(path, |contents| {
            let mut parser = toml::Parser::new(&contents);
            let value = try!(parser.parse()
                .ok_or_else(|| rr_error_message(format!("Couldn't parse toml file '{}': {:?}",
                                                        path.display(), parser.errors))));

            let mut decoder = toml::Decoder::new(toml::Value::Table(value));
            Ok(try!(ConfigFromFile::decode(&mut decoder)))
        })
    }

    fn combine(&self, other: &ConfigFromFile) -> ConfigFromFile {
        ConfigFromFile {
            cargo_publish: self.cargo_publish.or(other.cargo_publish),
            git_push: self.git_push.or(other.git_push),
            commit_message: self.commit_message.as_ref().or(other.commit_message.as_ref()).cloned(),
            tag_name: self.tag_name.as_ref().or(other.tag_name.as_ref()).cloned(),
            editor: self.editor.as_ref().or(other.editor.as_ref()).cloned()
        }
    }
}

fn config_file_name() -> &'static str {
    ".rusty-release.toml"
}
<|endoftext|>


<commit_before>use dirs;
use serde::Deserialize;
use std::fs;
use std::path::Path;

pub struct Config {
    pub cipher: String,
    pub key_length: usize,
    pub password_generator: String,
}

impl Config {
    fn merge(self, other: ConfigFromFile) -> Config {
        Config {
            cipher: other.cipher.unwrap_or(self.cipher),
            key_length: other.key_length.unwrap_or(self.key_length),
            password_generator: other.password_generator.unwrap_or(self.password_generator),
        }
    }
}

pub fn default_config() -> Config {
    Config {
        cipher: "AES-128".to_string(),
        key_length: 64,
        password_generator: "pwgen -nc 12 10".to_string(),
    }
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct ConfigFromFile {
    pub cipher: Option<String>,
    pub key_length: Option<usize>,
    pub password_generator: Option<String>,
}

const EMPTY_CONFIG: ConfigFromFile = ConfigFromFile {
    cipher: None,
    key_length: None,
    password_generator: None,
};

fn from_file(path: &Path) -> ConfigFromFile {
    match fs::read(path) {
        Ok(contents) => toml::from_slice(&contents).unwrap_or(EMPTY_CONFIG),
        Err(_) => EMPTY_CONFIG,
    }
}

pub fn load(repo_path: &Path) -> Config {
    let home_config = dirs::home_dir()
        .map(|h| from_file(&h.join(".sala.toml")))
        .unwrap_or(EMPTY_CONFIG);
    let xdg_config = dirs::config_dir()
        .map(|h| from_file(&h.join("sala.toml")))
        .unwrap_or(EMPTY_CONFIG);
    let repo_config = from_file(&repo_path.join(".sala/config"));

    default_config()
        .merge(home_config)
        .merge(xdg_config)
        .merge(repo_config)
}
<commit_msg>Misc (Rust)<commit_after>use dirs;
use serde::Deserialize;
use std::fs;
use std::path::Path;

pub struct Config {
    pub cipher: String,
    pub key_length: usize,
    pub password_generator: String,
}

impl Config {
    fn merge(self, other: ConfigFromFile) -> Config {
        Config {
            cipher: other.cipher.unwrap_or(self.cipher),
            key_length: other.key_length.unwrap_or(self.key_length),
            password_generator: other.password_generator.unwrap_or(self.password_generator),
        }
    }
}

pub fn default_config() -> Config {
    Config {
        cipher: "AES-128".into(),
        key_length: 64,
        password_generator: "pwgen -nc 12 10".into(),
    }
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct ConfigFromFile {
    pub cipher: Option<String>,
    pub key_length: Option<usize>,
    pub password_generator: Option<String>,
}

const EMPTY_CONFIG: ConfigFromFile = ConfigFromFile {
    cipher: None,
    key_length: None,
    password_generator: None,
};

fn from_file(path: &Path) -> ConfigFromFile {
    match fs::read(path) {
        Ok(contents) => toml::from_slice(&contents).unwrap_or(EMPTY_CONFIG),
        Err(_) => EMPTY_CONFIG,
    }
}

pub fn load(repo_path: &Path) -> Config {
    let home_config = dirs::home_dir()
        .map(|h| from_file(&h.join(".sala.toml")))
        .unwrap_or(EMPTY_CONFIG);
    let xdg_config = dirs::config_dir()
        .map(|h| from_file(&h.join("sala.toml")))
        .unwrap_or(EMPTY_CONFIG);
    let repo_config = from_file(&repo_path.join(".sala/config"));

    default_config()
        .merge(home_config)
        .merge(xdg_config)
        .merge(repo_config)
}
<|endoftext|>


<commit_before>use clap::Parser;

mod command;
mod error;
mod ui;

use tracing::debug;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
use vut::project::BumpVersion;

#[derive(Debug, Parser)]
#[clap(name = "Vut", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"))]
struct Opt {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Debug, Parser)]
enum Command {
    #[clap(name = "init", about = "Initialize version file")]
    Init {
        #[clap(long = "example", help = "Create example configuration")]
        example: bool,

        #[clap(
            short = 'f',
            long = "force",
            help = "Proceed even if inside the scope of an existing Vut configuration"
        )]
        force: bool,

        #[clap(name = "version", help = "Specify initial version")]
        version: Option<String>,
    },

    #[clap(name = "get", about = "Get version")]
    Get {
        #[structopt(name = "format", help = "Output format (json)")]
        format: String,
    },

    #[clap(name = "set", about = "Set version")]
    Set {
        #[structopt(name = "version", help = "Version to set")]
        version: String,
    },

    #[clap(name = "bump", about = "Bump version")]
    Bump {
        #[structopt(help = "Version to bump (major|minor|prerelease|build)")]
        bump_version: BumpVersion,
    },

    #[clap(name = "generate", alias = "gen", about = "Generate template output")]
    Generate,
}

fn main() {
    let opt = Opt::parse();

    // Initialize logging
    initialize_logging();

    debug!("Debug logging enabled.");

    let cmd_result = match opt.command {
        Command::Bump { bump_version } => command::bump(bump_version),
        Command::Generate => command::generate(),
        Command::Get { format } => command::get(&format),
        Command::Init {
            example,
            force,
            version,
        } => command::init(example, force, version.as_deref()),
        Command::Set { version } => command::set(&version),
    };

    match cmd_result {
        Ok(_) => {}
        Err(err) => {
            // Print error description to stderr
            eprintln!("{}", err.description);

            // Return the exit code that corresponds to the error kind
            std::process::exit(err.kind.exit_code());
        }
    };
}

fn initialize_logging() {
    let subscriber = FmtSubscriber::builder()
        .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")))
        .with_writer(std::io::stderr)
        .finish();

    tracing::subscriber::set_global_default(subscriber).expect("Setting default tracing subscriber failed!");
}
<commit_msg>fix: Replace remaining structopt attributes with clap<commit_after>use clap::Parser;

mod command;
mod error;
mod ui;

use tracing::debug;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
use vut::project::BumpVersion;

#[derive(Debug, Parser)]
#[clap(name = "Vut", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"))]
struct Opt {
    #[clap(subcommand)]
    command: Command,
}

#[derive(Debug, Parser)]
enum Command {
    #[clap(name = "init", about = "Initialize version file")]
    Init {
        #[clap(long = "example", help = "Create example configuration")]
        example: bool,

        #[clap(
            short = 'f',
            long = "force",
            help = "Proceed even if inside the scope of an existing Vut configuration"
        )]
        force: bool,

        #[clap(name = "version", help = "Specify initial version")]
        version: Option<String>,
    },

    #[clap(name = "get", about = "Get version")]
    Get {
        #[clap(name = "format", help = "Output format (json)")]
        format: String,
    },

    #[clap(name = "set", about = "Set version")]
    Set {
        #[clap(name = "version", help = "Version to set")]
        version: String,
    },

    #[clap(name = "bump", about = "Bump version")]
    Bump {
        #[clap(help = "Version to bump (major|minor|prerelease|build)")]
        bump_version: BumpVersion,
    },

    #[clap(name = "generate", alias = "gen", about = "Generate template output")]
    Generate,
}

fn main() {
    let opt = Opt::parse();

    // Initialize logging
    initialize_logging();

    debug!("Debug logging enabled.");

    let cmd_result = match opt.command {
        Command::Bump { bump_version } => command::bump(bump_version),
        Command::Generate => command::generate(),
        Command::Get { format } => command::get(&format),
        Command::Init {
            example,
            force,
            version,
        } => command::init(example, force, version.as_deref()),
        Command::Set { version } => command::set(&version),
    };

    match cmd_result {
        Ok(_) => {}
        Err(err) => {
            // Print error description to stderr
            eprintln!("{}", err.description);

            // Return the exit code that corresponds to the error kind
            std::process::exit(err.kind.exit_code());
        }
    };
}

fn initialize_logging() {
    let subscriber = FmtSubscriber::builder()
        .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")))
        .with_writer(std::io::stderr)
        .finish();

    tracing::subscriber::set_global_default(subscriber).expect("Setting default tracing subscriber failed!");
}
<|endoftext|>


<commit_before>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::sync::{Arc};
use std::marker::{PhantomData, Unpin};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::cell::{UnsafeCell};
use std::mem;
use std::panic::{UnwindSafe};
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send+Unpin> {
    /// Queue used for scheduling runtime for this object
    queue:   Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:    *mut T,

    /// Mark this type as !RefUnwindSafe.
    _marker: PhantomData<UnsafeCell<T>>
}

unsafe impl<T: Send+Unpin> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send+Unpin> Sync for Desync<T> {}

impl<T: Send+Unpin+UnwindSafe> UnwindSafe for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*mut T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send+Unpin> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:   queue,
            data:    Box::into_raw(Box::new(data)),
            _marker: PhantomData
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(self.data);

        desync(&self.queue, move || {
            let data = data.0;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(self.data);

            sync(&self.queue, move || {
                let data = data.0;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(self.data);

            try_sync(&self.queue, move || {
                let data = data.0;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Deprecated name for `future_desync`
    ///
    #[inline]
    #[deprecated(since="0.7.0", note="please use either `future_desync` or `future_sync` to schedule futures")]
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        self.future_desync(job)
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future will be scheduled in the background, so it will make progress even if the current scheduler is
    /// blocked for any reason. Additionally, it's not necessary to await the returned future, which can be discarded
    /// if necessary. As soon as this function returns, the order of the operations in the future are fixed: that is,
    /// any other operation queued on the same `Desync` will be performed after the future completes.
    ///
    /// Call `detach()` on the returned future to fully put it in the background. If awaited the scheduler may try to
    /// 'steal' runtime on the current thread to complete the future, but it will also run the future in the background
    /// if the current thread is not available or a background thread is available faster. The future here has static
    /// lifetime so can be passed around in case something needs to monitor when it completes.
    ///
    /// This can also be used to run the future synchronously on the current thread, useful for cases where an async
    /// function needs to be called from a non-async context. Call `sync()` on the returned future to wait for its
    /// result synchronously.
    ///
    /// Note that this has a companion function, `future_sync()` which is more useful for cases where the future is
    /// awaited, as the 'inner' future is able to borrow values from the 'outer' future due to the shorter lifetime
    /// of the returned value.
    ///
    /// The normal `sync()` and `desync()` methods will schedule their operations in order around any `future_sync()`
    /// or `future_desync()` calls, so it's possible to easily mix async and traditional threaded code in one program.
    ///
    pub fn future_desync<'a, TFn, TFuture>(&self, job: TFn) -> SchedulerFuture<TFuture::Output>
    where
        TFn:                'static + Send + FnOnce(&'a mut T) -> TFuture,
        TFuture:            'a + Send + Future,
        TFuture::Output:    'static + Send,
    {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(self.data);

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    /// The future will be scheduled in the current execution context, so it will only make progress if the current
    /// scheduler is running. The task will be cancelled and will not complete execution if the future is dropped before
    /// completion, so it's usually necessary to await the result of this function for the task to behave correctly.
    ///
    /// This is most useful for 'foreground' futures: note that the future can safely borrow values from the outer context
    /// to use while it's running (in much the same way `sync()` can in synchronous contexts). The need to dispatch to
    /// the awaiting context can make this future slightly slower than `future_desync()`
    ///
    /// Desync has a feature that makes it possible to wait synchronously for futures to complete: slightly
    /// counter-intuitively this is found in the future returned by `future_desync()` function rather than this function,
    /// which is synchronous with the current async context instead of with the current thread.
    ///
    /// The normal `sync()` and `desync()` methods will schedule their operations in order around any `future_sync()`
    /// or `future_desync()` calls, so it's possible to easily mix async and traditional threaded code in one program.
    ///
    pub fn future_sync<'a, 'b, TFn, TFuture>(&'a self, job: TFn) -> impl 'a + Future<Output=Result<TFuture::Output, oneshot::Canceled>> + Send
    where
        TFn:                'a + Send + FnOnce(&'b mut T) -> TFuture,
        TFuture:            'b + Send + Future,
        TFuture::Output:    'a + Send,
        'b:                 'a,
    {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(self.data);

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res, Fut>(&self, after: Fut, job: TFn) -> impl 'static + Future<Output=Result<Res, oneshot::Canceled>> + Send
    where
        Res: 'static + Send,
        Fut: 'static + Future + Send,
        TFn: 'static + Send + FnOnce(&mut T, Fut::Output) -> Res
    {
        self.future_desync(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }
        })
    }
}

impl<T: Send+Unpin> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = DataRef::<T>(self.data);

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                let data = data.0;
                mem::drop(unsafe { Box::from_raw(data) });
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                let data = data.0;
                mem::drop(unsafe { Box::from_raw(data) });
            });
        }
    }
}
<commit_msg>Remove unneeded Unpin bound on Desync<commit_after>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::sync::{Arc};
use std::marker::{PhantomData};
use futures::channel::oneshot;
use futures::future::{Future, BoxFuture};

use std::cell::{UnsafeCell};
use std::mem;
use std::panic::{UnwindSafe};
use std::result::{Result};

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send> {
    /// Queue used for scheduling runtime for this object
    queue:   Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:    *mut T,

    /// Mark this type as !RefUnwindSafe.
    _marker: PhantomData<UnsafeCell<T>>
}

unsafe impl<T: Send> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send> Sync for Desync<T> {}

impl<T: Send+UnwindSafe> UnwindSafe for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*mut T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:   queue,
            data:    Box::into_raw(Box::new(data)),
            _marker: PhantomData
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    #[inline]
    #[deprecated(since="0.3.0", note="please use `desync` instead")]
    pub fn r#async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        self.desync(job)
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn desync<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        // As drop() is the last thing called, we know that this object will still exist at the point where the queue makes the asynchronous callback
        let data = DataRef::<T>(self.data);

        desync(&self.queue, move || {
            let data = data.0;
            job(unsafe { &mut *data });
        })
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            // Exclusivity is guaranteed because the queue executes only one task at a time
            let data = DataRef::<T>(self.data);

            sync(&self.queue, move || {
                let data = data.0;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Performs an operation synchronously on this item,
    ///
    pub fn try_sync<TFn, FnResult>(&self, job: TFn) -> Result<FnResult, TrySyncError>
    where TFn: Send+FnOnce(&mut T) -> FnResult, FnResult: Send {
        let result = {
            // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
            let data = DataRef::<T>(self.data);

            try_sync(&self.queue, move || {
                let data = data.0;
                job(unsafe { &mut *data })
            })
        };

        result
    }

    ///
    /// Deprecated name for `future_desync`
    ///
    #[inline]
    #[deprecated(since="0.7.0", note="please use either `future_desync` or `future_sync` to schedule futures")]
    pub fn future<TFn, TOutput>(&self, job: TFn) -> impl Future<Output=Result<TOutput, oneshot::Canceled>>+Send
    where   TFn:        'static+Send+for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, TOutput>,
            TOutput:    'static+Send {
        self.future_desync(job)
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    /// The future will be scheduled in the background, so it will make progress even if the current scheduler is
    /// blocked for any reason. Additionally, it's not necessary to await the returned future, which can be discarded
    /// if necessary. As soon as this function returns, the order of the operations in the future are fixed: that is,
    /// any other operation queued on the same `Desync` will be performed after the future completes.
    ///
    /// Call `detach()` on the returned future to fully put it in the background. If awaited the scheduler may try to
    /// 'steal' runtime on the current thread to complete the future, but it will also run the future in the background
    /// if the current thread is not available or a background thread is available faster. The future here has static
    /// lifetime so can be passed around in case something needs to monitor when it completes.
    ///
    /// This can also be used to run the future synchronously on the current thread, useful for cases where an async
    /// function needs to be called from a non-async context. Call `sync()` on the returned future to wait for its
    /// result synchronously.
    ///
    /// Note that this has a companion function, `future_sync()` which is more useful for cases where the future is
    /// awaited, as the 'inner' future is able to borrow values from the 'outer' future due to the shorter lifetime
    /// of the returned value.
    ///
    /// The normal `sync()` and `desync()` methods will schedule their operations in order around any `future_sync()`
    /// or `future_desync()` calls, so it's possible to easily mix async and traditional threaded code in one program.
    ///
    pub fn future_desync<'a, TFn, TFuture>(&self, job: TFn) -> SchedulerFuture<TFuture::Output>
    where
        TFn:                'static + Send + FnOnce(&'a mut T) -> TFuture,
        TFuture:            'a + Send + Future,
        TFuture::Output:    'static + Send,
    {
        // The future will have a lifetime shorter than the lifetime of this structure, and exclusivity is guaranteed
        // because queues only execute one task at a time
        let data = DataRef::<T>(self.data);

        scheduler().future_desync(&self.queue, move || {
            let data        = data.0;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning a future that must be awaited
    /// before it is dropped.
    ///
    /// The future will be scheduled in the current execution context, so it will only make progress if the current
    /// scheduler is running. The task will be cancelled and will not complete execution if the future is dropped before
    /// completion, so it's usually necessary to await the result of this function for the task to behave correctly.
    ///
    /// This is most useful for 'foreground' futures: note that the future can safely borrow values from the outer context
    /// to use while it's running (in much the same way `sync()` can in synchronous contexts). The need to dispatch to
    /// the awaiting context can make this future slightly slower than `future_desync()`
    ///
    /// Desync has a feature that makes it possible to wait synchronously for futures to complete: slightly
    /// counter-intuitively this is found in the future returned by `future_desync()` function rather than this function,
    /// which is synchronous with the current async context instead of with the current thread.
    ///
    /// The normal `sync()` and `desync()` methods will schedule their operations in order around any `future_sync()`
    /// or `future_desync()` calls, so it's possible to easily mix async and traditional threaded code in one program.
    ///
    pub fn future_sync<'a, 'b, TFn, TFuture>(&'a self, job: TFn) -> impl 'a + Future<Output=Result<TFuture::Output, oneshot::Canceled>> + Send
    where
        TFn:                'a + Send + FnOnce(&'b mut T) -> TFuture,
        TFuture:            'b + Send + Future,
        TFuture::Output:    'a + Send,
        'b:                 'a,
    {
        // The future will have a lifetime shorter than the lifetime of this structure
        let data = DataRef::<T>(self.data);

        scheduler().future_sync(&self.queue, move || {
            let data        = data.0;
            let job         = job(unsafe { &mut *data });

            async {
                job.await
            }
        })
    }

    ///
    /// After the pending operations for this item are performed, waits for the
    /// supplied future to complete and then calls the specified function
    ///
    pub fn after<'a, TFn, Res, Fut>(&self, after: Fut, job: TFn) -> impl 'static + Future<Output=Result<Res, oneshot::Canceled>> + Send
    where
        Res: 'static + Send,
        Fut: 'static + Future + Send,
        TFn: 'static + Send + FnOnce(&mut T, Fut::Output) -> Res
    {
        self.future_desync(move |data| {
            async move {
                let future_result = after.await;
                job(data, future_result)
            }
        })
    }
}

impl<T: Send> Drop for Desync<T> {
    fn drop(&mut self) {
        use std::thread;

        // Take the data we're about to drop from the object
        let data = DataRef::<T>(self.data);

        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        if thread::panicking() {
            // If the thread is already panicking when we're dropped, do not panic again
            scheduler().sync_no_panic(&self.queue, move || {
                let data = data.0;
                mem::drop(unsafe { Box::from_raw(data) });
            });
        } else {
            // Thread is not panicking
            sync(&self.queue, move || {
                let data = data.0;
                mem::drop(unsafe { Box::from_raw(data) });
            });
        }
    }
}
<|endoftext|>


<commit_before>use vector::Vector3;

trait Point3 {
    fn from_origin(&self) -> Vector3;
}

trait SpatialTree<T : Point3> {
    fn find_closest_point(&self, p : Point3) -> T;
    fn find_closest_point_within_range(&self, p : Point3, range : f64) -> T;
    fn find_k_nearest_points(&self, p : Point3, k: u32) -> Iterator<Item=T>;
    fn find_k_nearest_points_within_range(&self, p : Point3, k : u32, range : f64) -> Iterator<Item=T>;
}<commit_msg>box the iterator<commit_after>use vector::Vector3;

trait Point3 {
    fn from_origin(&self) -> Vector3;
}

trait SpatialTree<T : Point3> {
    fn find_closest_point(&self, p : Point3) -> T;
    fn find_closest_point_within_range(&self, p : Point3, range : f64) -> T;
    fn find_k_nearest_points(&self, p : Point3, k: u32) -> Box<Iterator<Item=T>>;
    fn find_k_nearest_points_within_range(&self, p : Point3, k : u32, range : f64) -> Box<Iterator<Item=T>>;
}<|endoftext|>


<commit_before>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

use file;

/// A struct representing an editor
pub struct Editor<'a> {
    /// Represents the mode the editor
    /// is currently in
    mode: EditorMode,
    /// Represents the name of the file
    /// we are working with
    filename: &'a str,
    /// A hash of the original file's contents
    hash: String,
    /// Represents the contents of the file
    pub contents: Vec<Vec<u8>>,
}

/// This enum represents what mode the editor is in.
/// Modes are analogous to the concept found in the
/// Vi editor.
#[derive(PartialEq)]
pub enum EditorMode {
    /// Command mode
    Command,
    /// Insert mode
    Insert,
}

impl<'a> Editor<'a> {
    /// Creates a new editor. If filename is the empty
    /// string, the contents are `Forge`. Otherwise, the
    /// given filename is read as the contents.
    pub fn new(filename: &str) -> Editor {
        let contents: Vec<Vec<u8>> = vec![vec![]];
        let hash = String::new();

        Editor {
            mode: EditorMode::Command,
            filename: filename,
            contents: contents,
            hash: hash,
        }
    }

    /// Reads `self.filename` to a vector of lines. A line is determined
    /// by a newline character, or when a line's length is the same as
    /// the given `line_length`. `\n` is included in the line, and must
    /// be ignored when printing in the UI.
    pub fn read_file(&mut self, line_length: usize) {
        let filename = self.filename;
        if filename == "" {
            self.contents = vec![vec![b'F', b'o', b'r', b'g', b'e']];
        } else if file::file_exists(filename) {
            // It *should* be safe to unwrap here, since we have already
            // checked that `filename` exists.
            self.contents = file::read_file_lines(filename, line_length)
                .unwrap();
        }

        let hash = file::sha512_file(filename).unwrap();
        self.hash = hash;
    }

    pub fn write_file(&mut self) -> Result<(), String> {
        let filename = self.filename;
        let contents = &self.contents;

        if filename == "" {
            return Err(String::from("No filename given!"));
        } else {
            match file::write_file_lines(filename, contents) {
                Ok(_) => return Ok(()),
                Err(e) => return Err(format!("{}", e)),
            }
        }
    }

    /// Returns the name of the file we are working with.
    pub fn filename(&self) -> &str {
        self.filename
    }

    /// Returns true if we are in `Command` mode,
    /// false otherwise.
    pub fn command_mode(&self) -> bool {
        self.mode == EditorMode::Command
    }

    /// Switches the mode that the editor is in.
    pub fn switch_mode(&mut self) {
        use self::EditorMode::*;

        match self.mode {
            Command => self.mode = Insert,
            Insert => self.mode = Command,
        }
    }

    pub fn replace_char(&mut self, (x, y): (usize, usize), c: char) {
        // contents[line][position in line]
        self.contents[y][x] = c as u8;
    }

    pub fn hash(&self) -> String {
        self.hash.clone()
    }
}
<commit_msg>Only hash file if it exists<commit_after>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

use file;

/// A struct representing an editor
pub struct Editor<'a> {
    /// Represents the mode the editor
    /// is currently in
    mode: EditorMode,
    /// Represents the name of the file
    /// we are working with
    filename: &'a str,
    /// A hash of the original file's contents
    hash: String,
    /// Represents the contents of the file
    pub contents: Vec<Vec<u8>>,
}

/// This enum represents what mode the editor is in.
/// Modes are analogous to the concept found in the
/// Vi editor.
#[derive(PartialEq)]
pub enum EditorMode {
    /// Command mode
    Command,
    /// Insert mode
    Insert,
}

impl<'a> Editor<'a> {
    /// Creates a new editor. If filename is the empty
    /// string, the contents are `Forge`. Otherwise, the
    /// given filename is read as the contents.
    pub fn new(filename: &str) -> Editor {
        let contents: Vec<Vec<u8>> = vec![vec![]];
        let hash = String::new();

        Editor {
            mode: EditorMode::Command,
            filename: filename,
            contents: contents,
            hash: hash,
        }
    }

    /// Reads `self.filename` to a vector of lines. A line is determined
    /// by a newline character, or when a line's length is the same as
    /// the given `line_length`. `\n` is included in the line, and must
    /// be ignored when printing in the UI.
    pub fn read_file(&mut self, line_length: usize) {
        let filename = self.filename;
        if filename == "" {
            self.contents = vec![vec![b'F', b'o', b'r', b'g', b'e']];
        } else if file::file_exists(filename) {
            // It *should* be safe to unwrap here, since we have already
            // checked that `filename` exists.
            self.contents = file::read_file_lines(filename, line_length)
                .unwrap();

            let hash = file::sha512_file(filename).unwrap();
            self.hash = hash;
        }
    }

    pub fn write_file(&mut self) -> Result<(), String> {
        let filename = self.filename;
        let contents = &self.contents;

        if filename == "" {
            return Err(String::from("No filename given!"));
        } else {
            match file::write_file_lines(filename, contents) {
                Ok(_) => return Ok(()),
                Err(e) => return Err(format!("{}", e)),
            }
        }
    }

    /// Returns the name of the file we are working with.
    pub fn filename(&self) -> &str {
        self.filename
    }

    /// Returns true if we are in `Command` mode,
    /// false otherwise.
    pub fn command_mode(&self) -> bool {
        self.mode == EditorMode::Command
    }

    /// Switches the mode that the editor is in.
    pub fn switch_mode(&mut self) {
        use self::EditorMode::*;

        match self.mode {
            Command => self.mode = Insert,
            Insert => self.mode = Command,
        }
    }

    pub fn replace_char(&mut self, (x, y): (usize, usize), c: char) {
        // contents[line][position in line]
        self.contents[y][x] = c as u8;
    }

    pub fn hash(&self) -> String {
        self.hash.clone()
    }
}
<|endoftext|>


<commit_before>use crate::{channel::Reply, ChannelId, Error, Promise, PromiseResolver, Result};
use amq_protocol::{frame::AMQPFrame, protocol::{AMQPClass, basic::AMQPMethod}};
use parking_lot::Mutex;
use pinky_swear::Cancellable;
use std::{
    collections::{HashMap, VecDeque},
    fmt,
    sync::Arc,
};
use tracing::{level_enabled, trace, Level};

pub(crate) struct ExpectedReply(
    pub(crate) Reply,
    pub(crate) Box<dyn Cancellable<Error> + Send>,
);

impl fmt::Debug for ExpectedReply {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("ExpectedReply").field(&self.0).finish()
    }
}

#[derive(Clone, Default)]
pub(crate) struct Frames {
    inner: Arc<Mutex<Inner>>,
}

impl Frames {
    pub(crate) fn push(
        &self,
        channel_id: ChannelId,
        frame: AMQPFrame,
        resolver: PromiseResolver<()>,
        expected_reply: Option<ExpectedReply>,
    ) {
        self.inner
            .lock()
            .push(channel_id, frame, resolver, expected_reply);
    }

    pub(crate) async fn push_frames(&self, frames: Vec<AMQPFrame>) -> Result<()> {
        let promise = self.inner.lock().push_frames(frames);
        promise.await
    }

    pub(crate) fn retry(&self, frame: (AMQPFrame, Option<PromiseResolver<()>>)) {
        self.inner.lock().retry_frames.push_back(frame);
    }

    pub(crate) fn pop(&self, flow: bool) -> Option<(AMQPFrame, Option<PromiseResolver<()>>)> {
        self.inner.lock().pop(flow)
    }

    pub(crate) fn next_expected_reply(&self, channel_id: ChannelId) -> Option<Reply> {
        self.inner
            .lock()
            .expected_replies
            .get_mut(&channel_id)
            .and_then(|replies| replies.pop_front())
            .map(|t| t.0)
    }

    pub(crate) fn next_expected_close_ok_reply(
        &self,
        channel_id: u16,
        error: Error,
    ) -> Option<Reply> {
        self.inner
            .lock()
            .next_expected_close_ok_reply(channel_id, error)
    }

    pub(crate) fn has_pending(&self) -> bool {
        self.inner.lock().has_pending()
    }

    pub(crate) fn drop_pending(&self, error: Error) {
        self.inner.lock().drop_pending(error);
    }

    pub(crate) fn clear_expected_replies(&self, channel_id: ChannelId, error: Error) {
        self.inner.lock().clear_expected_replies(channel_id, error);
    }
}

struct Inner {
    /* Header frames must follow basic.publish frames directly, otherwise RabbitMQ-server send us an UNEXPECTED_FRAME */
    /* After sending the Header frame, we need to send the associated Body frames before anything else for the same reason */
    publish_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    retry_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    low_prio_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    expected_replies: HashMap<ChannelId, VecDeque<ExpectedReply>>,
}

impl Default for Inner {
    fn default() -> Self {
        Self {
            publish_frames: VecDeque::default(),
            retry_frames: VecDeque::default(),
            frames: VecDeque::default(),
            low_prio_frames: VecDeque::default(),
            expected_replies: HashMap::default(),
        }
    }
}

impl fmt::Debug for Frames {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Frames");
        if let Some(inner) = self.inner.try_lock() {
            debug.field("expected_replies", &inner.expected_replies);
        }
        debug.finish()
    }
}

impl Inner {
    fn push(
        &mut self,
        channel_id: ChannelId,
        frame: AMQPFrame,
        resolver: PromiseResolver<()>,
        expected_reply: Option<ExpectedReply>,
    ) {
        self.frames.push_back((frame, Some(resolver)));
        if let Some(reply) = expected_reply {
            trace!(
                channel=%channel_id,
                expected_reply=?reply,
                "state is now waiting"
            );
            self.expected_replies
                .entry(channel_id)
                .or_default()
                .push_back(reply);
        }
    }

    fn push_frames(&mut self, mut frames: Vec<AMQPFrame>) -> Promise<()> {
        let (promise, resolver) = Promise::new();
        let last_frame = frames.pop();

        if level_enabled!(Level::TRACE) {
            promise.set_marker("Frames".into());
        }

        for frame in frames {
            self.low_prio_frames.push_back((frame, None));
        }
        if let Some(last_frame) = last_frame {
            self.low_prio_frames.push_back((last_frame, Some(resolver)));
        } else {
            resolver.swear(Ok(()));
        }
        promise
    }

    fn pop(&mut self, flow: bool) -> Option<(AMQPFrame, Option<PromiseResolver<()>>)> {
        if let Some(frame) = self
            .retry_frames
            .pop_front()
            .or_else(|| self.publish_frames.pop_front())
            .or_else(|| self.frames.pop_front())
        {
            return Some(frame);
        }
        if flow {
            if let Some(frame) = self.low_prio_frames.pop_front() {
                // If the next frame is a header, that means we're a basic.publish
                // Header frame needs to follow directly the basic.publish frame, and Body frames
                // need to be sent just after those or the AMQP server will close the connection.
                // Push the header into publish_frames which is there to handle just that.
                if self
                    .low_prio_frames
                    .front()
                    .map(|(frame, _)| frame.is_header())
                    .unwrap_or(false)
                {
                    // Yes, this will always be Some() with a Header frame, but let's keep our unwrap() count low
                    if let Some(next_frame) = self.low_prio_frames.pop_front() {
                        self.publish_frames.push_back(next_frame);
                    }
                    while let Some(next_frame) = self.low_prio_frames.pop_front() {
                        match next_frame.0 {
                            AMQPFrame::Body(..) => {
                                self.publish_frames.push_back(next_frame);
                            }
                            _ => {
                                // We've exhausted Body frames for this publish, push back the next one and exit
                                self.low_prio_frames.push_front(next_frame);
                                break;
                            }
                        }
                    }
                }
                return Some(frame);
            }
        }
        None
    }

    fn has_pending(&self) -> bool {
        !(self.retry_frames.is_empty()
            && self.publish_frames.is_empty()
            && self.frames.is_empty()
            && self.low_prio_frames.is_empty())
    }

    fn drop_pending(&mut self, error: Error) {
        Self::drop_pending_frames(&mut self.retry_frames, error.clone());
        Self::drop_pending_frames(&mut self.publish_frames, error.clone());
        Self::drop_pending_frames(&mut self.frames, error.clone());
        Self::drop_pending_frames(&mut self.low_prio_frames, error.clone());
        for (_, replies) in self.expected_replies.drain() {
            Self::cancel_expected_replies(replies, error.clone());
        }
    }

    fn drop_pending_frames(
        frames: &mut VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
        error: Error,
    ) {
        for (frame, resolver) in std::mem::take(frames) {
            if let Some(resolver) = resolver {
                match frame {
                    AMQPFrame::Method(_, AMQPClass::Basic(AMQPMethod::Cancel(_))) => {
                        resolver.swear(Ok(()))
                    }
                    _ => resolver.swear(Err(error.clone())),
                }
            }
        }
    }

    fn next_expected_close_ok_reply(&mut self, channel_id: u16, error: Error) -> Option<Reply> {
        let expected_replies = self.expected_replies.get_mut(&channel_id)?;
        while let Some(reply) = expected_replies.pop_front() {
            match &reply.0 {
                Reply::ChannelCloseOk(_) => return Some(reply.0),
                Reply::BasicCancelOk(pinky) => pinky.swear(Ok(())), // Channel close means consumer is canceled automatically
                _ => reply.1.cancel(error.clone()),
            }
        }
        None
    }

    fn clear_expected_replies(&mut self, channel_id: ChannelId, error: Error) {
        if let Some(replies) = self.expected_replies.remove(&channel_id) {
            Self::cancel_expected_replies(replies, error);
        }
    }

    fn cancel_expected_replies(replies: VecDeque<ExpectedReply>, error: Error) {
        for ExpectedReply(reply, cancel) in replies {
            match reply {
                Reply::BasicCancelOk(pinky) => pinky.swear(Ok(())),
                _ => cancel.cancel(error.clone()),
            }
        }
    }
}
<commit_msg>rustfmt<commit_after>use crate::{channel::Reply, ChannelId, Error, Promise, PromiseResolver, Result};
use amq_protocol::{
    frame::AMQPFrame,
    protocol::{basic::AMQPMethod, AMQPClass},
};
use parking_lot::Mutex;
use pinky_swear::Cancellable;
use std::{
    collections::{HashMap, VecDeque},
    fmt,
    sync::Arc,
};
use tracing::{level_enabled, trace, Level};

pub(crate) struct ExpectedReply(
    pub(crate) Reply,
    pub(crate) Box<dyn Cancellable<Error> + Send>,
);

impl fmt::Debug for ExpectedReply {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("ExpectedReply").field(&self.0).finish()
    }
}

#[derive(Clone, Default)]
pub(crate) struct Frames {
    inner: Arc<Mutex<Inner>>,
}

impl Frames {
    pub(crate) fn push(
        &self,
        channel_id: ChannelId,
        frame: AMQPFrame,
        resolver: PromiseResolver<()>,
        expected_reply: Option<ExpectedReply>,
    ) {
        self.inner
            .lock()
            .push(channel_id, frame, resolver, expected_reply);
    }

    pub(crate) async fn push_frames(&self, frames: Vec<AMQPFrame>) -> Result<()> {
        let promise = self.inner.lock().push_frames(frames);
        promise.await
    }

    pub(crate) fn retry(&self, frame: (AMQPFrame, Option<PromiseResolver<()>>)) {
        self.inner.lock().retry_frames.push_back(frame);
    }

    pub(crate) fn pop(&self, flow: bool) -> Option<(AMQPFrame, Option<PromiseResolver<()>>)> {
        self.inner.lock().pop(flow)
    }

    pub(crate) fn next_expected_reply(&self, channel_id: ChannelId) -> Option<Reply> {
        self.inner
            .lock()
            .expected_replies
            .get_mut(&channel_id)
            .and_then(|replies| replies.pop_front())
            .map(|t| t.0)
    }

    pub(crate) fn next_expected_close_ok_reply(
        &self,
        channel_id: u16,
        error: Error,
    ) -> Option<Reply> {
        self.inner
            .lock()
            .next_expected_close_ok_reply(channel_id, error)
    }

    pub(crate) fn has_pending(&self) -> bool {
        self.inner.lock().has_pending()
    }

    pub(crate) fn drop_pending(&self, error: Error) {
        self.inner.lock().drop_pending(error);
    }

    pub(crate) fn clear_expected_replies(&self, channel_id: ChannelId, error: Error) {
        self.inner.lock().clear_expected_replies(channel_id, error);
    }
}

struct Inner {
    /* Header frames must follow basic.publish frames directly, otherwise RabbitMQ-server send us an UNEXPECTED_FRAME */
    /* After sending the Header frame, we need to send the associated Body frames before anything else for the same reason */
    publish_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    retry_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    low_prio_frames: VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
    expected_replies: HashMap<ChannelId, VecDeque<ExpectedReply>>,
}

impl Default for Inner {
    fn default() -> Self {
        Self {
            publish_frames: VecDeque::default(),
            retry_frames: VecDeque::default(),
            frames: VecDeque::default(),
            low_prio_frames: VecDeque::default(),
            expected_replies: HashMap::default(),
        }
    }
}

impl fmt::Debug for Frames {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Frames");
        if let Some(inner) = self.inner.try_lock() {
            debug.field("expected_replies", &inner.expected_replies);
        }
        debug.finish()
    }
}

impl Inner {
    fn push(
        &mut self,
        channel_id: ChannelId,
        frame: AMQPFrame,
        resolver: PromiseResolver<()>,
        expected_reply: Option<ExpectedReply>,
    ) {
        self.frames.push_back((frame, Some(resolver)));
        if let Some(reply) = expected_reply {
            trace!(
                channel=%channel_id,
                expected_reply=?reply,
                "state is now waiting"
            );
            self.expected_replies
                .entry(channel_id)
                .or_default()
                .push_back(reply);
        }
    }

    fn push_frames(&mut self, mut frames: Vec<AMQPFrame>) -> Promise<()> {
        let (promise, resolver) = Promise::new();
        let last_frame = frames.pop();

        if level_enabled!(Level::TRACE) {
            promise.set_marker("Frames".into());
        }

        for frame in frames {
            self.low_prio_frames.push_back((frame, None));
        }
        if let Some(last_frame) = last_frame {
            self.low_prio_frames.push_back((last_frame, Some(resolver)));
        } else {
            resolver.swear(Ok(()));
        }
        promise
    }

    fn pop(&mut self, flow: bool) -> Option<(AMQPFrame, Option<PromiseResolver<()>>)> {
        if let Some(frame) = self
            .retry_frames
            .pop_front()
            .or_else(|| self.publish_frames.pop_front())
            .or_else(|| self.frames.pop_front())
        {
            return Some(frame);
        }
        if flow {
            if let Some(frame) = self.low_prio_frames.pop_front() {
                // If the next frame is a header, that means we're a basic.publish
                // Header frame needs to follow directly the basic.publish frame, and Body frames
                // need to be sent just after those or the AMQP server will close the connection.
                // Push the header into publish_frames which is there to handle just that.
                if self
                    .low_prio_frames
                    .front()
                    .map(|(frame, _)| frame.is_header())
                    .unwrap_or(false)
                {
                    // Yes, this will always be Some() with a Header frame, but let's keep our unwrap() count low
                    if let Some(next_frame) = self.low_prio_frames.pop_front() {
                        self.publish_frames.push_back(next_frame);
                    }
                    while let Some(next_frame) = self.low_prio_frames.pop_front() {
                        match next_frame.0 {
                            AMQPFrame::Body(..) => {
                                self.publish_frames.push_back(next_frame);
                            }
                            _ => {
                                // We've exhausted Body frames for this publish, push back the next one and exit
                                self.low_prio_frames.push_front(next_frame);
                                break;
                            }
                        }
                    }
                }
                return Some(frame);
            }
        }
        None
    }

    fn has_pending(&self) -> bool {
        !(self.retry_frames.is_empty()
            && self.publish_frames.is_empty()
            && self.frames.is_empty()
            && self.low_prio_frames.is_empty())
    }

    fn drop_pending(&mut self, error: Error) {
        Self::drop_pending_frames(&mut self.retry_frames, error.clone());
        Self::drop_pending_frames(&mut self.publish_frames, error.clone());
        Self::drop_pending_frames(&mut self.frames, error.clone());
        Self::drop_pending_frames(&mut self.low_prio_frames, error.clone());
        for (_, replies) in self.expected_replies.drain() {
            Self::cancel_expected_replies(replies, error.clone());
        }
    }

    fn drop_pending_frames(
        frames: &mut VecDeque<(AMQPFrame, Option<PromiseResolver<()>>)>,
        error: Error,
    ) {
        for (frame, resolver) in std::mem::take(frames) {
            if let Some(resolver) = resolver {
                match frame {
                    AMQPFrame::Method(_, AMQPClass::Basic(AMQPMethod::Cancel(_))) => {
                        resolver.swear(Ok(()))
                    }
                    _ => resolver.swear(Err(error.clone())),
                }
            }
        }
    }

    fn next_expected_close_ok_reply(&mut self, channel_id: u16, error: Error) -> Option<Reply> {
        let expected_replies = self.expected_replies.get_mut(&channel_id)?;
        while let Some(reply) = expected_replies.pop_front() {
            match &reply.0 {
                Reply::ChannelCloseOk(_) => return Some(reply.0),
                Reply::BasicCancelOk(pinky) => pinky.swear(Ok(())), // Channel close means consumer is canceled automatically
                _ => reply.1.cancel(error.clone()),
            }
        }
        None
    }

    fn clear_expected_replies(&mut self, channel_id: ChannelId, error: Error) {
        if let Some(replies) = self.expected_replies.remove(&channel_id) {
            Self::cancel_expected_replies(replies, error);
        }
    }

    fn cancel_expected_replies(replies: VecDeque<ExpectedReply>, error: Error) {
        for ExpectedReply(reply, cancel) in replies {
            match reply {
                Reply::BasicCancelOk(pinky) => pinky.swear(Ok(())),
                _ => cancel.cancel(error.clone()),
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::common::Log;

use futures::prelude::*;

use hyper;
use serde_json;

const TRUSTED_LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/log_list.json";
const ALL_LOG_LIST_URL: &'static str = "https://www.gstatic.com/ct/log_list/all_logs_list.json";

#[derive(Deserialize)]
struct LogsResponseLogs {
    description: String,
    url: String,
    operated_by: Vec<u32>,
    disqualified_at: Option<u64>,
}

#[derive(Deserialize)]
struct LogsResponseOperators {
    name: String,
    id: u32,
}

#[derive(Deserialize)]
struct LogsResponse {
    logs: Vec<LogsResponseLogs>,
    operators: Vec<LogsResponseOperators>,
}

pub fn fetch_trusted_ct_logs<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    fetch_log_list(http_client, TRUSTED_LOG_LIST_URL.parse().unwrap())
}

pub fn fetch_all_ct_logs<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    fetch_log_list(http_client, ALL_LOG_LIST_URL.parse().unwrap())
}

fn fetch_log_list<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
    uri: hyper::Uri,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    async_block! {
        let response = await!(http_client.get(uri)).unwrap();
        // Limit the response to 10MB at most, to be resillient to DoS.
        let body = await!(response.body().take(10 * 1024 * 1024).concat2()).unwrap();
        let logs_response: LogsResponse = serde_json::from_slice(&body).unwrap();

        let google_id = logs_response
            .operators
            .iter()
            .find(|o| o.name == "Google")
            .map(|o| o.id)
            .unwrap();

        Ok(
            logs_response
                .logs
                .into_iter()
                .filter(|log| log.disqualified_at.is_none())
                .map(move |log| {
                    Log {
                        url: log.url,
                        description: log.description,
                        is_google: log.operated_by.contains(&google_id),
                    }
                })
                .collect(),
        )
    }
}
<commit_msg>clippy lint<commit_after>use super::common::Log;

use futures::prelude::*;

use hyper;
use serde_json;

const TRUSTED_LOG_LIST_URL: &str = "https://www.gstatic.com/ct/log_list/log_list.json";
const ALL_LOG_LIST_URL: &str = "https://www.gstatic.com/ct/log_list/all_logs_list.json";

#[derive(Deserialize)]
struct LogsResponseLogs {
    description: String,
    url: String,
    operated_by: Vec<u32>,
    disqualified_at: Option<u64>,
}

#[derive(Deserialize)]
struct LogsResponseOperators {
    name: String,
    id: u32,
}

#[derive(Deserialize)]
struct LogsResponse {
    logs: Vec<LogsResponseLogs>,
    operators: Vec<LogsResponseOperators>,
}

pub fn fetch_trusted_ct_logs<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    fetch_log_list(http_client, TRUSTED_LOG_LIST_URL.parse().unwrap())
}

pub fn fetch_all_ct_logs<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    fetch_log_list(http_client, ALL_LOG_LIST_URL.parse().unwrap())
}

fn fetch_log_list<'a, C: hyper::client::Connect>(
    http_client: &'a hyper::Client<C>,
    uri: hyper::Uri,
) -> impl Future<Item = Vec<Log>, Error = ()> + 'a {
    async_block! {
        let response = await!(http_client.get(uri)).unwrap();
        // Limit the response to 10MB at most, to be resillient to DoS.
        let body = await!(response.body().take(10 * 1024 * 1024).concat2()).unwrap();
        let logs_response: LogsResponse = serde_json::from_slice(&body).unwrap();

        let google_id = logs_response
            .operators
            .iter()
            .find(|o| o.name == "Google")
            .map(|o| o.id)
            .unwrap();

        Ok(
            logs_response
                .logs
                .into_iter()
                .filter(|log| log.disqualified_at.is_none())
                .map(move |log| {
                    Log {
                        url: log.url,
                        description: log.description,
                        is_google: log.operated_by.contains(&google_id),
                    }
                })
                .collect(),
        )
    }
}
<|endoftext|>


<commit_before>use base64;
use crate::buffer;
use crate::image;
use std::{fs, io};

use image_crate::ImageFormat::{Jpeg, Png};
use std::path::Path;
use crate::{Document, Error, Gltf, Result};

/// Return type of `import`.
type Import = (Document, Vec<buffer::Data>, Vec<image::Data>);

/// Represents the set of URI schemes the importer supports.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum Scheme<'a> {
    /// `data:[<media type>];base64,<data>`.
    Data(Option<&'a str>, &'a str),

    /// `file:[//]<absolute file path>`.
    ///
    /// Note: The file scheme does not implement authority.
    File(&'a str),

    /// `../foo`, etc.
    Relative,

    /// Placeholder for an unsupported URI scheme identifier.
    Unsupported,
}

impl<'a> Scheme<'a> {
    fn parse<'s>(uri: &'s str) -> Scheme<'s> {
        if uri.contains(":") {
            if uri.starts_with("data:") {
                let match0 = &uri["data:".len()..].split(";base64,").nth(0);
                let match1 = &uri["data:".len()..].split(";base64,").nth(1);
                if match1.is_some() {
                    Scheme::Data(Some(match0.unwrap()), match1.unwrap())
                } else if match0.is_some() {
                    Scheme::Data(None, match0.unwrap())
                } else {
                    Scheme::Unsupported
                }
            } else if uri.starts_with("file://") {
                Scheme::File(&uri["file://".len()..])
            } else if uri.starts_with("file:") {
                Scheme::File(&uri["file:".len()..])
            } else {
                Scheme::Unsupported
            }
        } else {
            Scheme::Relative
        }
    }

    fn read(base: &Path, uri: &str) -> Result<Vec<u8>> {
        match Scheme::parse(uri) {
            Scheme::Data(_, base64) => base64::decode(&base64).map_err(Error::Base64),
            Scheme::File(path) => read_to_end(path),
            Scheme::Relative => read_to_end(base.join(uri)),
            Scheme::Unsupported => Err(Error::UnsupportedScheme),
        }
    }
}

fn read_to_end<P>(path: P) -> Result<Vec<u8>>
where P: AsRef<Path>
{
    use io::Read;
    let file = fs::File::open(path.as_ref()).map_err(Error::Io)?;
    let length = file.metadata().map(|x| x.len()).unwrap_or(0);
    let mut reader = io::BufReader::new(file);
    let mut data = Vec::with_capacity(length as usize);
    reader.read_to_end(&mut data).map_err(Error::Io)?;
    Ok(data)
}

/// Import the buffer data referenced by a glTF document.
pub fn import_buffer_data(
    document: &Document,
    base: Option<&Path>,
    mut blob: Option<Vec<u8>>,
) -> Result<Vec<buffer::Data>> {
    let mut buffers = Vec::new();
    for buffer in document.buffers() {
        let mut data = match buffer.source() {
            buffer::Source::Uri(uri) if base.is_some() => Scheme::read(base.unwrap(), uri),
            buffer::Source::Bin => blob.take().ok_or(Error::MissingBlob),
            _ => Err(Error::ExternalReferenceInSliceImport)
        }?;
        if data.len() < buffer.length() {
            return Err(
                Error::BufferLength {
                    buffer: buffer.index(),
                    expected: buffer.length(),
                    actual: data.len(),
                }
            );
        }
        while data.len() % 4 != 0 {
            data.push(0);
        }
        buffers.push(buffer::Data(data));
    }
    Ok(buffers)
}

/// Import the image data referenced by a glTF document.
pub fn import_image_data(
    document: &Document,
    base: Option<&Path>,
    buffer_data: &[buffer::Data],
) -> Result<Vec<image::Data>> {
    let mut images = Vec::new();
    #[cfg(feature = "guess_mime_type")]
    let guess_format = |encoded_image: &[u8]| {
        match image_crate::guess_format(encoded_image) {
            Ok(image_crate::ImageFormat::PNG) => Some(Png),
            Ok(image_crate::ImageFormat::JPEG) => Some(Jpeg),
            _ => None,
        }
    };
    #[cfg(not(feature = "guess_mime_type"))]
    let guess_format = |_encoded_image: &[u8]| {
        None
    };
    for image in document.images() {
        match image.source() {
            image::Source::Uri { uri, mime_type } if base.is_some() => {
                match Scheme::parse(uri) {
                    Scheme::Data(Some(annoying_case), base64) => {
                        let encoded_image = base64::decode(&base64).map_err(Error::Base64)?;
                        let encoded_format = match annoying_case.as_ref() {
                            "image/png" => Png,
                            "image/jpeg" => Jpeg,
                            _ => match guess_format(&encoded_image) {
                                Some(format) => format,
                                None => return Err(Error::UnsupportedImageEncoding),
                            },
                        };
                        let decoded_image = image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?;
                        images.push(image::Data::new(decoded_image));
                        continue;
                    },
                    Scheme::Unsupported => return Err(Error::UnsupportedScheme),
                    _ => {},
                }
                let encoded_image = Scheme::read(base.unwrap(), uri)?;
                let encoded_format =  match mime_type {
                    Some("image/png") => Png,
                    Some("image/jpeg") => Jpeg,
                    Some(_) => match guess_format(&encoded_image) {
                        Some(format) => format,
                        None => return Err(Error::UnsupportedImageEncoding),
                    },
                    None => match uri.rsplit(".").next() {
                        Some("png") => Png,
                        Some("jpg") | Some("jpeg") => Jpeg,
                        _ => match guess_format(&encoded_image) {
                            Some(format) => format,
                            None => return Err(Error::UnsupportedImageEncoding),
                        },
                    },
                };
                let decoded_image = image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?;
                images.push(image::Data::new(decoded_image));
            },
            image::Source::View { view, mime_type } => {
                let parent_buffer_data = &buffer_data[view.buffer().index()].0;
                let begin = view.offset();
                let end = begin + view.length();
                let encoded_image = &parent_buffer_data[begin..end];
                let encoded_format = match mime_type {
                    "image/png" => Png,
                    "image/jpeg" => Jpeg,
                    _ => match guess_format(encoded_image) {
                        Some(format) => format,
                        None => return Err(Error::UnsupportedImageEncoding),
                    },
                };
                let decoded_image = image_crate::load_from_memory_with_format(encoded_image, encoded_format)?;
                images.push(image::Data::new(decoded_image));
            },
            _ => return Err(Error::ExternalReferenceInSliceImport)
        }
    }

    Ok(images)
}

fn import_impl(Gltf { document, blob }: Gltf, base: Option<&Path>) -> Result<Import> {
    let buffer_data = import_buffer_data(&document, base, blob)?;
    let image_data = import_image_data(&document, base, &buffer_data)?;
    let import = (document, buffer_data, image_data);
    Ok(import)
}

fn import_path(path: &Path) -> Result<Import> {
    let base = path.parent().unwrap_or(Path::new("./"));
    let file = fs::File::open(path).map_err(Error::Io)?;
    let reader = io::BufReader::new(file);
    import_impl(Gltf::from_reader(reader)?, Some(base))
}

/// Import some glTF 2.0 from the file system.
///
/// ```
/// # fn run() -> Result<(), gltf::Error> {
/// # let path = "examples/Box.gltf";
/// # #[allow(unused)]
/// let (document, buffers, images) = gltf::import(path)?;
/// # Ok(())
/// # }
/// # fn main() {
/// #     run().expect("test failure");
/// # }
/// ```
///
/// ### Note
///
/// This function is provided as a convenience for loading glTF and associated
/// resources from the file system. It is suitable for real world use but may
/// not be suitable for all real world use cases. More complex import scenarios
/// such downloading from web URLs are not handled by this function. These
/// scenarios are delegated to the user.
///
/// You can read glTF without loading resources by constructing the [`Gltf`]
/// (standard glTF) or [`Glb`] (binary glTF) data structures explicitly.
///
/// [`Gltf`]: struct.Gltf.html
/// [`Glb`]: struct.Glb.html
pub fn import<P>(path: P) -> Result<Import>
    where P: AsRef<Path>
{
    import_path(path.as_ref())
}

pub fn import_slice_impl(slice: &[u8]) -> Result<Import> {
    import_impl(Gltf::from_slice(slice)?, None)
}

/// Import some glTF 2.0 from a slice
///
/// ```
/// # extern crate gltf;
/// # use std::fs;
/// # use std::io::Read;
/// # fn run() -> Result<(), gltf::Error> {
/// # let path = "examples/Box.glb";
/// # let mut file = fs::File::open(path).map_err(gltf::Error::Io)?;
/// # let mut bytes = Vec::new();
/// # file.read_to_end(&mut bytes).map_err(gltf::Error::Io)?;
/// # #[allow(unused)]
/// let (document, buffers, images) = gltf::import_slice(bytes.as_slice())?;
/// # Ok(())
/// # }
/// # fn main() {
/// #     run().expect("test failure");
/// # }
/// ```
pub fn import_slice<S>(slice: S) -> Result<Import>
    where S: AsRef<[u8]>
{
    import_slice_impl(slice.as_ref())
}
<commit_msg>Updated code for `"all-features"`.<commit_after>use base64;
use crate::buffer;
use crate::image;
use std::{fs, io};

use image_crate::ImageFormat::{Jpeg, Png};
use std::path::Path;
use crate::{Document, Error, Gltf, Result};

/// Return type of `import`.
type Import = (Document, Vec<buffer::Data>, Vec<image::Data>);

/// Represents the set of URI schemes the importer supports.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum Scheme<'a> {
    /// `data:[<media type>];base64,<data>`.
    Data(Option<&'a str>, &'a str),

    /// `file:[//]<absolute file path>`.
    ///
    /// Note: The file scheme does not implement authority.
    File(&'a str),

    /// `../foo`, etc.
    Relative,

    /// Placeholder for an unsupported URI scheme identifier.
    Unsupported,
}

impl<'a> Scheme<'a> {
    fn parse<'s>(uri: &'s str) -> Scheme<'s> {
        if uri.contains(":") {
            if uri.starts_with("data:") {
                let match0 = &uri["data:".len()..].split(";base64,").nth(0);
                let match1 = &uri["data:".len()..].split(";base64,").nth(1);
                if match1.is_some() {
                    Scheme::Data(Some(match0.unwrap()), match1.unwrap())
                } else if match0.is_some() {
                    Scheme::Data(None, match0.unwrap())
                } else {
                    Scheme::Unsupported
                }
            } else if uri.starts_with("file://") {
                Scheme::File(&uri["file://".len()..])
            } else if uri.starts_with("file:") {
                Scheme::File(&uri["file:".len()..])
            } else {
                Scheme::Unsupported
            }
        } else {
            Scheme::Relative
        }
    }

    fn read(base: &Path, uri: &str) -> Result<Vec<u8>> {
        match Scheme::parse(uri) {
            Scheme::Data(_, base64) => base64::decode(&base64).map_err(Error::Base64),
            Scheme::File(path) => read_to_end(path),
            Scheme::Relative => read_to_end(base.join(uri)),
            Scheme::Unsupported => Err(Error::UnsupportedScheme),
        }
    }
}

fn read_to_end<P>(path: P) -> Result<Vec<u8>>
where P: AsRef<Path>
{
    use io::Read;
    let file = fs::File::open(path.as_ref()).map_err(Error::Io)?;
    let length = file.metadata().map(|x| x.len()).unwrap_or(0);
    let mut reader = io::BufReader::new(file);
    let mut data = Vec::with_capacity(length as usize);
    reader.read_to_end(&mut data).map_err(Error::Io)?;
    Ok(data)
}

/// Import the buffer data referenced by a glTF document.
pub fn import_buffer_data(
    document: &Document,
    base: Option<&Path>,
    mut blob: Option<Vec<u8>>,
) -> Result<Vec<buffer::Data>> {
    let mut buffers = Vec::new();
    for buffer in document.buffers() {
        let mut data = match buffer.source() {
            buffer::Source::Uri(uri) if base.is_some() => Scheme::read(base.unwrap(), uri),
            buffer::Source::Bin => blob.take().ok_or(Error::MissingBlob),
            _ => Err(Error::ExternalReferenceInSliceImport)
        }?;
        if data.len() < buffer.length() {
            return Err(
                Error::BufferLength {
                    buffer: buffer.index(),
                    expected: buffer.length(),
                    actual: data.len(),
                }
            );
        }
        while data.len() % 4 != 0 {
            data.push(0);
        }
        buffers.push(buffer::Data(data));
    }
    Ok(buffers)
}

/// Import the image data referenced by a glTF document.
pub fn import_image_data(
    document: &Document,
    base: Option<&Path>,
    buffer_data: &[buffer::Data],
) -> Result<Vec<image::Data>> {
    let mut images = Vec::new();
    #[cfg(feature = "guess_mime_type")]
    let guess_format = |encoded_image: &[u8]| {
        match image_crate::guess_format(encoded_image) {
            Ok(image_crate::ImageFormat::Png) => Some(Png),
            Ok(image_crate::ImageFormat::Jpeg) => Some(Jpeg),
            _ => None,
        }
    };
    #[cfg(not(feature = "guess_mime_type"))]
    let guess_format = |_encoded_image: &[u8]| {
        None
    };
    for image in document.images() {
        match image.source() {
            image::Source::Uri { uri, mime_type } if base.is_some() => {
                match Scheme::parse(uri) {
                    Scheme::Data(Some(annoying_case), base64) => {
                        let encoded_image = base64::decode(&base64).map_err(Error::Base64)?;
                        let encoded_format = match annoying_case.as_ref() {
                            "image/png" => Png,
                            "image/jpeg" => Jpeg,
                            _ => match guess_format(&encoded_image) {
                                Some(format) => format,
                                None => return Err(Error::UnsupportedImageEncoding),
                            },
                        };
                        let decoded_image = image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?;
                        images.push(image::Data::new(decoded_image));
                        continue;
                    },
                    Scheme::Unsupported => return Err(Error::UnsupportedScheme),
                    _ => {},
                }
                let encoded_image = Scheme::read(base.unwrap(), uri)?;
                let encoded_format =  match mime_type {
                    Some("image/png") => Png,
                    Some("image/jpeg") => Jpeg,
                    Some(_) => match guess_format(&encoded_image) {
                        Some(format) => format,
                        None => return Err(Error::UnsupportedImageEncoding),
                    },
                    None => match uri.rsplit(".").next() {
                        Some("png") => Png,
                        Some("jpg") | Some("jpeg") => Jpeg,
                        _ => match guess_format(&encoded_image) {
                            Some(format) => format,
                            None => return Err(Error::UnsupportedImageEncoding),
                        },
                    },
                };
                let decoded_image = image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?;
                images.push(image::Data::new(decoded_image));
            },
            image::Source::View { view, mime_type } => {
                let parent_buffer_data = &buffer_data[view.buffer().index()].0;
                let begin = view.offset();
                let end = begin + view.length();
                let encoded_image = &parent_buffer_data[begin..end];
                let encoded_format = match mime_type {
                    "image/png" => Png,
                    "image/jpeg" => Jpeg,
                    _ => match guess_format(encoded_image) {
                        Some(format) => format,
                        None => return Err(Error::UnsupportedImageEncoding),
                    },
                };
                let decoded_image = image_crate::load_from_memory_with_format(encoded_image, encoded_format)?;
                images.push(image::Data::new(decoded_image));
            },
            _ => return Err(Error::ExternalReferenceInSliceImport)
        }
    }

    Ok(images)
}

fn import_impl(Gltf { document, blob }: Gltf, base: Option<&Path>) -> Result<Import> {
    let buffer_data = import_buffer_data(&document, base, blob)?;
    let image_data = import_image_data(&document, base, &buffer_data)?;
    let import = (document, buffer_data, image_data);
    Ok(import)
}

fn import_path(path: &Path) -> Result<Import> {
    let base = path.parent().unwrap_or(Path::new("./"));
    let file = fs::File::open(path).map_err(Error::Io)?;
    let reader = io::BufReader::new(file);
    import_impl(Gltf::from_reader(reader)?, Some(base))
}

/// Import some glTF 2.0 from the file system.
///
/// ```
/// # fn run() -> Result<(), gltf::Error> {
/// # let path = "examples/Box.gltf";
/// # #[allow(unused)]
/// let (document, buffers, images) = gltf::import(path)?;
/// # Ok(())
/// # }
/// # fn main() {
/// #     run().expect("test failure");
/// # }
/// ```
///
/// ### Note
///
/// This function is provided as a convenience for loading glTF and associated
/// resources from the file system. It is suitable for real world use but may
/// not be suitable for all real world use cases. More complex import scenarios
/// such downloading from web URLs are not handled by this function. These
/// scenarios are delegated to the user.
///
/// You can read glTF without loading resources by constructing the [`Gltf`]
/// (standard glTF) or [`Glb`] (binary glTF) data structures explicitly.
///
/// [`Gltf`]: struct.Gltf.html
/// [`Glb`]: struct.Glb.html
pub fn import<P>(path: P) -> Result<Import>
    where P: AsRef<Path>
{
    import_path(path.as_ref())
}

pub fn import_slice_impl(slice: &[u8]) -> Result<Import> {
    import_impl(Gltf::from_slice(slice)?, None)
}

/// Import some glTF 2.0 from a slice
///
/// ```
/// # extern crate gltf;
/// # use std::fs;
/// # use std::io::Read;
/// # fn run() -> Result<(), gltf::Error> {
/// # let path = "examples/Box.glb";
/// # let mut file = fs::File::open(path).map_err(gltf::Error::Io)?;
/// # let mut bytes = Vec::new();
/// # file.read_to_end(&mut bytes).map_err(gltf::Error::Io)?;
/// # #[allow(unused)]
/// let (document, buffers, images) = gltf::import_slice(bytes.as_slice())?;
/// # Ok(())
/// # }
/// # fn main() {
/// #     run().expect("test failure");
/// # }
/// ```
pub fn import_slice<S>(slice: S) -> Result<Import>
    where S: AsRef<[u8]>
{
    import_slice_impl(slice.as_ref())
}
<|endoftext|>