<commit_before>use crate::files::Files;
use eyre::{bail, Result};
use regex::Regex;
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{
    collections::{hash_map::Entry, HashMap},
    fs,
    path::{Path, PathBuf},
    str::FromStr,
};
use strum::{EnumString, EnumVariantNames, ToString, VariantNames};

/// # Details of a project
///
/// An implementation, and extension, of schema.org [`Project`](https://schema.org/Project).
/// Uses schema.org properties where possible but adds extension properties
/// where needed (e.g. `theme`).
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Project {
    /// The name of the project
    name: Option<String>,

    /// A description of the project
    description: Option<String>,

    /// The path (within the project) of the project's image
    ///
    /// If not specified, will default to the most recently
    /// modified image in the project (if any).
    image: Option<String>,

    /// The path (within the project) of the project's main file
    ///
    /// If not specified, will default to the first file matching the
    /// the regular expression in the configuration settings.
    main: Option<String>,

    /// The default theme to use when viewing documents in this project
    ///
    /// If not specified, will default to the default theme in the
    /// configuration settings.
    theme: Option<String>,

    // The following properties are derived from the filesystem
    // and should never be read from, or written to, the `project.json` file
    /// The filesystem path of the project folder
    #[serde(skip_deserializing)]
    path: PathBuf,

    /// The resolved path of the project's image file
    #[serde(skip_deserializing)]
    image_path: Option<PathBuf>,

    /// The resolved path of the project's main file
    #[serde(skip_deserializing)]
    main_path: Option<PathBuf>,

    /// The files in the project folder
    #[serde(skip_deserializing)]
    files: Files,
}

/// A format to display a plugin using
#[derive(Debug, EnumString, EnumVariantNames, PartialEq, ToString)]
#[strum(serialize_all = "lowercase")]
pub enum ShowFormat {
    Md,
    Toml,
    Json,
}

impl Project {
    /// The name of the project's manifest file within the project directory
    const FILE_NAME: &'static str = "project.json";

    /// Get the JSON Schema for a project
    pub fn schema() -> String {
        let schema = schema_for!(Project);
        serde_json::to_string_pretty(&schema).unwrap()
    }

    /// Get the path to a projects' manifest file
    fn file(folder: &str) -> PathBuf {
        Path::new(folder).join(Project::FILE_NAME)
    }

    /// Read a project's manifest file
    ///
    /// If there is no manifest file, then return a default project
    fn read(folder: &str) -> Result<Project> {
        if !Path::new(folder).exists() {
            bail!("Project folder does not exist: {}", folder)
        }

        let file = Project::file(folder);
        let mut project = if file.exists() {
            let json = fs::read_to_string(file)?;
            serde_json::from_str(&json)?
        } else {
            Project::default()
        };
        project.path = Path::new(folder).canonicalize()?;

        Ok(project)
    }

    /// Write a project's manifest file
    ///
    /// If the project folder does not exist yet then it will be created
    pub fn write(folder: &str, project: &Project) -> Result<()> {
        fs::create_dir_all(folder)?;

        let file = Project::file(folder);
        let json = serde_json::to_string_pretty(project)?;
        fs::write(file, json)?;

        Ok(())
    }

    /// Initialize a project in a new, or existing, folder
    ///
    /// If the project has already been initialized (i.e. has a manifest file)
    /// then this function will do nothing.
    pub fn init(folder: &str) -> Result<()> {
        if Project::file(folder).exists() {
            return Ok(());
        }

        let project = Project::default();
        Project::write(folder, &project)?;

        Ok(())
    }

    /// Load a project including creating default values for properties
    /// where necessary
    pub fn open(folder: &str, config: &config::ProjectsConfig, watch: bool) -> Result<Project> {
        let mut project = Project::read(folder)?;

        // Get all the files in the project
        project.files = Files::load(folder, watch)?;

        // Resolve the main file path first as some of the other project properties
        // may be defined there (e.g. in the YAML header of a Markdown file)
        project.main_path = project.resolve_main_path(&config.main_patterns);

        // Name defaults to the name of the folder
        project.name = project
            .name
            .or_else(|| match Path::new(folder).components().last() {
                Some(last) => Some(last.as_os_str().to_string_lossy().to_string()),
                None => Some("Unnamed".to_string()),
            });

        // Theme defaults to the configured default
        project.theme = project.theme.or_else(|| Some(config.theme.clone()));

        Ok(project)
    }

    /// Attempt to resolve the path of the main file for a project
    ///
    /// Attempts to use the projects `main` property. If that is not specified, or
    /// there is no matching file in the project, attempts to match one of the
    /// project's files against the `main_patterns`.
    fn resolve_main_path(&self, main_patterns: &[String]) -> Option<PathBuf> {
        let files = &*self.files.obtain().expect("Unable to get files");

        // Check that there is a file with the specified main path
        if let Some(main) = &self.main {
            let main_path = self.path.join(main);
            if files.contains_key(&main_path) {
                return Some(main_path);
            } else {
                tracing::warn!("Project main file specified could not be found: {}", main);
                // Will attempt to find using patterns
            }
        }

        // For each `main_pattern` (in order)...
        for pattern in main_patterns {
            // Make matching case insensitive
            let re = match Regex::new(&pattern.to_lowercase()) {
                Ok(re) => re,
                Err(_) => {
                    tracing::warn!("Project main file pattern is invalid: {}", pattern);
                    continue;
                }
            };

            for file in files.values() {
                // Ignore directories
                if file.children.is_some() {
                    continue;
                }
                // Match relative path to pattern
                if let Ok(relative_path) = file.path.strip_prefix(&self.path) {
                    if re.is_match(&relative_path.to_string_lossy().to_lowercase()) {
                        return Some(file.path.clone());
                    }
                }
            }
        }

        None
    }

    /// Show a project in a format
    ///
    /// Used for displaying a project in CLI and possibly elsewhere.
    pub fn show(&self, format: ShowFormat) -> Result<(String, String)> {
        let content = match format {
            ShowFormat::Json => serde_json::to_string_pretty(self)?,
            ShowFormat::Toml => toml::to_string(self)?,
            ShowFormat::Md => {
                use handlebars::Handlebars;

                let template = r#"
# {{name}}

**Main**: {{ main }}
**Theme**: {{ theme }}

"#;
                let hb = Handlebars::new();
                hb.render_template(template.trim(), self)?
            }
        };

        Ok((format.to_string(), content))
    }
}

/// An in-memory store of projects and associated
/// data (e.g. file system watchers)
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Projects {
    /// The projects, stored by absolute path
    pub registry: HashMap<String, Project>,
}

impl Default for Projects {
    fn default() -> Self {
        Projects {
            registry: HashMap::new(),
        }
    }
}

impl Projects {
    /// Get the canonical absolute path of a project folder
    fn path(folder: &str) -> Result<String> {
        Ok(Path::new(folder).canonicalize()?.display().to_string())
    }

    /// List projects that are open
    pub fn list(&self) -> Result<HashMap<String, Project>> {
        Ok(self.registry.clone())
    }

    /// Open a project
    ///
    /// This function `loads` a project, stores it, watches the project folder,
    /// updates the project on changes and publishes the updates on the "projects"
    /// pubsub topic channel.
    pub fn open(
        &mut self,
        folder: &str,
        config: &config::ProjectsConfig,
        watch: bool,
    ) -> Result<Project> {
        let path = Projects::path(folder)?;

        let project = match self.registry.entry(path) {
            Entry::Occupied(entry) => entry.into_mut(),
            Entry::Vacant(entry) => match Project::open(folder, config, watch) {
                Ok(project) => entry.insert(project),
                Err(error) => return Err(error),
            },
        };
        Ok(project.clone())
    }

    /// Close a project
    pub fn close(&mut self, folder: &str) -> Result<()> {
        let path = Projects::path(folder)?;
        self.registry.remove(&path);
        Ok(())
    }
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use defaults::Defaults;
    use validator::Validate;

    /// # Projects
    ///
    /// Configuration settings for project defaults
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default, rename_all = "camelCase")]
    pub struct ProjectsConfig {
        /// Patterns used to infer the main file of projects
        ///
        /// For projects that do not specify a main file, each file is tested
        /// against these case insensitive patterns in order. The first
        /// file (alphabetically) that matches is the project's main file.
        #[def = r#"vec!["^main\\b".to_string(), "^index\\b".to_string(), "^readme\\b".to_string()]"#]
        pub main_patterns: Vec<String>,

        /// Default project theme
        ///
        /// Will be applied to all projects that do not specify a theme
        #[def = r#"String::from("stencila")"#]
        pub theme: String,
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage projects",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Init(Init),
        List(List),
        Open(Open),
        Close(Close),
        Show(Show),
        Schema(Schema),
    }

    impl Command {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { action } = self;
            match action {
                Action::Init(action) => action.run(),
                Action::List(action) => action.run(projects),
                Action::Open(action) => action.run(projects, config),
                Action::Close(action) => action.run(projects),
                Action::Show(action) => action.run(projects, config),
                Action::Schema(action) => action.run(),
            }
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Initialize a project in a new, or existing, folder",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Init {
        /// The path of the new, or existing, folder to initialize
        ///
        /// If no folder exists at the path, then one will be created.
        /// If no `project.json` file exists in the folder then a new one
        /// will be created.
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Init {
        pub fn run(&self) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            Project::init(folder)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "List open projects",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}

    impl List {
        pub fn run(&self, projects: &mut Projects) -> Result<Option<(String, String)>> {
            Ok(Some((
                "json".into(),
                format!("{:?}", projects.list()?.keys()),
            )))
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Open a project",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Open {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Open {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            projects.open(folder, config, true)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Close a project",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Close {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Close {
        pub fn run(&self, projects: &mut Projects) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            projects.close(folder)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Show a project details",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,

        /// The format to display the project in
        #[structopt(short, long, default_value = "md", possible_values = ShowFormat::VARIANTS, case_insensitive = true)]
        pub format: String,
    }

    impl Show {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { folder, format } = self;
            let format = ShowFormat::from_str(&format)?;
            let content = projects.open(folder, config, false)?.show(format)?;
            Ok(Some(content))
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get the JSON Schema for projects",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Schema {}

    impl Schema {
        pub fn run(&self) -> Result<Option<(String, String)>> {
            Ok(Some(("json".into(), Project::schema())))
        }
    }
}
<commit_msg>chore(Projects): Remove unecessary syntax<commit_after>use crate::files::Files;
use eyre::{bail, Result};
use regex::Regex;
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{
    collections::{hash_map::Entry, HashMap},
    fs,
    path::{Path, PathBuf},
    str::FromStr,
};
use strum::{EnumString, EnumVariantNames, ToString, VariantNames};

/// # Details of a project
///
/// An implementation, and extension, of schema.org [`Project`](https://schema.org/Project).
/// Uses schema.org properties where possible but adds extension properties
/// where needed (e.g. `theme`).
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct Project {
    /// The name of the project
    name: Option<String>,

    /// A description of the project
    description: Option<String>,

    /// The path (within the project) of the project's image
    ///
    /// If not specified, will default to the most recently
    /// modified image in the project (if any).
    image: Option<String>,

    /// The path (within the project) of the project's main file
    ///
    /// If not specified, will default to the first file matching the
    /// the regular expression in the configuration settings.
    main: Option<String>,

    /// The default theme to use when viewing documents in this project
    ///
    /// If not specified, will default to the default theme in the
    /// configuration settings.
    theme: Option<String>,

    // The following properties are derived from the filesystem
    // and should never be read from, or written to, the `project.json` file
    /// The filesystem path of the project folder
    #[serde(skip_deserializing)]
    path: PathBuf,

    /// The resolved path of the project's image file
    #[serde(skip_deserializing)]
    image_path: Option<PathBuf>,

    /// The resolved path of the project's main file
    #[serde(skip_deserializing)]
    main_path: Option<PathBuf>,

    /// The files in the project folder
    #[serde(skip_deserializing)]
    files: Files,
}

/// A format to display a plugin using
#[derive(Debug, EnumString, EnumVariantNames, PartialEq, ToString)]
#[strum(serialize_all = "lowercase")]
pub enum ShowFormat {
    Md,
    Toml,
    Json,
}

impl Project {
    /// The name of the project's manifest file within the project directory
    const FILE_NAME: &'static str = "project.json";

    /// Get the JSON Schema for a project
    pub fn schema() -> String {
        let schema = schema_for!(Project);
        serde_json::to_string_pretty(&schema).unwrap()
    }

    /// Get the path to a projects' manifest file
    fn file(folder: &str) -> PathBuf {
        Path::new(folder).join(Project::FILE_NAME)
    }

    /// Read a project's manifest file
    ///
    /// If there is no manifest file, then return a default project
    fn read(folder: &str) -> Result<Project> {
        if !Path::new(folder).exists() {
            bail!("Project folder does not exist: {}", folder)
        }

        let file = Project::file(folder);
        let mut project = if file.exists() {
            let json = fs::read_to_string(file)?;
            serde_json::from_str(&json)?
        } else {
            Project::default()
        };
        project.path = Path::new(folder).canonicalize()?;

        Ok(project)
    }

    /// Write a project's manifest file
    ///
    /// If the project folder does not exist yet then it will be created
    pub fn write(folder: &str, project: &Project) -> Result<()> {
        fs::create_dir_all(folder)?;

        let file = Project::file(folder);
        let json = serde_json::to_string_pretty(project)?;
        fs::write(file, json)?;

        Ok(())
    }

    /// Initialize a project in a new, or existing, folder
    ///
    /// If the project has already been initialized (i.e. has a manifest file)
    /// then this function will do nothing.
    pub fn init(folder: &str) -> Result<()> {
        if Project::file(folder).exists() {
            return Ok(());
        }

        let project = Project::default();
        Project::write(folder, &project)?;

        Ok(())
    }

    /// Load a project including creating default values for properties
    /// where necessary
    pub fn open(folder: &str, config: &config::ProjectsConfig, watch: bool) -> Result<Project> {
        let mut project = Project::read(folder)?;

        // Get all the files in the project
        project.files = Files::load(folder, watch)?;

        // Resolve the main file path first as some of the other project properties
        // may be defined there (e.g. in the YAML header of a Markdown file)
        project.main_path = project.resolve_main_path(&config.main_patterns);

        // Name defaults to the name of the folder
        project.name = project
            .name
            .or_else(|| match Path::new(folder).components().last() {
                Some(last) => Some(last.as_os_str().to_string_lossy().to_string()),
                None => Some("Unnamed".to_string()),
            });

        // Theme defaults to the configured default
        project.theme = project.theme.or_else(|| Some(config.theme.clone()));

        Ok(project)
    }

    /// Attempt to resolve the path of the main file for a project
    ///
    /// Attempts to use the projects `main` property. If that is not specified, or
    /// there is no matching file in the project, attempts to match one of the
    /// project's files against the `main_patterns`.
    fn resolve_main_path(&self, main_patterns: &[String]) -> Option<PathBuf> {
        let files = self.files.obtain().expect("Unable to get files");

        // Check that there is a file with the specified main path
        if let Some(main) = &self.main {
            let main_path = self.path.join(main);
            if files.contains_key(&main_path) {
                return Some(main_path);
            } else {
                tracing::warn!("Project main file specified could not be found: {}", main);
                // Will attempt to find using patterns
            }
        }

        // For each `main_pattern` (in order)...
        for pattern in main_patterns {
            // Make matching case insensitive
            let re = match Regex::new(&pattern.to_lowercase()) {
                Ok(re) => re,
                Err(_) => {
                    tracing::warn!("Project main file pattern is invalid: {}", pattern);
                    continue;
                }
            };

            for file in files.values() {
                // Ignore directories
                if file.children.is_some() {
                    continue;
                }
                // Match relative path to pattern
                if let Ok(relative_path) = file.path.strip_prefix(&self.path) {
                    if re.is_match(&relative_path.to_string_lossy().to_lowercase()) {
                        return Some(file.path.clone());
                    }
                }
            }
        }

        None
    }

    /// Show a project in a format
    ///
    /// Used for displaying a project in CLI and possibly elsewhere.
    pub fn show(&self, format: ShowFormat) -> Result<(String, String)> {
        let content = match format {
            ShowFormat::Json => serde_json::to_string_pretty(self)?,
            ShowFormat::Toml => toml::to_string(self)?,
            ShowFormat::Md => {
                use handlebars::Handlebars;

                let template = r#"
# {{name}}

**Main**: {{ main }}
**Theme**: {{ theme }}

"#;
                let hb = Handlebars::new();
                hb.render_template(template.trim(), self)?
            }
        };

        Ok((format.to_string(), content))
    }
}

/// An in-memory store of projects and associated
/// data (e.g. file system watchers)
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Projects {
    /// The projects, stored by absolute path
    pub registry: HashMap<String, Project>,
}

impl Default for Projects {
    fn default() -> Self {
        Projects {
            registry: HashMap::new(),
        }
    }
}

impl Projects {
    /// Get the canonical absolute path of a project folder
    fn path(folder: &str) -> Result<String> {
        Ok(Path::new(folder).canonicalize()?.display().to_string())
    }

    /// List projects that are open
    pub fn list(&self) -> Result<HashMap<String, Project>> {
        Ok(self.registry.clone())
    }

    /// Open a project
    ///
    /// This function `loads` a project, stores it, watches the project folder,
    /// updates the project on changes and publishes the updates on the "projects"
    /// pubsub topic channel.
    pub fn open(
        &mut self,
        folder: &str,
        config: &config::ProjectsConfig,
        watch: bool,
    ) -> Result<Project> {
        let path = Projects::path(folder)?;

        let project = match self.registry.entry(path) {
            Entry::Occupied(entry) => entry.into_mut(),
            Entry::Vacant(entry) => match Project::open(folder, config, watch) {
                Ok(project) => entry.insert(project),
                Err(error) => return Err(error),
            },
        };
        Ok(project.clone())
    }

    /// Close a project
    pub fn close(&mut self, folder: &str) -> Result<()> {
        let path = Projects::path(folder)?;
        self.registry.remove(&path);
        Ok(())
    }
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use defaults::Defaults;
    use validator::Validate;

    /// # Projects
    ///
    /// Configuration settings for project defaults
    #[derive(Debug, Defaults, PartialEq, Clone, JsonSchema, Deserialize, Serialize, Validate)]
    #[serde(default, rename_all = "camelCase")]
    pub struct ProjectsConfig {
        /// Patterns used to infer the main file of projects
        ///
        /// For projects that do not specify a main file, each file is tested
        /// against these case insensitive patterns in order. The first
        /// file (alphabetically) that matches is the project's main file.
        #[def = r#"vec!["^main\\b".to_string(), "^index\\b".to_string(), "^readme\\b".to_string()]"#]
        pub main_patterns: Vec<String>,

        /// Default project theme
        ///
        /// Will be applied to all projects that do not specify a theme
        #[def = r#"String::from("stencila")"#]
        pub theme: String,
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage projects",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Init(Init),
        List(List),
        Open(Open),
        Close(Close),
        Show(Show),
        Schema(Schema),
    }

    impl Command {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { action } = self;
            match action {
                Action::Init(action) => action.run(),
                Action::List(action) => action.run(projects),
                Action::Open(action) => action.run(projects, config),
                Action::Close(action) => action.run(projects),
                Action::Show(action) => action.run(projects, config),
                Action::Schema(action) => action.run(),
            }
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Initialize a project in a new, or existing, folder",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Init {
        /// The path of the new, or existing, folder to initialize
        ///
        /// If no folder exists at the path, then one will be created.
        /// If no `project.json` file exists in the folder then a new one
        /// will be created.
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Init {
        pub fn run(&self) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            Project::init(folder)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "List open projects",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}

    impl List {
        pub fn run(&self, projects: &mut Projects) -> Result<Option<(String, String)>> {
            Ok(Some((
                "json".into(),
                format!("{:?}", projects.list()?.keys()),
            )))
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Open a project",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Open {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Open {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            projects.open(folder, config, true)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Close a project",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Close {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,
    }

    impl Close {
        pub fn run(&self, projects: &mut Projects) -> Result<Option<(String, String)>> {
            let Self { folder } = self;
            projects.close(folder)?;
            Ok(None)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Show a project details",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The path of the project folder
        #[structopt(default_value = ".")]
        pub folder: String,

        /// The format to display the project in
        #[structopt(short, long, default_value = "md", possible_values = ShowFormat::VARIANTS, case_insensitive = true)]
        pub format: String,
    }

    impl Show {
        pub fn run(
            &self,
            projects: &mut Projects,
            config: &config::ProjectsConfig,
        ) -> Result<Option<(String, String)>> {
            let Self { folder, format } = self;
            let format = ShowFormat::from_str(&format)?;
            let content = projects.open(folder, config, false)?.show(format)?;
            Ok(Some(content))
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get the JSON Schema for projects",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Schema {}

    impl Schema {
        pub fn run(&self) -> Result<Option<(String, String)>> {
            Ok(Some(("json".into(), Project::schema())))
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::env;

fn main() {
    let mut files = Vec::new();

    // The first file we want to write to is stdout
    match File::create("/dev/stdout") {
        Ok(f) => {
            files.push(f);
        }
        Err(error) => {
            println!("Error when creating file for writing: {}", error);
        }
    }

    // Create any additional files specified
    for file_name in env::args() {
        match File::create(file_name) {
            Ok(f) => {
                files.push(f);
            }
            Err(error) => {
                println!("Error when creating file for writing: {}", error);
            }
        }
    }

    // Read from standard input and write to all the files
    let mut line = String::new();
    loop {
        match io::stdin().read_line(&mut line) {
            Ok(n) => {
                // Have we read all the lines, if yes, break
                if n == 0 {
                    break;
                }
                for f in &mut files{
                    match f.write(line.as_bytes()) {
                        Ok(_) => {}
                        Err(error) => {
                            println!("Error when writing to file: {}", error);
                        }
                    }
                }
                // Clear the line we just read so that we don't keep
                // appending
                line.clear();
            }
            Err(error) => {
                println!("Error reading from stdin : {}", error);
            }
        }
    }
}
<commit_msg>Rust: import the Write trait instead of using prelude<commit_after>use std::io;
use std::io::Write;
use std::fs::File;
use std::env;

fn main() {
    let mut files = Vec::new();

    // The first file we want to write to is stdout
    match File::create("/dev/stdout") {
        Ok(f) => {
            files.push(f);
        }
        Err(error) => {
            println!("Error when creating file for writing: {}", error);
        }
    }

    // Create any additional files specified
    for file_name in env::args() {
        match File::create(file_name) {
            Ok(f) => {
                files.push(f);
            }
            Err(error) => {
                println!("Error when creating file for writing: {}", error);
            }
        }
    }

    // Read from standard input and write to all the files
    let mut line = String::new();
    loop {
        match io::stdin().read_line(&mut line) {
            Ok(n) => {
                // Have we read all the lines, if yes, break
                if n == 0 {
                    break;
                }
                for f in &mut files{
                    match f.write(line.as_bytes()) {
                        Ok(_) => {}
                        Err(error) => {
                            println!("Error when writing to file: {}", error);
                        }
                    }
                }
                // Clear the line we just read so that we don't keep
                // appending
                line.clear();
            }
            Err(error) => {
                println!("Error reading from stdin : {}", error);
            }
        }
    }
}
<|endoftext|>


<commit_before>use hyper::Client as HyperClient;

use api::append_to_stream::Appender;
use api::read_stream_events_forward::Reader;
use api::delete_stream::Deleter;
use Stream;
use event::Event;
use types::Result;
use expected_version::ExpectedVersion;
use connection::ConnectionInfo;

pub struct Client {
    connection_info: ConnectionInfo,
}

impl Client {
    pub fn new(connection_info: ConnectionInfo) -> Client {
        Client { connection_info: connection_info }
    }

    pub fn default() -> Client {
        Client {
            connection_info: ConnectionInfo {
                host: "127.0.0.1".into(),
                port: 2113
            }
        }
    }

    pub fn append_to_stream<I>(&self,
                               stream_name: &str,
                               expected_version: ExpectedVersion,
                               events: I)
                               -> Result<()>
        where I: IntoIterator<Item = Event> {

        let http_client = HyperClient::default();
        let appender = Appender::new(&self.connection_info, &http_client);
        appender.append(stream_name, expected_version, events)
    }

    //TODO Restrict `count` using u8 or u16
    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let http_client = HyperClient::default();
        let reader = Reader::new(&self.connection_info, &http_client);
        reader.read_stream_events_forward(stream_name, start, count, resolve_link_tos)
    }

    pub fn delete_stream(&self,
                         stream_name: &str,
                         expected_version: ExpectedVersion)
                         -> Result<()> {
        let http_client = HyperClient::default();
        let deleter = Deleter::new(&self.connection_info, &http_client);
        deleter.delete(stream_name, expected_version)
    }

    pub fn hard_delete_stream(&self,
                              stream_name: &str,
                              expected_version: ExpectedVersion)
                              -> Result<()> {
        let http_client = HyperClient::default();
        let deleter = Deleter::new(&self.connection_info, &http_client);
        deleter.hard_delete(stream_name, expected_version)
    }
}
<commit_msg>Do refactoring<commit_after>use hyper::Client as HyperClient;

use api::append_to_stream::Appender;
use api::read_stream_events_forward::Reader;
use api::delete_stream::Deleter;
use Stream;
use event::Event;
use types::Result;
use expected_version::ExpectedVersion;
use connection::ConnectionInfo;

pub struct Client {
    connection_info: ConnectionInfo,
    http_client: HyperClient,
}

impl Client {
    pub fn new(connection_info: ConnectionInfo) -> Client {
        Client { connection_info: connection_info, http_client: HyperClient::default() }
    }

    pub fn default() -> Client {
        Client {
            connection_info: ConnectionInfo {
                host: "127.0.0.1".into(),
                port: 2113
            },
            http_client: HyperClient::default()
        }
    }

    pub fn append_to_stream<I>(&self,
                               stream_name: &str,
                               expected_version: ExpectedVersion,
                               events: I)
                               -> Result<()>
        where I: IntoIterator<Item = Event> {
        let appender = Appender::new(&self.connection_info, &self.http_client);
        appender.append(stream_name, expected_version, events)
    }

    //TODO Restrict `count` using u8 or u16
    pub fn read_stream_events_forward(&self,
                                      stream_name: &str,
                                      start: u32,
                                      count: u32,
                                      resolve_link_tos: bool)
                                      -> Result<Stream> {
        let reader = Reader::new(&self.connection_info, &self.http_client);
        reader.read_stream_events_forward(stream_name, start, count, resolve_link_tos)
    }

    pub fn delete_stream(&self,
                         stream_name: &str,
                         expected_version: ExpectedVersion)
                         -> Result<()> {
        let deleter = Deleter::new(&self.connection_info, &self.http_client);
        deleter.delete(stream_name, expected_version)
    }

    pub fn hard_delete_stream(&self,
                              stream_name: &str,
                              expected_version: ExpectedVersion)
                              -> Result<()> {
        let deleter = Deleter::new(&self.connection_info, &self.http_client);
        deleter.hard_delete(stream_name, expected_version)
    }
}
<|endoftext|>


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

use std::mem;
use std::cell::UnsafeCell;

pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub unsafe fn uninitialized() -> ReentrantMutex {
        mem::uninitialized()
    }

    //pub unsafe fn init(&mut self) -> ReentrantMutex {
    //    ffi::InitializeCriticalSection(self.inner.get());
    //}
    pub unsafe fn init(&mut self) {
        ffi::InitializeCriticalSection(self.inner.get());
    }

    pub unsafe fn lock(&self) {
        ffi::EnterCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        ffi::TryEnterCriticalSection(self.inner.get()) != 0
    }

    pub unsafe fn unlock(&self) {
        ffi::LeaveCriticalSection(self.inner.get());
    }

    pub unsafe fn destroy(&self) {
        ffi::DeleteCriticalSection(self.inner.get());
    }
}

mod ffi {
    use libc::{LPVOID, LONG, HANDLE, c_ulong, BOOLEAN};
    #[allow(non_camel_case_types)]
    pub type ULONG_PTR = c_ulong;

    #[allow(non_snake_case)]
    #[repr(C)]
    pub struct CRITICAL_SECTION {
        CriticalSectionDebug: LPVOID,
        LockCount: LONG,
        RecursionCount: LONG,
        OwningThread: HANDLE,
        LockSemaphore: HANDLE,
        SpinCount: ULONG_PTR
    }

    extern "system" {
        pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
        pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
    }
}
<commit_msg>+ inline attribute for winapi wrapper<commit_after>// Copyright 2015 The Rust Project Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::mem;
use std::cell::UnsafeCell;

pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    #[inline]
    pub unsafe fn uninitialized() -> ReentrantMutex {
        mem::uninitialized()
    }

    #[inline]
    pub unsafe fn init(&mut self) {
        ffi::InitializeCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn lock(&self) {
        ffi::EnterCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        ffi::TryEnterCriticalSection(self.inner.get()) != 0
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        ffi::LeaveCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn destroy(&self) {
        ffi::DeleteCriticalSection(self.inner.get());
    }
}

mod ffi {
    use libc::{LPVOID, LONG, HANDLE, c_ulong, BOOLEAN};
    #[allow(non_camel_case_types)]
    pub type ULONG_PTR = c_ulong;

    #[allow(non_snake_case)]
    #[repr(C)]
    pub struct CRITICAL_SECTION {
        CriticalSectionDebug: LPVOID,
        LockCount: LONG,
        RecursionCount: LONG,
        OwningThread: HANDLE,
        LockSemaphore: HANDLE,
        SpinCount: ULONG_PTR
    }

    extern "system" {
        pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
        pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
    }
}
<|endoftext|>


<commit_before>use serde::Deserialize;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;

#[derive(Deserialize)]
struct Sections {
    #[serde(default)]
    expand: Config,
}

#[derive(Deserialize, Default)]
pub struct Config {
    pub theme: Option<String>,
    pub color: Option<String>,
    #[serde(default)]
    pub pager: bool,
}

pub fn deserialize() -> Config {
    try_deserialize().unwrap_or_default()
}

fn try_deserialize() -> Option<Config> {
    let cargo_home = env::var_os("CARGO_HOME").map(PathBuf::from)?;
    let config_names = ["config", "config.toml"];
    let config_path = config_names
        .iter()
        .map(|name| cargo_home.join(name))
        .find(|path| path.exists())?;

    let content = fs::read(&config_path).ok()?;

    let full_config: Sections = match toml::from_slice(&content) {
        Ok(config) => config,
        Err(err) => {
            let _ = writeln!(
                io::stderr(),
                "Warning: {}: {}",
                config_path.display(),
                err
            );
            return None;
        }
    };

    Some(full_config.expand)
}
<commit_msg>Format with rustfmt 1.4.38<commit_after>use serde::Deserialize;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;

#[derive(Deserialize)]
struct Sections {
    #[serde(default)]
    expand: Config,
}

#[derive(Deserialize, Default)]
pub struct Config {
    pub theme: Option<String>,
    pub color: Option<String>,
    #[serde(default)]
    pub pager: bool,
}

pub fn deserialize() -> Config {
    try_deserialize().unwrap_or_default()
}

fn try_deserialize() -> Option<Config> {
    let cargo_home = env::var_os("CARGO_HOME").map(PathBuf::from)?;
    let config_names = ["config", "config.toml"];
    let config_path = config_names
        .iter()
        .map(|name| cargo_home.join(name))
        .find(|path| path.exists())?;

    let content = fs::read(&config_path).ok()?;

    let full_config: Sections = match toml::from_slice(&content) {
        Ok(config) => config,
        Err(err) => {
            let _ = writeln!(io::stderr(), "Warning: {}: {}", config_path.display(), err);
            return None;
        }
    };

    Some(full_config.expand)
}
<|endoftext|>


<commit_before>use std::io::{File, Open, Read, Write};
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Decoder, Json};

#[derive(Show, RustcEncodable, RustcDecodable)]
pub struct Config {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String
}

const PATH: &'static str = "./.cttw.conf";

pub fn read() -> Option<Config> {
    let path = Path::new(PATH);
    let mut file = match File::open_mode(&path, Open, Read) {
        Ok(f) => f,
        Err(_) => return None
    };
    let conf = Json::from_reader(&mut file).unwrap();
    Decodable::decode(&mut Decoder::new(conf)).ok()
}

pub fn write(conf: &Config) {
    let path = Path::new(PATH);
    let mut file = match File::open_mode(&path, Open, Write) {
        Ok(f) => f,
        Err(e) => panic!("{}", e)
    };
    let _ = file.write_line(&json::encode(conf)[]);
}
<commit_msg>update to rustc 1.0.0-dev (70b13a7c7 2015-01-25 00:49:41 +0000)<commit_after>use std::io::{File, Open, Read, Write};
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Decoder, Json};

#[derive(Show, RustcEncodable, RustcDecodable)]
pub struct Config {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String
}

const PATH: &'static str = "./.cttw.conf";

pub fn read() -> Option<Config> {
    let path = Path::new(PATH);
    let mut file = match File::open_mode(&path, Open, Read) {
        Ok(f) => f,
        Err(_) => return None
    };
    let conf = Json::from_reader(&mut file).unwrap();
    Decodable::decode(&mut Decoder::new(conf)).ok()
}

pub fn write(conf: &Config) {
    let path = Path::new(PATH);
    let mut file = match File::open_mode(&path, Open, Write) {
        Ok(f) => f,
        Err(e) => panic!("{}", e)
    };
    let _ = file.write_line(&json::encode(conf).unwrap()[]);
}
<|endoftext|>


<commit_before>extern crate boxcars;
extern crate failure;
extern crate globset;
extern crate rayon;
extern crate serde_json;
#[macro_use]
extern crate structopt;

use failure::{err_msg, Error, ResultExt};
use structopt::StructOpt;
use std::fs::{File, OpenOptions};
use std::io::{self, BufWriter};
use std::io::prelude::*;
use rayon::prelude::*;
use boxcars::{CrcCheck, NetworkParse, ParserBuilder, Replay};
use std::path::Path;
use globset::Glob;

#[derive(StructOpt, Debug, Clone, PartialEq)]
#[structopt(name = "rrrocket",
            about = "Parses Rocket League replay files and outputs JSON with decoded information")]
struct Opt {
    #[structopt(short = "c", long = "crc-check", help = "forces a crc check for corruption even when replay was successfully parsed")]
    crc: bool,

    #[structopt(short = "n", long = "network-parse", help = "parses the network data of a replay instead of skipping it")]
    body: bool,

    #[structopt(short = "m", long = "multiple", help = "parse multiple replays, instead of writing JSON to stdout, write to a sibling JSON file")]
    multiple: bool,

    #[structopt(help = "Rocket League replay files")] input: Vec<String>,
}

fn read_file(input: &str) -> Result<Vec<u8>, Error> {
    let mut f = File::open(input)
        .with_context(|e| format!("Could not open rocket league file: {} -- {}", input, e))?;
    let mut buffer = vec![];
    f.read_to_end(&mut buffer)
        .with_context(|e| format!("Could not read rocket league file: {} -- {}", input, e))?;
    Ok(buffer)
}

fn parse_replay<'a>(opt: &Opt, data: &'a [u8]) -> Result<Replay<'a>, Error> {
    ParserBuilder::new(&data[..])
        .with_crc_check(if opt.crc {
            CrcCheck::Always
        } else {
            CrcCheck::OnError
        })
        .with_network_parse(if opt.body {
            NetworkParse::Always
        } else {
            NetworkParse::Never
        })
        .parse()
}

/// Each file argument that we get could be a directory so we need to expand that directory and
/// find all the *.replay files. File arguments turn into single element vectors.
fn expand_paths(files: &[String]) -> Result<Vec<Vec<String>>, Error> {
    let glob = Glob::new("*.replay")?.compile_matcher();

    files
        .iter()
        .map(|x| {
            let p = Path::new(x);
            if p.is_dir() {
                // If the commandline argument is a directory we look for all files that match
                // *.replay. A file that does not match the pattern because of an error reading the
                // directory / file will not be filtered and will cause the error to bubble up. In
                // the future, we could get smart and ignore directories / files we don't have
                // permission that wouldn't match the pattern anyways
                let files: Result<Vec<_>, _> = p.read_dir()?
                    .filter_map(|entry| {
                        match entry {
                            Ok(y) => {
                                if glob.is_match(y.path()) {
                                    // Force UTF-8. There is a special place in the fourth circle
                                    // of inferno for people who rename their rocket league replays
                                    // to not contain UTF-8. We won't panic, but will cause an
                                    // error when the file is attempted to be read.
                                    Some(Ok(y.path().to_string_lossy().into_owned()))
                                } else {
                                    None
                                }
                            }
                            Err(e) => Some(Err(e)),
                        }
                    })
                    .collect();
                Ok(files?)
            } else {
                Ok(vec![x.clone()])
            }
        })
        .collect()
}

fn parse_multiple_replays(opt: &Opt) -> Result<(), Error> {
    let res: Result<Vec<()>, Error> = expand_paths(&opt.input)?
        .into_iter()
        .flat_map(|x| x)
        .collect::<Vec<_>>()
        .par_iter()
        .map(|file| {
            let outfile = format!("{}.json", file);
            let fout = OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(true)
                .open(&outfile)
                .with_context(|e| {
                    format!(
                        "Could not open json output file: {} with error: {}",
                        outfile, e
                    )
                })?;
            let mut writer = BufWriter::new(fout);
            let data = read_file(file)?;
            let replay = parse_replay(opt, &data[..])
                .with_context(|e| format!("Could not parse: {} with error: {}", file, e))?;
            serde_json::to_writer(&mut writer, &replay).with_context(|e| {
                format!("Could not serialize replay {} to {}: {}", file, outfile, e)
            })?;
            Ok(())
        })
        .collect();
    res?;
    Ok(())
}

fn run() -> Result<(), Error> {
    let opt = Opt::from_args();
    if opt.multiple {
        parse_multiple_replays(&opt)
    } else if opt.input.len() != 1 {
        Err(err_msg(
            "Expected one input file if --multiple is not specified",
        ))
    } else {
        let file = &opt.input[0];
        let data = read_file(file)?;
        let replay = parse_replay(&opt, &data[..]).context("Could not parse replay")?;
        serde_json::to_writer(&mut io::stdout(), &replay).context("Could not serialize replay")?;
        Ok(())
    }
}

fn main() {
    if let Err(ref e) = run() {
        let mut stderr = io::stderr();
        for fail in e.causes() {
            let _ = writeln!(stderr, "{}", fail);
        }

        ::std::process::exit(1);
    }
}
<commit_msg>Add dry run option to rrrocket<commit_after>extern crate boxcars;
extern crate failure;
extern crate globset;
extern crate rayon;
extern crate serde_json;
#[macro_use]
extern crate structopt;

use failure::{err_msg, Error, ResultExt};
use structopt::StructOpt;
use std::fs::{File, OpenOptions};
use std::io::{self, BufWriter};
use std::io::prelude::*;
use rayon::prelude::*;
use boxcars::{CrcCheck, NetworkParse, ParserBuilder, Replay};
use std::path::Path;
use globset::Glob;

#[derive(StructOpt, Debug, Clone, PartialEq)]
#[structopt(name = "rrrocket",
            about = "Parses Rocket League replay files and outputs JSON with decoded information")]
struct Opt {
    #[structopt(short = "c", long = "crc-check", help = "forces a crc check for corruption even when replay was successfully parsed")]
    crc: bool,

    #[structopt(short = "n", long = "network-parse", help = "parses the network data of a replay instead of skipping it")]
    body: bool,

    #[structopt(short = "m", long = "multiple", help = "parse multiple replays, instead of writing JSON to stdout, write to a sibling JSON file")]
    multiple: bool,

    #[structopt(long = "dry-run", help = "parses but does not write JSON output")]
    dry_run: bool,

    #[structopt(help = "Rocket League replay files")] input: Vec<String>,
}

fn read_file(input: &str) -> Result<Vec<u8>, Error> {
    let mut f = File::open(input)
        .with_context(|e| format!("Could not open rocket league file: {} -- {}", input, e))?;
    let mut buffer = vec![];
    f.read_to_end(&mut buffer)
        .with_context(|e| format!("Could not read rocket league file: {} -- {}", input, e))?;
    Ok(buffer)
}

fn parse_replay<'a>(opt: &Opt, data: &'a [u8]) -> Result<Replay<'a>, Error> {
    ParserBuilder::new(&data[..])
        .with_crc_check(if opt.crc {
            CrcCheck::Always
        } else {
            CrcCheck::OnError
        })
        .with_network_parse(if opt.body {
            NetworkParse::Always
        } else {
            NetworkParse::Never
        })
        .parse()
}

/// Each file argument that we get could be a directory so we need to expand that directory and
/// find all the *.replay files. File arguments turn into single element vectors.
fn expand_paths(files: &[String]) -> Result<Vec<Vec<String>>, Error> {
    let glob = Glob::new("*.replay")?.compile_matcher();

    files
        .iter()
        .map(|x| {
            let p = Path::new(x);
            if p.is_dir() {
                // If the commandline argument is a directory we look for all files that match
                // *.replay. A file that does not match the pattern because of an error reading the
                // directory / file will not be filtered and will cause the error to bubble up. In
                // the future, we could get smart and ignore directories / files we don't have
                // permission that wouldn't match the pattern anyways
                let files: Result<Vec<_>, _> = p.read_dir()?
                    .filter_map(|entry| {
                        match entry {
                            Ok(y) => {
                                if glob.is_match(y.path()) {
                                    // Force UTF-8. There is a special place in the fourth circle
                                    // of inferno for people who rename their rocket league replays
                                    // to not contain UTF-8. We won't panic, but will cause an
                                    // error when the file is attempted to be read.
                                    Some(Ok(y.path().to_string_lossy().into_owned()))
                                } else {
                                    None
                                }
                            }
                            Err(e) => Some(Err(e)),
                        }
                    })
                    .collect();
                Ok(files?)
            } else {
                Ok(vec![x.clone()])
            }
        })
        .collect()
}

fn parse_multiple_replays(opt: &Opt) -> Result<(), Error> {
    let res: Result<Vec<()>, Error> = expand_paths(&opt.input)?
        .into_iter()
        .flat_map(|x| x)
        .collect::<Vec<_>>()
        .par_iter()
        .map(|file| {
            let data = read_file(file)?;
            let replay = parse_replay(opt, &data[..])
                .with_context(|e| format!("Could not parse: {} with error: {}", file, e))?;

            if !opt.dry_run {
                let outfile = format!("{}.json", file);
                let fout = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .truncate(true)
                    .open(&outfile)
                    .with_context(|e| {
                        format!(
                            "Could not open json output file: {} with error: {}",
                            outfile, e
                        )
                    })?;

                let mut writer = BufWriter::new(fout);
                serde_json::to_writer(&mut writer, &replay)
                    .with_context(|e| format!("Could not serialize replay {}: {}", file, e))?;
            }
            Ok(())
        })
        .collect();
    res?;
    Ok(())
}

fn run() -> Result<(), Error> {
    let opt = Opt::from_args();
    if opt.multiple {
        parse_multiple_replays(&opt)
    } else if opt.input.len() != 1 {
        Err(err_msg(
            "Expected one input file if --multiple is not specified",
        ))
    } else {
        let file = &opt.input[0];
        let data = read_file(file)?;
        let replay = parse_replay(&opt, &data[..]).context("Could not parse replay")?;
        if !opt.dry_run {
            serde_json::to_writer(&mut io::stdout(), &replay)
                .context("Could not serialize replay")?;
        }
        Ok(())
    }
}

fn main() {
    if let Err(ref e) = run() {
        let mut stderr = io::stderr();
        for fail in e.causes() {
            let _ = writeln!(stderr, "{}", fail);
        }

        ::std::process::exit(1);
    }
}
<|endoftext|>


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

use super::scheduler::*;

use std::sync::Arc;
use futures::channel::oneshot;
use futures::future::Future;

///
/// 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:   Box<T>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send> Sync 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>(*const 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::new(data)
        }
    }

    ///
    /// 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(&*self.data);

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            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 = unsafe {
            // 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(&*self.data);

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

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    pub fn future<TFn, Item: 'static+Send>(&self, job: TFn) -> impl Future<Output=Result<Item, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T) -> Item {
        let (send, receive) = oneshot::channel();

        self.desync(|data| {
            let result = job(data);

            if let Err(_result) = send.send(result) {
                // The listening side disconnected: we'll throw away the result and act like a normal desync instead
            }
        });

        receive
    }

    ///
    /// 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: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
        // Also, we'll have exclusive access to this object when the callback occurs
        let data = DataRef(&*self.data);

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

impl<T: Send> Drop for Desync<T> {
    fn drop(&mut self) {
        // 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)
        sync(&self.queue, || {});
    }
}
<commit_msg>Add a new version of Future that uses desync's ordering guarantees to provide access to the data for the lifetime of the future that's returned.<commit_after>//!
//! The main `Desync` struct
//!

use super::scheduler::*;

use std::sync::Arc;
use std::ops::{Deref, DerefMut};
use std::marker::{PhantomData};
use futures::channel::oneshot;
use futures::future::{Future, FutureObj};

///
/// 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:   Box<T>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send> Sync 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>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

///
/// A mutable reference to the data contained within a desync, used to retrieve or modify its value during a future
///
pub struct DesyncRef<'a, T: Send> {
    data: DataRef<T>,
    phantom_lifetime: PhantomData<&'a T>
}

// Desync guarantees that while the future is running, nothing else can access the reference (it does this through ordering operations
// rather than mutual exclusion)
unsafe impl<'a, T: Send> Send for DesyncRef<'a, T> {}

impl<'a, T: Send> Deref for DesyncRef<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.data.0 }
    }
}

impl<'a, T: Send> DerefMut for DesyncRef<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.data.0 as *mut 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::new(data)
        }
    }

    ///
    /// 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(&*self.data);

        desync(&self.queue, move || {
            let data = data.0 as *mut T;
            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 = unsafe {
            // 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(&*self.data);

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

        result
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    pub fn future2<TFn, Item>(&self, job: TFn) -> impl Future<Output=Result<Item, oneshot::Canceled>>+Send
    where   for<'a> TFn:    'static+Send+FnOnce(DesyncRef<'a, T>) -> FutureObj<'a, Item>,
            Item:           'static+Send {
        let data = DataRef(&*self.data);
        let data = DesyncRef { data: data, phantom_lifetime: PhantomData };

        scheduler().future(&self.queue, move || {
            async {
                let job     = job(data);
                job.await
            }
        })
    }

    ///
    /// Performs an operation asynchronously on the contents of this item, returning the
    /// result via a future.
    ///
    pub fn future<TFn, Item: 'static+Send>(&self, job: TFn) -> impl Future<Output=Result<Item, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T) -> Item {
        let (send, receive) = oneshot::channel();

        self.desync(|data| {
            let result = job(data);

            if let Err(_result) = send.send(result) {
                // The listening side disconnected: we'll throw away the result and act like a normal desync instead
            }
        });

        receive
    }

    ///
    /// 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: 'static+Send, Fut: 'static+Future+Send>(&self, after: Fut, job: TFn) -> impl 'static+Future<Output=Result<Res, oneshot::Canceled>>+Send
    where TFn: 'static+Send+FnOnce(&mut T, Fut::Output) -> Res {
        // As drop() is the last thing called, we know that this object will still exist at the point where the callback occurs
        // Also, we'll have exclusive access to this object when the callback occurs
        let data = DataRef(&*self.data);

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

impl<T: Send> Drop for Desync<T> {
    fn drop(&mut self) {
        // 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)
        sync(&self.queue, || {});
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::time::{Duration, Instant};

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use sdl2::pixels::Color;
use sdl2::rect::Rect; // TODO: abstract my own drawtypes?

use graphics::Display;
use input::Input;

pub static COLOR_BG: Color  = Color::RGB(0,0,0);
pub static COLOR_FPS: Color = Color::RGB(255,255,0);
pub static COLOR_PEN: Color = Color::RGB(125, 0, 175);

pub struct Engine {
	context:     sdl2::Sdl,
	controller:  Input,
	display:     Display,

    cursor: (i32,i32),
}

impl Engine {
    pub fn new(context: sdl2::Sdl) -> Engine {
        let video_renderer = Display::new(&context);

        Engine {
            context:    context,
            controller: Input::new(),
            display:    video_renderer,

            cursor: (0,0),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms  = Duration::from_millis(1000 / 120); // TODO: const fn?

		let mut event_pump = self.context.event_pump().unwrap();
        let mut is_running = true;

        let mut frame_start_at;
        let mut elapsed_time = Duration::from_millis(0);

        // drawing state
        let mut bitmap = Some(self.display.get_texture(1280,720));
        let mut mouse_clicked = false;
        let mut last_point = None;

        // init bitmap to good state
        let mut _last = self.display.retarget().set(bitmap.take().unwrap());
        self.display.clear_buffer();
        bitmap = self.display.retarget().reset()
            .ok().expect("did not get target back");

        while is_running {
            frame_start_at  = Instant::now();

			// drain input event queue once per frame
			self.controller.begin_new_frame();
			for event in event_pump.poll_iter() {
				match event {
					Event::KeyDown { keycode, .. } => {
						self.controller.key_down_event(keycode.unwrap());
					},

					Event::KeyUp { keycode, .. } => {
						self.controller.key_up_event(keycode.unwrap());
					},

                    Event::MouseMotion { x, y, .. } => self.cursor = (x,y),

                    Event::MouseButtonDown { .. } => mouse_clicked = true,
                    Event::MouseButtonUp   { .. } => mouse_clicked = false,

					_ => {},
				}
			}

            // handle exit game
			if self.controller.was_key_released(Keycode::Escape) { is_running = false; }

            // erase canvas
            if self.controller.was_key_released(Keycode::E) {
                let mut _last = self.display.retarget().set(bitmap.take().unwrap());
                self.display.clear_buffer();
                bitmap = self.display.retarget().reset()
                    .ok().expect("did not get target back");
            }

            if mouse_clicked {
                let (x2,y2) = self.cursor;

                if let Some((x1,y1)) = last_point {
                    let _last = self.display.retarget().set(bitmap.take().unwrap());

                    // let diffx = x2 - x1;
                    // let diffy = y2 - y1;
                    // println!("delta ({},{}), mag: {}", diffx, diffy, diffy-diffx);

                    for i in 0..10 {
                        self.display.draw_line(x1+i, y1, x2, y2+i, COLOR_PEN);
                    }

                    bitmap = self.display.retarget().reset()
                        .ok().expect("did not get target back");
                } // NOTE: doesn't draw point if mouse held for single frame

                last_point = Some((x2,y2));
           } else { last_point = None; }

            // handle draw calls
			self.display.clear_buffer(); // clear back-buffer
            self.display.copy(bitmap.as_ref().unwrap());
            self.draw_cursor();
            self.draw_debug(elapsed_time);
			self.display.switch_buffers();

            // sleep for <target> - <draw time> and floor to zero
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    fn draw_cursor(&mut self) {
        self.display.fill_rect(Rect::new(self.cursor.0, self.cursor.1, 10, 10), COLOR_PEN);
    }

    fn draw_debug(&mut self, time: Duration) {
        let mut time_ms = time.as_secs() * 1000;        // -> millis
        time_ms += time.subsec_nanos() as u64 / (1000 * 1000); // /> micros /> millis

        let buf = format!("{}ms, e = erase all", time_ms);
        self.display.blit_text(&buf[..], COLOR_FPS);
    }
}
<commit_msg>moar debug tools<commit_after>use std::thread;
use std::time::{Duration, Instant};

use sdl2;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use sdl2::pixels::Color;
use sdl2::rect::Rect; // TODO: abstract my own drawtypes?

use graphics::Display;
use input::Input;

pub static COLOR_BG: Color  = Color::RGB(0,0,0);
pub static COLOR_FPS: Color = Color::RGB(255,255,0);
pub static COLOR_PEN: Color = Color::RGB(125, 0, 175);

#[derive(Debug)]
enum BrushMode {
    Squareish,
    WowSoEdgy,
}

pub struct Engine {
	context:     sdl2::Sdl,
	controller:  Input,
	display:     Display,

    brush:  BrushMode,
    cursor: (i32,i32),
}

impl Engine {
    pub fn new(context: sdl2::Sdl) -> Engine {
        let video_renderer = Display::new(&context);

        Engine {
            context:    context,
            controller: Input::new(),
            display:    video_renderer,

            brush:  BrushMode::Squareish,
            cursor: (0,0),
        }
    }

    pub fn run(&mut self) {
        let target_fps_ms  = Duration::from_millis(1000 / 120); // TODO: const fn?

		let mut event_pump = self.context.event_pump().unwrap();
        let mut is_running = true;

        let mut frame_start_at;
        let mut elapsed_time = Duration::from_millis(0);

        // drawing state
        let mut bitmap = Some(self.display.get_texture(1280,720));
        let mut mouse_clicked = false;
        let mut last_point = None;

        // init bitmap to good state
        let mut _last = self.display.retarget().set(bitmap.take().unwrap());
        self.display.clear_buffer();
        bitmap = self.display.retarget().reset()
            .ok().expect("did not get target back");

        while is_running {
            frame_start_at  = Instant::now();

			// drain input event queue once per frame
			self.controller.begin_new_frame();
			for event in event_pump.poll_iter() {
				match event {
					Event::KeyDown { keycode, .. } => {
						self.controller.key_down_event(keycode.unwrap());
					},

					Event::KeyUp { keycode, .. } => {
						self.controller.key_up_event(keycode.unwrap());
					},

                    Event::MouseMotion { x, y, .. } => self.cursor = (x,y),

                    Event::MouseButtonDown { .. } => mouse_clicked = true,
                    Event::MouseButtonUp   { .. } => mouse_clicked = false,

					_ => {},
				}
			}

            // handle exit game
			if self.controller.was_key_released(Keycode::Escape) { is_running = false; }

            // erase canvas
            if self.controller.was_key_released(Keycode::E) {
                let mut _last = self.display.retarget().set(bitmap.take().unwrap());
                self.display.clear_buffer();
                bitmap = self.display.retarget().reset()
                    .ok().expect("did not get target back");
            }

            // switch brush
            if self.controller.was_key_released(Keycode::B) {
                self.brush = match self.brush { // TODO: better cycle?
                    BrushMode::Squareish => BrushMode::WowSoEdgy,
                    BrushMode::WowSoEdgy => BrushMode::Squareish,
                }
            }

            if mouse_clicked {
                let (x2,y2) = self.cursor;

                if let Some((x1,y1)) = last_point {
                    let _last = self.display.retarget().set(bitmap.take().unwrap());

                    // let diffx = x2 - x1;
                    // let diffy = y2 - y1;
                    // println!("delta ({},{}), mag: {}", diffx, diffy, diffy-diffx);

                    match self.brush {
                        BrushMode::WowSoEdgy => for i in 0..10 {
                            self.display.draw_line(x1+i, y1, x2, y2+i, COLOR_PEN);
                        },

                        BrushMode::Squareish => for i in 0..5 {
                            self.display.draw_line(x1+i, y1, x2+i, y2, COLOR_PEN);
                        },
                    }

                    bitmap = self.display.retarget().reset()
                        .ok().expect("did not get target back");
                } // NOTE: doesn't draw point if mouse held for single frame

                last_point = Some((x2,y2));
           } else { last_point = None; }

            // handle draw calls
			self.display.clear_buffer(); // clear back-buffer
            self.display.copy(bitmap.as_ref().unwrap());
            self.draw_cursor();
            self.draw_debug(elapsed_time);
			self.display.switch_buffers();

            // sleep for <target> - <draw time> and floor to zero
            elapsed_time = frame_start_at.elapsed();
            let sleep_time = if elapsed_time > target_fps_ms {
                Duration::from_millis(0)
            } else { target_fps_ms - elapsed_time };

            thread::sleep(sleep_time);
        }
    }

    fn draw_cursor(&mut self) {
        self.display.fill_rect(Rect::new(self.cursor.0, self.cursor.1, 10, 10), COLOR_PEN);
    }

    fn draw_debug(&mut self, time: Duration) {
        let mut time_ms = time.as_secs() * 1000;        // -> millis
        time_ms += time.subsec_nanos() as u64 / (1000 * 1000); // /> micros /> millis

        let buf = format!("{}ms, e = erase all, b = brush ({:?})", time_ms, self.brush);
        self.display.blit_text(&buf[..], COLOR_FPS);
    }
}
<|endoftext|>


<commit_before>//! A solver for the Travelling Salesman Problem.
//!
//! Based on code developed at ETH by Christoph von Praun, Florian
//! Schneider, Nicholas Matsakis, and Thomas Gross.

#![allow(dead_code)]

use docopt::Docopt;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::time::Instant;

#[cfg(test)] mod bench;
mod graph;
mod tour;
mod step;
mod solver;
mod parser;
mod weight;

use self::graph::{Graph, Node};
use self::solver::SolverCx;

const USAGE: &'static str = "
Usage: tsp bench [--seq-threshold N] [--from N] <datafile>

Parallel traveling salesman problem solver. Data input is expected to
be in TSPLIB format.

Suggested command:
    cargo run --release -- tsp bench data/tsp/dj15.tsp --seq-threshold 8

Commands:
    bench              Run the benchmark and print the timings.

Options:
    -h, --help         Show this message.
    --seq-threshold N  Adjust sequential fallback threshold [default: 10].
                       Fall back to seq search when there are N or fewer nodes remaining.
                       Lower values of N mean more parallelism.
    --from N           Node index from which to start the search [default: 0].
";

#[derive(RustcDecodable)]
pub struct Args {
    cmd_bench: bool,
    arg_datafile: String,
    flag_seq_threshold: usize,
    flag_from: usize,
}

pub fn main(args: &[String]) {
    let args: Args =
        Docopt::new(USAGE)
            .and_then(|d| d.argv(args).decode())
            .unwrap_or_else(|e| e.exit());

    if args.cmd_bench {
        let _ = run_solver(Path::new(&args.arg_datafile), args.flag_seq_threshold, args.flag_from);
    }
}

fn run_solver(datafile: &Path, seq_threshold: usize, from: usize) -> Result<(), ()> {
    let graph = match parse_solver(datafile) {
        Ok(g) => g,
        Err(e) => {
            println!("failed to parse `{}`: {}", datafile.display(), e);
            return Err(());
        }
    };

    println!("Graph size   : {} nodes.", graph.num_nodes());
    println!("Seq threshold: {} nodes.", seq_threshold);

    if from >= graph.num_nodes() {
        println!("Invalid node index given for `--from`: {}", from);
        return Err(());
    }

    let mut solver = SolverCx::new(&graph, seq_threshold);
    let par_start = Instant::now();
    solver.search_from(Node::new(from));
    let par_time = par_start.elapsed();

    let (path, weight) = solver.into_result();

    println!("Total search time: {:?}", par_time);
    if let Some(path) = path {
        println!("Cheapest path cost: {}", weight.to_usize());
        let mut output = format!("Cheapest path:");
        for node in path {
            output.push_str(&format!(" {}", node.index()));
        }
        println!("{}", output);
    } else {
        println!("No path found.");
    }

    Ok(())
}

fn parse_solver(datafile: &Path) -> Result<Graph, Box<Error>> {
    let mut file = try!(File::open(datafile));
    let mut text = String::new();
    try!(file.read_to_string(&mut text));
    let graph = try!(parser::parse_tsp_data(&text));
    Ok(graph)
}
<commit_msg>Fix example to use `?` instead of `try!`<commit_after>//! A solver for the Travelling Salesman Problem.
//!
//! Based on code developed at ETH by Christoph von Praun, Florian
//! Schneider, Nicholas Matsakis, and Thomas Gross.

#![allow(dead_code)]

use docopt::Docopt;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::time::Instant;

#[cfg(test)] mod bench;
mod graph;
mod tour;
mod step;
mod solver;
mod parser;
mod weight;

use self::graph::{Graph, Node};
use self::solver::SolverCx;

const USAGE: &'static str = "
Usage: tsp bench [--seq-threshold N] [--from N] <datafile>

Parallel traveling salesman problem solver. Data input is expected to
be in TSPLIB format.

Suggested command:
    cargo run --release -- tsp bench data/tsp/dj15.tsp --seq-threshold 8

Commands:
    bench              Run the benchmark and print the timings.

Options:
    -h, --help         Show this message.
    --seq-threshold N  Adjust sequential fallback threshold [default: 10].
                       Fall back to seq search when there are N or fewer nodes remaining.
                       Lower values of N mean more parallelism.
    --from N           Node index from which to start the search [default: 0].
";

#[derive(RustcDecodable)]
pub struct Args {
    cmd_bench: bool,
    arg_datafile: String,
    flag_seq_threshold: usize,
    flag_from: usize,
}

pub fn main(args: &[String]) {
    let args: Args =
        Docopt::new(USAGE)
            .and_then(|d| d.argv(args).decode())
            .unwrap_or_else(|e| e.exit());

    if args.cmd_bench {
        let _ = run_solver(Path::new(&args.arg_datafile), args.flag_seq_threshold, args.flag_from);
    }
}

fn run_solver(datafile: &Path, seq_threshold: usize, from: usize) -> Result<(), ()> {
    let graph = match parse_solver(datafile) {
        Ok(g) => g,
        Err(e) => {
            println!("failed to parse `{}`: {}", datafile.display(), e);
            return Err(());
        }
    };

    println!("Graph size   : {} nodes.", graph.num_nodes());
    println!("Seq threshold: {} nodes.", seq_threshold);

    if from >= graph.num_nodes() {
        println!("Invalid node index given for `--from`: {}", from);
        return Err(());
    }

    let mut solver = SolverCx::new(&graph, seq_threshold);
    let par_start = Instant::now();
    solver.search_from(Node::new(from));
    let par_time = par_start.elapsed();

    let (path, weight) = solver.into_result();

    println!("Total search time: {:?}", par_time);
    if let Some(path) = path {
        println!("Cheapest path cost: {}", weight.to_usize());
        let mut output = format!("Cheapest path:");
        for node in path {
            output.push_str(&format!(" {}", node.index()));
        }
        println!("{}", output);
    } else {
        println!("No path found.");
    }

    Ok(())
}

fn parse_solver(datafile: &Path) -> Result<Graph, Box<Error>> {
    let mut file = File::open(datafile)?;
    let mut text = String::new();
    file.read_to_string(&mut text)?;
    let graph = parser::parse_tsp_data(&text)?;
    Ok(graph)
}
<|endoftext|>


<commit_before>// http://rosettacode.org/wiki/Events
//
// Rust uses condition variables (Condvars) for asynchronous event processing.  Each Mutex has a
// list of zero or more Condvars, which are essentially events that the task may wait on or signal
// when it holds the mutex.  When a task begins to wait on a condvar, it enters a waiting state
// until the condvar is signaled by another task, at which point it resumes processing.  Thus, each
// condvar represents an event on which a task may wait.  The one subtlety is that condvar signals
// are only received if there is actually a task waiting on the signal--see the below program for
// an example of how this may be achieved in practice.
extern crate time;

use std::sync::{Arc, Mutex, Condvar};
use std::thread::{self, spawn};
use std::time::Duration;

// Given a duration to wait before sending an event from one process to another, returns the
// elapsed time before the event was actually sent.
fn handle_event(duration: Duration) -> Duration {
    // Create a Mutex.  By default a Mutex is created with a single condition variable (condvar_id
    // 0) but it can be created with an arbitrary number using Mutex::new_with_condvars();
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair_ = pair.clone();
    let start = time::precise_time_ns();
    // Lock the mutex
    let &(ref mutex, ref cond) = &*pair;
    let mut guard = mutex.lock().unwrap();
    // Start our secondary task (which will signal our waiting main task)
    spawn(move || {
		let &(ref mutex_, ref cond_) = &*pair_;
        // Lock the mutex
        let mut guard  = mutex_.lock().unwrap();
        *guard = true;

        // Sleep for `duration`.
        thread::sleep(duration);
        // Signal the waiting mutex (equivalent to guard.cond.signal_on(0)).
        // One can also signal to all tasks on the waiting mutex with broadcast (broadcast_on(0)).
        //
        // Note that if no tasks are actually waiting yet (which is possible, if we got the lock
        // before the other task), then this might report that it failed to wake up any tasks.
        // That is why the mutex was locked before the task was spawned--we know we cannot possibly
        // get past the mutex at the top of the task until the wait() statement below is reached.
        cond_.notify_one();
        // Although we signaled the waiting mutex, it will not awaken until this guard is dropped.
    });
    // Wait for the event state to be set to signaled (equivalent to guard.cond.wait_on(0)).
	while !*guard {
		guard = cond.wait(guard).unwrap();
	}
	// Should be done signaling (i.e. we've waited for `duration`).
    let end = time::precise_time_ns();
    // When the guard exits scope, the condvar is reset.
    drop(guard);
    // Return the elapsed time
    Duration::from_millis((end - start) / 1000000)
}

pub fn main() {
    let duration = Duration::from_secs(1); // Process event after one second.
    println!("{} seconds elapsed before event triggered", handle_event(duration).as_secs());
}

#[test]
pub fn test_events() {
    if !cfg!(windows) { // overflowing on windows (bug in time library?)
        let duration = Duration::from_secs(1) / 10; // Process event after one tenth of a second.
        // Make sure it really did take at least that long for the event to be processed.

        let out = handle_event(duration);
        assert!(duration <= out);
    }
}
<commit_msg>remove windows test<commit_after>// http://rosettacode.org/wiki/Events
//
// Rust uses condition variables (Condvars) for asynchronous event processing.  Each Mutex has a
// list of zero or more Condvars, which are essentially events that the task may wait on or signal
// when it holds the mutex.  When a task begins to wait on a condvar, it enters a waiting state
// until the condvar is signaled by another task, at which point it resumes processing.  Thus, each
// condvar represents an event on which a task may wait.  The one subtlety is that condvar signals
// are only received if there is actually a task waiting on the signal--see the below program for
// an example of how this may be achieved in practice.
extern crate time;

use std::sync::{Arc, Mutex, Condvar};
use std::thread::{self, spawn};
use std::time::Duration;

// Given a duration to wait before sending an event from one process to another, returns the
// elapsed time before the event was actually sent.
fn handle_event(duration: Duration) -> Duration {
    // Create a Mutex.  By default a Mutex is created with a single condition variable (condvar_id
    // 0) but it can be created with an arbitrary number using Mutex::new_with_condvars();
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair_ = pair.clone();
    let start = time::precise_time_ns();
    // Lock the mutex
    let &(ref mutex, ref cond) = &*pair;
    let mut guard = mutex.lock().unwrap();
    // Start our secondary task (which will signal our waiting main task)
    spawn(move || {
		let &(ref mutex_, ref cond_) = &*pair_;
        // Lock the mutex
        let mut guard  = mutex_.lock().unwrap();
        *guard = true;

        // Sleep for `duration`.
        thread::sleep(duration);
        // Signal the waiting mutex (equivalent to guard.cond.signal_on(0)).
        // One can also signal to all tasks on the waiting mutex with broadcast (broadcast_on(0)).
        //
        // Note that if no tasks are actually waiting yet (which is possible, if we got the lock
        // before the other task), then this might report that it failed to wake up any tasks.
        // That is why the mutex was locked before the task was spawned--we know we cannot possibly
        // get past the mutex at the top of the task until the wait() statement below is reached.
        cond_.notify_one();
        // Although we signaled the waiting mutex, it will not awaken until this guard is dropped.
    });
    // Wait for the event state to be set to signaled (equivalent to guard.cond.wait_on(0)).
	while !*guard {
		guard = cond.wait(guard).unwrap();
	}
	// Should be done signaling (i.e. we've waited for `duration`).
    let end = time::precise_time_ns();
    // When the guard exits scope, the condvar is reset.
    drop(guard);
    // Return the elapsed time
    Duration::from_millis((end - start) / 1000000)
}

pub fn main() {
    let duration = Duration::from_secs(1); // Process event after one second.
    println!("{} seconds elapsed before event triggered", handle_event(duration).as_secs());
}

#[test]
pub fn test_events() {
    // Process event after one tenth of a second.
    let duration = Duration::from_secs(1) / 10;

    // Make sure it really did take at least that long for the event to be processed.
    let out = handle_event(duration);
    assert!(duration <= out);
}
<|endoftext|>