<commit_before>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

use cargo_platform::CfgExpr;
use semver::Version;

use super::BuildContext;
use crate::core::compiler::CompileKind;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Arguments needed to pass to rustdoc to run this test.
    pub args: Vec<OsString>,
    /// Whether or not -Zunstable-options is needed.
    pub unstable_opts: bool,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    /// `(package, target, path_to_test_exe)`
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: PathBuf,

    /// The path to libstd for the target
    pub target_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,
    primary_unit_rustc_process: Option<ProcessBuilder>,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(
        bcx: &BuildContext<'a, 'cfg>,
        default_kind: CompileKind,
    ) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();

            if let Some(rustc) = primary_unit_rustc_process.as_mut() {
                rustc.display_env_vars();
            }
        }

        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.info(default_kind).sysroot_host_libdir.clone(),
            target_dylib_path: bcx.info(default_kind).sysroot_target_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            primary_unit_rustc_process,
            host: bcx.host_triple().to_string(),
            target: default_kind.short_name(bcx).to_string(),
            target_runner: target_runner(bcx, default_kind)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult<ProcessBuilder> {
        let rustc = if is_primary {
            self.primary_unit_rustc_process
                .clone()
                .unwrap_or_else(|| self.rustc_process.clone())
        } else {
            self.rustc_process.clone()
        };

        self.fill_env(rustc, pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }

        for crate_type in target.rustc_crate_types() {
            p.arg("--crate-type").arg(crate_type);
        }

        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    pub fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.push(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            // For build-std, we don't want to accidentally pull in any shared
            // libs from the sysroot that ships with rustc. This may not be
            // required (at least I cannot craft a situation where it
            // matters), but is here to be safe.
            if self.config.cli_unstable().build_std.is_none() {
                search_path.push(self.target_dylib_path.clone());
            }
            search_path
        };

        let dylib_path = util::dylib_path();
        let dylib_path_is_empty = dylib_path.is_empty();
        search_path.extend(dylib_path.into_iter());
        if cfg!(target_os = "macos") && dylib_path_is_empty {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set or set to an empty string. Since Cargo is explicitly setting
            // the value, make sure the defaults still work.
            if let Some(home) = env::var_os("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(
    bcx: &BuildContext<'_, '_>,
    kind: CompileKind,
) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = kind.short_name(bcx);

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get::<Option<config::PathAndArgs>>(&key)? {
        let path = v.path.resolve_program(bcx.config);
        return Ok(Some((path, v.args)));
    }

    // try target.'cfg(...)'.runner
    let target_cfg = bcx.info(kind).cfg();
    let mut cfgs = bcx
        .config
        .target_cfgs()?
        .iter()
        .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
        .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
    let matching_runner = cfgs.next();
    if let Some((key, runner)) = cfgs.next() {
        anyhow::bail!(
            "several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`\n\
             first match `{}` located in {}\n\
             second match `{}` located in {}",
            matching_runner.unwrap().0,
            matching_runner.unwrap().1.definition,
            key,
            runner.definition
        );
    }
    Ok(matching_runner.map(|(_k, runner)| {
        (
            runner.val.path.clone().resolve_program(bcx.config),
            runner.val.args.clone(),
        )
    }))
}
<commit_msg>Fix wrong directories in host_libdir.<commit_after>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

use cargo_platform::CfgExpr;
use semver::Version;

use super::BuildContext;
use crate::core::compiler::CompileKind;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Arguments needed to pass to rustdoc to run this test.
    pub args: Vec<OsString>,
    /// Whether or not -Zunstable-options is needed.
    pub unstable_opts: bool,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    /// `(package, target, path_to_test_exe)`
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: PathBuf,

    /// The path to libstd for the target
    pub target_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,
    primary_unit_rustc_process: Option<ProcessBuilder>,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(
        bcx: &BuildContext<'a, 'cfg>,
        default_kind: CompileKind,
    ) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();

            if let Some(rustc) = primary_unit_rustc_process.as_mut() {
                rustc.display_env_vars();
            }
        }

        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.info(CompileKind::Host).sysroot_host_libdir.clone(),
            target_dylib_path: bcx.info(default_kind).sysroot_target_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            primary_unit_rustc_process,
            host: bcx.host_triple().to_string(),
            target: default_kind.short_name(bcx).to_string(),
            target_runner: target_runner(bcx, default_kind)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult<ProcessBuilder> {
        let rustc = if is_primary {
            self.primary_unit_rustc_process
                .clone()
                .unwrap_or_else(|| self.rustc_process.clone())
        } else {
            self.rustc_process.clone()
        };

        self.fill_env(rustc, pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }

        for crate_type in target.rustc_crate_types() {
            p.arg("--crate-type").arg(crate_type);
        }

        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    pub fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.push(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            // For build-std, we don't want to accidentally pull in any shared
            // libs from the sysroot that ships with rustc. This may not be
            // required (at least I cannot craft a situation where it
            // matters), but is here to be safe.
            if self.config.cli_unstable().build_std.is_none() {
                search_path.push(self.target_dylib_path.clone());
            }
            search_path
        };

        let dylib_path = util::dylib_path();
        let dylib_path_is_empty = dylib_path.is_empty();
        search_path.extend(dylib_path.into_iter());
        if cfg!(target_os = "macos") && dylib_path_is_empty {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set or set to an empty string. Since Cargo is explicitly setting
            // the value, make sure the defaults still work.
            if let Some(home) = env::var_os("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(
    bcx: &BuildContext<'_, '_>,
    kind: CompileKind,
) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = kind.short_name(bcx);

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get::<Option<config::PathAndArgs>>(&key)? {
        let path = v.path.resolve_program(bcx.config);
        return Ok(Some((path, v.args)));
    }

    // try target.'cfg(...)'.runner
    let target_cfg = bcx.info(kind).cfg();
    let mut cfgs = bcx
        .config
        .target_cfgs()?
        .iter()
        .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
        .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
    let matching_runner = cfgs.next();
    if let Some((key, runner)) = cfgs.next() {
        anyhow::bail!(
            "several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`\n\
             first match `{}` located in {}\n\
             second match `{}` located in {}",
            matching_runner.unwrap().0,
            matching_runner.unwrap().1.definition,
            key,
            runner.definition
        );
    }
    Ok(matching_runner.map(|(_k, runner)| {
        (
            runner.val.path.clone().resolve_program(bcx.config),
            runner.val.args.clone(),
        )
    }))
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//!   to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//! deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-pre-spec` -- Upcoming Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};

pub use ruma_common::*;
#[doc(inline)]
pub use ruma_identifiers as identifiers;
#[doc(inline)]
pub use ruma_serde as serde;

pub use ruma_serde::Outgoing;

pub use ruma_identifiers::{
    device_id, device_key_id, event_id, mxc_uri, room_alias_id, room_id, room_version_id,
    server_name, server_signing_key_id, user_id, ClientSecret, DeviceId, DeviceKeyAlgorithm,
    DeviceKeyId, DeviceSignatures, DeviceSigningKeyId, EntitySignatures, EventEncryptionAlgorithm,
    EventId, KeyId, KeyName, MxcUri, RoomAliasId, RoomId, RoomOrAliasId, RoomVersionId, ServerName,
    ServerSignatures, ServerSigningKeyId, SessionId, Signatures, SigningKeyAlgorithm, UserId,
};

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_events as events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://matrix.org/docs/spec/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}
<commit_msg>ruma: Re-export TransactionId from crate root<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//!   to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//! deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-pre-spec` -- Upcoming Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};

pub use ruma_common::*;
#[doc(inline)]
pub use ruma_identifiers as identifiers;
#[doc(inline)]
pub use ruma_serde as serde;

pub use ruma_serde::Outgoing;

pub use ruma_identifiers::{
    device_id, device_key_id, event_id, mxc_uri, room_alias_id, room_id, room_version_id,
    server_name, server_signing_key_id, user_id, ClientSecret, DeviceId, DeviceKeyAlgorithm,
    DeviceKeyId, DeviceSignatures, DeviceSigningKeyId, EntitySignatures, EventEncryptionAlgorithm,
    EventId, KeyId, KeyName, MxcUri, RoomAliasId, RoomId, RoomOrAliasId, RoomVersionId, ServerName,
    ServerSignatures, ServerSigningKeyId, SessionId, Signatures, SigningKeyAlgorithm,
    TransactionId, UserId,
};

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_events as events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://matrix.org/docs/spec/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}
<|endoftext|>


<commit_before>fn evaluate(program: Vec<char>) {
    let stack: Vec<i32> = vec![];

    for c in program {
        match c {
            '*' => println!("Multiply last two on stack"),
            '+' => println!("Sum last two on stack"),
            '-' => println!("Subtract last two on stack"),
            '/' => println!("Divide last two on stack"),
            _   => println!("Find digit -> Add to stack. Else -> Die"),
        }
    }
}

fn main() {
    let program: Vec<char> = "3 4 * 2 * 1 /".chars().filter(|&c| c != ' ').collect();

    evaluate(program);
}
<commit_msg>moved into struct<commit_after>struct Stack {
    content: Vec<i32>,
}

impl Stack {
    fn push(&mut self, d: i32) {
        self.content.push(d);
    }

    fn multiply(&mut self) {
        println!("Multiply");
    }

    fn add(&mut self) {
        println!("Add");
    }

    fn subtract(&mut self) {
        println!("Subtract");
    }

    fn divide(&mut self) {
        println!("Divide");
    }
}

fn evaluate(program: Vec<char>) {
    let mut stack = Stack { content: vec![] };

    for c in program {
        match c {
            '*' => stack.multiply(),
            '+' => stack.add(),
            '-' => stack.subtract(),
            '/' => stack.divide(),
            '1' => stack.push(1),
            '2' => stack.push(2),
            '3' => stack.push(3),
            '4' => stack.push(4),
            '5' => stack.push(5),
            '6' => stack.push(6),
            '7' => stack.push(7),
            '8' => stack.push(8),
            '9' => stack.push(9),
            _   => println!("DIE..."),
        }
    }

    for c in stack.content {
        println!("{}", c);
    }
}

fn main() {
    let program: Vec<char> = "3 4 * 2 * 1 /".chars().filter(|&c| c != ' ').collect();

    evaluate(program);
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fs::File;
use std::io::{self, Stdout};

use anyhow::Result;
use comfy_table::*;
use snap::read::FrameDecoder;

use pueue_lib::log::{get_log_file_handles, read_last_lines};
use pueue_lib::network::message::TaskLogMessage;
use pueue_lib::settings::Settings;
use pueue_lib::task::{Task, TaskResult, TaskStatus};

use super::{colors::Colors, helper::*};
use crate::cli::SubCommand;

/// Print the log ouput of finished tasks.
/// Either print the logs of every task
/// or only print the logs of the specified tasks.
pub fn print_logs(
    mut task_logs: BTreeMap<usize, TaskLogMessage>,
    cli_command: &SubCommand,
    colors: &Colors,
    settings: &Settings,
) {
    // Get actual commandline options.
    // This is necessary to know how we should display/return the log information.
    let (json, task_ids, lines, full) = match cli_command {
        SubCommand::Log {
            json,
            task_ids,
            lines,
            full,
        } => (*json, task_ids.clone(), *lines, *full),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_log. This shouldn't happen",
            cli_command
        ),
    };

    // Return the server response in json representation.
    // TODO: This only works properly if we get the logs from remote.
    // TODO: However, this still doesn't work, since the logs are still compressed.
    if json {
        println!("{}", serde_json::to_string(&task_logs).unwrap());
        return;
    }

    // Check some early return conditions
    if task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks");
        return;
    }

    if !task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks for your specified ids");
        return;
    }

    // Determine, whether we should draw everything or only a part of the log output.
    // None implicates that all lines are printed
    let lines = if full {
        None
    } else if let Some(lines) = lines {
        Some(lines)
    } else {
        // By default only some lines are shown per task, if multiple tasks exist.
        // For a single task, the whole log output is shown.
        if task_logs.len() > 1 {
            Some(15)
        } else {
            None
        }
    };

    // Do the actual log printing
    let mut task_iter = task_logs.iter_mut().peekable();
    while let Some((_, mut task_log)) = task_iter.next() {
        print_log(&mut task_log, colors, settings, lines);

        // Add a newline if there is another task that's going to be printed.
        if let Some((_, task_log)) = task_iter.peek() {
            if vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused]
                .contains(&task_log.task.status)
            {
                println!();
            }
        }
    }
}

/// Print the log of a single task.
///
/// message: The message returned by the daemon. This message includes all
///          requested tasks and the tasks' logs, if we don't read local logs.
/// lines: Whether we should reduce the log output of each task to a specific number of lines.
///         `None` implicates that everything should be printed.
///         This is only important, if we read local lines.
pub fn print_log(
    message: &mut TaskLogMessage,
    colors: &Colors,
    settings: &Settings,
    lines: Option<usize>,
) {
    let task = &message.task;
    // We only show logs of finished or running tasks.
    if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused].contains(&task.status) {
        return;
    }

    print_task_info(task, colors);

    if settings.client.read_local_logs {
        print_local_log(message.task.id, colors, settings, lines);
    } else if message.stdout.is_some() && message.stderr.is_some() {
        print_remote_log(message, colors);
    } else {
        println!("Logs requested from pueue daemon, but none received. Please report this bug.");
    }
}

/// Print some information about a task, which is displayed on top of the task's log output.
fn print_task_info(task: &Task, colors: &Colors) {
    // Print task id and exit code.
    let task_cell = Cell::new(format!("Task {}: ", task.id)).add_attribute(Attribute::Bold);

    let (exit_status, color) = match &task.result {
        Some(TaskResult::Success) => ("completed successfully".into(), colors.green()),
        Some(TaskResult::Failed(exit_code)) => {
            (format!("failed with exit code {}", exit_code), colors.red())
        }
        Some(TaskResult::FailedToSpawn(err)) => (format!("failed to spawn: {}", err), colors.red()),
        Some(TaskResult::Killed) => ("killed by system or user".into(), colors.red()),
        Some(TaskResult::Errored) => ("some IO error.\n Check daemon log.".into(), colors.red()),
        Some(TaskResult::DependencyFailed) => ("dependency failed".into(), colors.red()),
        None => match &task.status {
            TaskStatus::Paused => ("paused".into(), colors.white()),
            TaskStatus::Running => ("running".into(), colors.yellow()),
            _ => (task.status.to_string(), colors.white()),
        },
    };
    let status_cell = Cell::new(exit_status).fg(color);

    // The styling of the task number and status is done by a single-row table.
    let mut table = Table::new();
    table.load_preset("││─ └──┘     ─ ┌┐  ");
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
    table.set_header(vec![task_cell, status_cell]);
    println!("{}", table);

    // All other information is alligned and styled by using a separat table.
    let mut table = Table::new();
    table.load_preset(comfy_table::presets::NOTHING);
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);

    // Command and path
    table.add_row(vec![
        Cell::new("Command:").add_attribute(Attribute::Bold),
        Cell::new(&task.command),
    ]);
    table.add_row(vec![
        Cell::new("Path:").add_attribute(Attribute::Bold),
        Cell::new(&task.path),
    ]);

    // Start and end time
    if let Some(start) = task.start {
        table.add_row(vec![
            Cell::new("Start:").add_attribute(Attribute::Bold),
            Cell::new(start.to_rfc2822()),
        ]);
    }
    if let Some(end) = task.end {
        table.add_row(vec![
            Cell::new("End:").add_attribute(Attribute::Bold),
            Cell::new(end.to_rfc2822()),
        ]);
    }

    // Set the padding of the left column to 0 align the keys to the right
    let first_column = table.get_column_mut(0).unwrap();
    first_column.set_cell_alignment(CellAlignment::Right);
    first_column.set_padding((0, 0));

    println!("{}", table);
}

/// The daemon didn't send any log output, thereby we didn't request any.
/// If that's the case, read the log files from the local pueue directory
fn print_local_log(task_id: usize, colors: &Colors, settings: &Settings, lines: Option<usize>) {
    let (mut stdout_file, mut stderr_file) =
        match get_log_file_handles(task_id, &settings.shared.pueue_directory()) {
            Ok((stdout, stderr)) => (stdout, stderr),
            Err(err) => {
                println!("Failed to get log file handles: {}", err);
                return;
            }
        };
    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();

    print_local_file(
        &mut stdout,
        &mut stdout_file,
        &lines,
        style_text("stdout:", Some(colors.green()), Some(Attribute::Bold)),
    );

    print_local_file(
        &mut stdout,
        &mut stderr_file,
        &lines,
        style_text("stderr:", Some(colors.red()), Some(Attribute::Bold)),
    );
}

/// Print a local log file.
/// This is usually either the stdout or the stderr
pub fn print_local_file(stdout: &mut Stdout, file: &mut File, lines: &Option<usize>, text: String) {
    if let Ok(metadata) = file.metadata() {
        if metadata.len() != 0 {
            // Don't print a newline between the task information and the first output
            println!("\n{}", text);

            // Only print the last lines if requested
            if let Some(lines) = lines {
                println!("{}", read_last_lines(file, *lines));
                return;
            }

            // Print everything
            if let Err(err) = io::copy(file, stdout) {
                println!("Failed reading local log file: {}", err);
            };
        }
    }
}

/// Prints log output received from the daemon.
/// We can safely call .unwrap() on stdout and stderr in here, since this
/// branch is always called after ensuring that both are `Some`.
pub fn print_remote_log(task_log: &TaskLogMessage, colors: &Colors) {
    // Save whether stdout was printed, so we can add a newline between outputs.
    if !task_log.stdout.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_log(&task_log, colors, true) {
            println!("Error while parsing stdout: {}", err);
        }
    }

    if !task_log.stderr.as_ref().unwrap().is_empty() {
        if let Err(err) = print_remote_task_log(&task_log, colors, false) {
            println!("Error while parsing stderr: {}", err);
        };
    }
}

/// Print log output of a finished process.
fn print_remote_task_log(task_log: &TaskLogMessage, colors: &Colors, stdout: bool) -> Result<()> {
    let (pre_text, color, bytes) = if stdout {
        (
            "stdout: ",
            colors.green(),
            task_log.stdout.as_ref().unwrap(),
        )
    } else {
        ("stderr: ", colors.red(), task_log.stderr.as_ref().unwrap())
    };

    println!(
        "\n{}",
        style_text(pre_text, Some(color), Some(Attribute::Bold))
    );

    let mut decompressor = FrameDecoder::new(bytes.as_slice());

    let stdout = io::stdout();
    let mut write = stdout.lock();
    io::copy(&mut decompressor, &mut write)?;

    Ok(())
}
<commit_msg>client: Refactor remote log decompression code<commit_after>use std::collections::BTreeMap;
use std::fs::File;
use std::io::{self, Stdout};

use anyhow::Result;
use comfy_table::*;
use snap::read::FrameDecoder;

use pueue_lib::log::{get_log_file_handles, read_last_lines};
use pueue_lib::network::message::TaskLogMessage;
use pueue_lib::settings::Settings;
use pueue_lib::task::{Task, TaskResult, TaskStatus};

use super::{colors::Colors, helper::*};
use crate::cli::SubCommand;

/// Print the log ouput of finished tasks.
/// Either print the logs of every task
/// or only print the logs of the specified tasks.
pub fn print_logs(
    mut task_logs: BTreeMap<usize, TaskLogMessage>,
    cli_command: &SubCommand,
    colors: &Colors,
    settings: &Settings,
) {
    // Get actual commandline options.
    // This is necessary to know how we should display/return the log information.
    let (json, task_ids, lines, full) = match cli_command {
        SubCommand::Log {
            json,
            task_ids,
            lines,
            full,
        } => (*json, task_ids.clone(), *lines, *full),
        _ => panic!(
            "Got wrong Subcommand {:?} in print_log. This shouldn't happen",
            cli_command
        ),
    };

    // Return the server response in json representation.
    // TODO: This only works properly if we get the logs from remote.
    // TODO: However, this still doesn't work, since the logs are still compressed.
    if json {
        println!("{}", serde_json::to_string(&task_logs).unwrap());
        return;
    }

    // Check some early return conditions
    if task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks");
        return;
    }

    if !task_ids.is_empty() && task_logs.is_empty() {
        println!("There are no finished tasks for your specified ids");
        return;
    }

    // Determine, whether we should draw everything or only a part of the log output.
    // None implicates that all lines are printed
    let lines = if full {
        None
    } else if let Some(lines) = lines {
        Some(lines)
    } else {
        // By default only some lines are shown per task, if multiple tasks exist.
        // For a single task, the whole log output is shown.
        if task_logs.len() > 1 {
            Some(15)
        } else {
            None
        }
    };

    // Do the actual log printing
    let mut task_iter = task_logs.iter_mut().peekable();
    while let Some((_, mut task_log)) = task_iter.next() {
        print_log(&mut task_log, colors, settings, lines);

        // Add a newline if there is another task that's going to be printed.
        if let Some((_, task_log)) = task_iter.peek() {
            if vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused]
                .contains(&task_log.task.status)
            {
                println!();
            }
        }
    }
}

/// Print the log of a single task.
///
/// message: The message returned by the daemon. This message includes all
///          requested tasks and the tasks' logs, if we don't read local logs.
/// lines: Whether we should reduce the log output of each task to a specific number of lines.
///         `None` implicates that everything should be printed.
///         This is only important, if we read local lines.
pub fn print_log(
    message: &mut TaskLogMessage,
    colors: &Colors,
    settings: &Settings,
    lines: Option<usize>,
) {
    let task = &message.task;
    // We only show logs of finished or running tasks.
    if !vec![TaskStatus::Done, TaskStatus::Running, TaskStatus::Paused].contains(&task.status) {
        return;
    }

    print_task_info(task, colors);

    if settings.client.read_local_logs {
        print_local_log(message.task.id, colors, settings, lines);
    } else if message.stdout.is_some() && message.stderr.is_some() {
        print_remote_log(message, colors);
    } else {
        println!("Logs requested from pueue daemon, but none received. Please report this bug.");
    }
}

/// Print some information about a task, which is displayed on top of the task's log output.
fn print_task_info(task: &Task, colors: &Colors) {
    // Print task id and exit code.
    let task_cell = Cell::new(format!("Task {}: ", task.id)).add_attribute(Attribute::Bold);

    let (exit_status, color) = match &task.result {
        Some(TaskResult::Success) => ("completed successfully".into(), colors.green()),
        Some(TaskResult::Failed(exit_code)) => {
            (format!("failed with exit code {}", exit_code), colors.red())
        }
        Some(TaskResult::FailedToSpawn(err)) => (format!("failed to spawn: {}", err), colors.red()),
        Some(TaskResult::Killed) => ("killed by system or user".into(), colors.red()),
        Some(TaskResult::Errored) => ("some IO error.\n Check daemon log.".into(), colors.red()),
        Some(TaskResult::DependencyFailed) => ("dependency failed".into(), colors.red()),
        None => match &task.status {
            TaskStatus::Paused => ("paused".into(), colors.white()),
            TaskStatus::Running => ("running".into(), colors.yellow()),
            _ => (task.status.to_string(), colors.white()),
        },
    };
    let status_cell = Cell::new(exit_status).fg(color);

    // The styling of the task number and status is done by a single-row table.
    let mut table = Table::new();
    table.load_preset("││─ └──┘     ─ ┌┐  ");
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
    table.set_header(vec![task_cell, status_cell]);
    println!("{}", table);

    // All other information is alligned and styled by using a separat table.
    let mut table = Table::new();
    table.load_preset(comfy_table::presets::NOTHING);
    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);

    // Command and path
    table.add_row(vec![
        Cell::new("Command:").add_attribute(Attribute::Bold),
        Cell::new(&task.command),
    ]);
    table.add_row(vec![
        Cell::new("Path:").add_attribute(Attribute::Bold),
        Cell::new(&task.path),
    ]);

    // Start and end time
    if let Some(start) = task.start {
        table.add_row(vec![
            Cell::new("Start:").add_attribute(Attribute::Bold),
            Cell::new(start.to_rfc2822()),
        ]);
    }
    if let Some(end) = task.end {
        table.add_row(vec![
            Cell::new("End:").add_attribute(Attribute::Bold),
            Cell::new(end.to_rfc2822()),
        ]);
    }

    // Set the padding of the left column to 0 align the keys to the right
    let first_column = table.get_column_mut(0).unwrap();
    first_column.set_cell_alignment(CellAlignment::Right);
    first_column.set_padding((0, 0));

    println!("{}", table);
}

/// The daemon didn't send any log output, thereby we didn't request any.
/// If that's the case, read the log files from the local pueue directory
fn print_local_log(task_id: usize, colors: &Colors, settings: &Settings, lines: Option<usize>) {
    let (mut stdout_file, mut stderr_file) =
        match get_log_file_handles(task_id, &settings.shared.pueue_directory()) {
            Ok((stdout, stderr)) => (stdout, stderr),
            Err(err) => {
                println!("Failed to get log file handles: {}", err);
                return;
            }
        };
    // Stdout handler to directly write log file output to io::stdout
    // without having to load anything into memory.
    let mut stdout = io::stdout();

    print_local_file(
        &mut stdout,
        &mut stdout_file,
        &lines,
        style_text("stdout:", Some(colors.green()), Some(Attribute::Bold)),
    );

    print_local_file(
        &mut stdout,
        &mut stderr_file,
        &lines,
        style_text("stderr:", Some(colors.red()), Some(Attribute::Bold)),
    );
}

/// Print a local log file.
/// This is usually either the stdout or the stderr
pub fn print_local_file(stdout: &mut Stdout, file: &mut File, lines: &Option<usize>, text: String) {
    if let Ok(metadata) = file.metadata() {
        if metadata.len() != 0 {
            // Don't print a newline between the task information and the first output
            println!("\n{}", text);

            // Only print the last lines if requested
            if let Some(lines) = lines {
                println!("{}", read_last_lines(file, *lines));
                return;
            }

            // Print everything
            if let Err(err) = io::copy(file, stdout) {
                println!("Failed reading local log file: {}", err);
            };
        }
    }
}

/// Prints log output received from the daemon.
/// We can safely call .unwrap() on stdout and stderr in here, since this
/// branch is always called after ensuring that both are `Some`.
pub fn print_remote_log(task_log: &TaskLogMessage, colors: &Colors) {
    // Save whether stdout was printed, so we can add a newline between outputs.
    if let Some(bytes) = task_log.stdout.as_ref() {
        if !bytes.is_empty() {
            println!(
                "\n{}",
                style_text("stdout: ", Some(colors.green()), Some(Attribute::Bold))
            );

            if let Err(err) = decompress_and_print_remote_log(bytes) {
                println!("Error while parsing stdout: {}", err);
            }
        }
    }

    if let Some(bytes) = task_log.stderr.as_ref() {
        if !bytes.is_empty() {
            println!(
                "\n{}",
                style_text("stderr: ", Some(colors.red()), Some(Attribute::Bold))
            );

            if let Err(err) = decompress_and_print_remote_log(bytes) {
                println!("Error while parsing stderr: {}", err);
            };
        }
    }
}

/// We cannot easily stream log output from the client to the daemon (yet).
/// Right now, stdout and stderr are compressed in the daemon and sent as a single payload to the
/// client. In here, we take that payload, decompress it and stream it it directly to stdout.
fn decompress_and_print_remote_log(bytes: &[u8]) -> Result<()> {
    let mut decompressor = FrameDecoder::new(bytes);

    let stdout = io::stdout();
    let mut write = stdout.lock();
    io::copy(&mut decompressor, &mut write)?;

    Ok(())
}
<|endoftext|>


<commit_before>// check-pass
// compile-flags: --cfg something

#![deny(unused_mut)]

extern "C" {
    fn ffi(
        #[allow(unused_mut)] a: i32,
        #[cfg(something)] b: i32,
        #[cfg_attr(something, cfg(nothing))] c: i32,
        #[deny(unused_mut)] d: i32,
        #[forbid(unused_mut)] #[warn(unused_mut)] ...
    );
}

type FnType = fn(
    #[allow(unused_mut)] a: i32,
    #[cfg(something)] b: i32,
    #[cfg_attr(something, cfg(nothing))] c: i32,
    #[deny(unused_mut)] d: i32,
    #[forbid(unused_mut)] #[warn(unused_mut)] e: i32
);

pub fn foo(
    #[allow(unused_mut)] a: i32,
    #[cfg(something)] b: i32,
    #[cfg_attr(something, cfg(nothing))] c: i32,
    #[deny(unused_mut)] d: i32,
    #[forbid(unused_mut)] #[warn(unused_mut)] _e: i32
) {}

// self

struct SelfStruct {}
impl SelfStruct {
    fn foo(
        #[allow(unused_mut)] self,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}

struct RefStruct {}
impl RefStruct {
    fn foo(
        #[allow(unused_mut)] &self,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}
trait RefTrait {
    fn foo(
        #[forbid(unused_mut)] &self,
        #[warn(unused_mut)] a: i32
    ) {}
}
impl RefTrait for RefStruct {
    fn foo(
        #[forbid(unused_mut)] &self,
        #[warn(unused_mut)] a: i32
    ) {}
}

// Box<Self>

struct BoxSelfStruct {}
impl BoxSelfStruct {
    fn foo(
        #[allow(unused_mut)] self: Box<Self>,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}
trait BoxSelfTrait {
    fn foo(
        #[forbid(unused_mut)] self: Box<Self>,
        #[warn(unused_mut)] a: i32
    ) {}
}
impl BoxSelfTrait for BoxSelfStruct {
    fn foo(
        #[forbid(unused_mut)] self: Box<Self>,
        #[warn(unused_mut)] a: i32
    ) {}
}

fn main() {
    let _: unsafe extern "C" fn(_, _, _, ...) = ffi;
    let _: fn(_, _, _, _) = foo;
    let _: FnType = |_, _, _, _| {};
    let c = |
        #[allow(unused_mut)] a: u32,
        #[cfg(something)] b: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] c: i32,
    | {};
    let _ = c(1, 2);
}
<commit_msg>rewrite old test so that its attributes are consistent with what we want in the language.<commit_after>// check-pass
// compile-flags: --cfg something

#![deny(unused_mut)]

extern "C" {
    fn ffi(
        #[allow(unused_mut)] a: i32,
        #[cfg(something)] b: i32,
        #[cfg_attr(something, cfg(nothing))] c: i32,
        #[forbid(unused_mut)] d: i32,
        #[deny(unused_mut)] #[warn(unused_mut)] ...
    );
}

type FnType = fn(
    #[allow(unused_mut)] a: i32,
    #[cfg(something)] b: i32,
    #[cfg_attr(something, cfg(nothing))] c: i32,
    #[forbid(unused_mut)] d: i32,
    #[deny(unused_mut)] #[warn(unused_mut)] e: i32
);

pub fn foo(
    #[allow(unused_mut)] a: i32,
    #[cfg(something)] b: i32,
    #[cfg_attr(something, cfg(nothing))] c: i32,
    #[forbid(unused_mut)] d: i32,
    #[deny(unused_mut)] #[warn(unused_mut)] _e: i32
) {}

// self

struct SelfStruct {}
impl SelfStruct {
    fn foo(
        #[allow(unused_mut)] self,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}

struct RefStruct {}
impl RefStruct {
    fn foo(
        #[allow(unused_mut)] &self,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}
trait RefTrait {
    fn foo(
        #[forbid(unused_mut)] &self,
        #[warn(unused_mut)] a: i32
    ) {}
}
impl RefTrait for RefStruct {
    fn foo(
        #[forbid(unused_mut)] &self,
        #[warn(unused_mut)] a: i32
    ) {}
}

// Box<Self>

struct BoxSelfStruct {}
impl BoxSelfStruct {
    fn foo(
        #[allow(unused_mut)] self: Box<Self>,
        #[cfg(something)] a: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] b: i32,
    ) {}
}
trait BoxSelfTrait {
    fn foo(
        #[forbid(unused_mut)] self: Box<Self>,
        #[warn(unused_mut)] a: i32
    ) {}
}
impl BoxSelfTrait for BoxSelfStruct {
    fn foo(
        #[forbid(unused_mut)] self: Box<Self>,
        #[warn(unused_mut)] a: i32
    ) {}
}

fn main() {
    let _: unsafe extern "C" fn(_, _, _, ...) = ffi;
    let _: fn(_, _, _, _) = foo;
    let _: FnType = |_, _, _, _| {};
    let c = |
        #[allow(unused_mut)] a: u32,
        #[cfg(something)] b: i32,
        #[cfg_attr(something, cfg(nothing))]
        #[deny(unused_mut)] c: i32,
    | {};
    let _ = c(1, 2);
}
<|endoftext|>


<commit_before>extern crate proc_macro;

// spell-checker:ignore () SIGPIPE uucore uumain uutils

//## rust proc-macro background info
//* ref: <https://dev.to/naufraghi/procedural-macro-in-rust-101-k3f> @@ <http://archive.is/Vbr5e>
//* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ <http://archive.is/8YDua>

//## proc_dbg macro
//* used to help debug the compile-time proc_macro code

#[cfg(feature = "debug")]
macro_rules! proc_dbg {
    ($x:expr) => {
        dbg!($x)
    };
}
#[cfg(not(feature = "debug"))]
macro_rules! proc_dbg {
    ($x:expr) => {
        std::convert::identity($x)
    };
}

//## main!()

// main!( EXPR )
// generates a `main()` function for utilities within the uutils group
// EXPR == syn::Expr::Lit::String | syn::Expr::Path::Ident ~ EXPR contains the location of the utility `uumain()` function
//* for future use of "eager" macros and more generic use, EXPR may be in either STRING or IDENT form
//* for ease-of-use, the trailing "::uumain" is optional and will be added if needed

struct Tokens {
    expr: syn::Expr,
}

impl syn::parse::Parse for Tokens {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        Ok(Tokens {
            expr: input.parse()?,
        })
    }
}

#[proc_macro]
pub fn main(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let Tokens { expr } = syn::parse_macro_input!(stream as Tokens);
    // match EXPR as a string literal or an ident path, o/w panic!()
    let expr = match expr {
        syn::Expr::Lit(expr) => match expr.lit {
            syn::Lit::Str(ref lit) => {
                let mut s = lit.value();
                if !s.ends_with("::uumain") {
                    s += "::uumain";
                }
                syn::LitStr::new(&s, proc_macro2::Span::call_site())
                    .parse()
                    .unwrap()
            }
            _ => panic!(),
        },
        syn::Expr::Path(expr) => {
            if &expr.path.segments.last().unwrap().ident.to_string() != "uumain" {
                syn::parse_quote!( #expr::uumain )
            } else {
                expr
            }
        }
        _ => panic!(),
    };
    let f = quote::quote! { #expr(uucore::args().collect()) };
    // generate a uutils utility `main()` function, tailored for the calling utility
    let result = quote::quote! {
        fn main() {
            use std::io::Write;
            uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
            let code = #f; // execute utility code
            std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
            std::process::exit(code);
        }
    };
    proc_macro::TokenStream::from(result)
}
<commit_msg>build/uucore_procs ~ add debugging statements<commit_after>extern crate proc_macro;

// spell-checker:ignore () SIGPIPE uucore uumain uutils

//## rust proc-macro background info
//* ref: <https://dev.to/naufraghi/procedural-macro-in-rust-101-k3f> @@ <http://archive.is/Vbr5e>
//* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ <http://archive.is/8YDua>

//## proc_dbg macro
//* used to help debug the compile-time proc_macro code

#[cfg(feature = "debug")]
macro_rules! proc_dbg {
    ($x:expr) => {
        dbg!($x)
    };
}
#[cfg(not(feature = "debug"))]
macro_rules! proc_dbg {
    ($x:expr) => {
        std::convert::identity($x)
    };
}

//## main!()

// main!( EXPR )
// generates a `main()` function for utilities within the uutils group
// EXPR == syn::Expr::Lit::String | syn::Expr::Path::Ident ~ EXPR contains the lexical path to the utility `uumain()` function
//* NOTE: EXPR is ultimately expected to be a multi-segment lexical path (eg, `crate::func`); so, if a single segment path is provided, a trailing "::uumain" is automatically added
//* for more generic use (and future use of "eager" macros), EXPR may be in either STRING or IDENT form

struct Tokens {
    expr: syn::Expr,
}

impl syn::parse::Parse for Tokens {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        Ok(Tokens {
            expr: input.parse()?,
        })
    }
}

#[proc_macro]
pub fn main(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let Tokens { expr } = syn::parse_macro_input!(stream as Tokens);
    proc_dbg!(&expr);

    const ARG_PANIC_TEXT: &str = "expected ident lexical path (or a literal string version) to 'uumain()' as argument";

    // match EXPR as a string literal or an ident path, o/w panic!()
    let mut expr = match expr {
        syn::Expr::Lit(expr_lit) => {
            match expr_lit.lit {
                syn::Lit::Str(ref lit_str) => { lit_str.parse::<syn::ExprPath>().unwrap() },
                _ => panic!(ARG_PANIC_TEXT),
            }
        },
        syn::Expr::Path(expr_path) => { expr_path },
        _ => panic!(ARG_PANIC_TEXT),
    };
    proc_dbg!(&expr);

    // for a single segment ExprPath argument, add trailing '::uumain' segment
    if expr.path.segments.len() < 2 {
        expr = syn::parse_quote!( #expr::uumain );
    };
    proc_dbg!(&expr);

    let f = quote::quote! { #expr(uucore::args().collect()) };
    proc_dbg!(&f);

    // generate a uutils utility `main()` function, tailored for the calling utility
    let result = quote::quote! {
        fn main() {
            use std::io::Write;
            uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
            let code = #f; // execute utility code
            std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
            std::process::exit(code);
        }
    };
    proc_macro::TokenStream::from(result)
}
<|endoftext|>


<commit_before><commit_msg>Use correct directions in bdpt pdf precompute<commit_after><|endoftext|>


<commit_before>use super::canvas::{Canvas, Image, FONT_W};
use super::{WidgetId};
use ::{V2, Rect, ToColor, color};
use ::Anchor::*;

/// Helper methods for canvas context that do not depend on the underlying
/// implementation details.
pub trait CanvasUtil {
    /// Draw a thick solid line on the canvas.
    fn draw_line<C: ToColor+Copy>(&mut self, width: f32, p1: V2<f32>, p2: V2<f32>, layer: f32, color: &C);
    /// Get the size of an atlas image.
    fn image_dim(&self, img: Image) -> V2<u32>;

    /// Draw a stored image on the canvas.
    fn draw_image<C: ToColor+Copy, D: ToColor+Copy>(&mut self, img: Image,
        offset: V2<f32>, z: f32, color: &C, back_color: &D);

    /// Draw a filled rectangle.
    fn fill_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C);

    /// Draw a wireframe rectangle.
    fn draw_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C);

    /// Draw an immediate GUI button and return whether it was pressed.
    fn button(&mut self, id: WidgetId, pos: V2<f32>, z: f32) -> bool;

    fn draw_char<C: ToColor+Copy, D: ToColor+Copy>(&mut self, c: char, offset: V2<f32>, z: f32, color: &C, border: Option<&D>);

    fn char_width(&self, c: char) -> f32;

    /// Write a timestamped screenshot PNG to disk.
    fn save_screenshot(&mut self, basename: &str);
}

impl<'a> CanvasUtil for Canvas<'a> {
    fn draw_line<C: ToColor+Copy>(&mut self, width: f32, p1: V2<f32>, p2: V2<f32>, layer: f32, color: &C) {
        if p1 == p2 { return; }

        let tex = self.solid_tex_coord();

        // The front vector. Extend by width.
        let v1 = p2 - p1;
        let scalar = v1.dot(v1);
        let scalar = (scalar + width * width) / scalar;
        let v1 = v1 * scalar;

        // The sideways vector, turn into unit vector, then multiply by half the width.
        let v2 = V2(-v1.1, v1.0);
        let scalar = width / 2.0 * 1.0 / v2.dot(v2).sqrt();
        let v2 = v2 * scalar;

        let ind0 = self.num_vertices();
        self.push_vertex(p1 + v2, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 - v2, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 - v2 + v1, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 + v2 + v1, layer, tex, color, &color::BLACK);
        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn image_dim(&self, img: Image) -> V2<u32> {
        self.image_data(img).pos.1.map(|x| x as u32)
    }

    fn draw_image<C: ToColor+Copy, D: ToColor+Copy>(&mut self, img: Image,
        offset: V2<f32>, z: f32, color: &C, back_color: &D) {
        // Use round numbers, fractions seem to cause artifacts to pixels.
        let offset = offset.map(|x| x.floor());
        let mut pos;
        let mut tex;
        {
            let data = self.image_data(img);
            pos = data.pos + offset;
            tex = data.tex;
        }

        let ind0 = self.num_vertices();

        self.push_vertex(pos.point(TopLeft), z, tex.point(TopLeft), color, back_color);
        self.push_vertex(pos.point(TopRight), z, tex.point(TopRight), color, back_color);
        self.push_vertex(pos.point(BottomRight), z, tex.point(BottomRight), color, back_color);
        self.push_vertex(pos.point(BottomLeft), z, tex.point(BottomLeft), color, back_color);

        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn fill_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C) {
        let tex = self.solid_tex_coord();
        let ind0 = self.num_vertices();

        self.push_vertex(rect.point(TopLeft), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(TopRight), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(BottomRight), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(BottomLeft), z, tex, color, &color::BLACK);

        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn draw_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C) {
        self.draw_line(1.0, rect.point(TopLeft), rect.point(TopRight) - V2(1.0, 0.0), z, color);
        self.draw_line(1.0, rect.point(TopRight) - V2(1.0, 0.0), rect.point(BottomRight) - V2(1.0, 0.0), z, color);
        self.draw_line(1.0, rect.point(BottomLeft) - V2(0.0, 1.0), rect.point(BottomRight) - V2(1.0, 1.0), z, color);
        self.draw_line(1.0, rect.point(TopLeft), rect.point(BottomLeft), z, color);
    }

    fn button(&mut self, id: WidgetId, pos: V2<f32>, z: f32) -> bool {
        // TODO: Button visual style! Closures?
        let area = Rect(pos, V2(64.0, 16.0));
        let mut color = color::GREEN;
        if area.contains(&self.mouse_pos) {
            self.hot_widget = Some(id);
            if self.active_widget.is_none() && self.mouse_pressed {
                self.active_widget = Some(id);
            }
            color = color::RED;
        }

        self.fill_rect(&area, z, &color);

        return !self.mouse_pressed // Mouse is released
            && self.active_widget == Some(id) // But this button is hot and active
            && self.hot_widget == Some(id);
    }

    fn draw_char<C: ToColor+Copy, D: ToColor+Copy>(&mut self, c: char, offset: V2<f32>, z: f32, color: &C, border: Option<&D>) {
        static BORDER: [V2<f32>; 8] =
            [V2(-1.0, -1.0), V2( 0.0, -1.0), V2( 1.0, -1.0),
             V2(-1.0,  0.0),                 V2( 1.0,  0.0),
             V2(-1.0,  1.0), V2( 0.0,  1.0), V2( 1.0,  1.0)];
        if let Some(img) = self.font_image(c) {
            if let Some(b) = border {
                // Put the border a tiny bit further in the z-buffer so it
                // won't clobber the text on the same layer.
                let border_z = z + 0.00001;
                for &d in BORDER.iter() {
                    self.draw_image(img, offset + d, border_z, b, &color::BLACK);
                }
            }
            self.draw_image(img, offset, z, color, &color::BLACK);
        }
    }

    fn char_width(&self, c: char) -> f32 {
        // Special case for space, the atlas image won't have a width.
        if c == ' ' { return (FONT_W / 2) as f32; }

        // Infer letter width from the cropped atlas image. (Use mx instead of
        // dim on the pos rectangle so that the left-side space will be
        // preserved and the letters are kept slightly apart.)
        if let Some(img) = self.font_image(c) {
            let width = self.image_data(img).pos.mx().0;
            return width;
        }

        // Not a valid letter.
        (FONT_W / 2) as f32
    }

    fn save_screenshot(&mut self, basename: &str) {
        use time;
        use std::path::{Path};
        use std::fs::{self, File, PathExt};
        use image;

        let shot = self.screenshot();

        let timestamp = time::precise_time_s() as u64;
        // Create screenshot filenames by concatenating the current timestamp in
        // seconds with a running number from 00 to 99. 100 shots per second
        // should be good enough.

        // Default if we fail to generate any of the 100 candidates for this
        // second, just overwrite with the "xx" prefix then.
        let mut filename = format!("{}-{}{}.png", basename, timestamp, "xx");

        // Run through candidates for this second.
        for i in 0..100 {
            let test_filename = format!("{}-{}{:02}.png", basename, timestamp, i);
            if !Path::new(&test_filename).exists() {
                // Thread-safe claiming: create_dir will fail if the dir
                // already exists (it'll exist if another thread is gunning
                // for the same filename and managed to get past us here).
                // At least assuming that create_dir is atomic...
                let squat_dir = format!(".tmp-{}{:02}", timestamp, i);
                if fs::create_dir(&squat_dir).is_ok() {
                    File::create(&test_filename).unwrap();
                    filename = test_filename;
                    fs::remove_dir(&squat_dir).unwrap();
                    break;
                } else {
                    continue;
                }
            }
        }

        let _ = image::save_buffer(&Path::new(&filename), &shot, shot.width(), shot.height(), image::ColorType::RGB(8));
    }
}
<commit_msg>Stable screenshot, now fully Rust 1.0 compliant<commit_after>use super::canvas::{Canvas, Image, FONT_W};
use super::{WidgetId};
use ::{V2, Rect, ToColor, color};
use ::Anchor::*;

/// Helper methods for canvas context that do not depend on the underlying
/// implementation details.
pub trait CanvasUtil {
    /// Draw a thick solid line on the canvas.
    fn draw_line<C: ToColor+Copy>(&mut self, width: f32, p1: V2<f32>, p2: V2<f32>, layer: f32, color: &C);
    /// Get the size of an atlas image.
    fn image_dim(&self, img: Image) -> V2<u32>;

    /// Draw a stored image on the canvas.
    fn draw_image<C: ToColor+Copy, D: ToColor+Copy>(&mut self, img: Image,
        offset: V2<f32>, z: f32, color: &C, back_color: &D);

    /// Draw a filled rectangle.
    fn fill_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C);

    /// Draw a wireframe rectangle.
    fn draw_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C);

    /// Draw an immediate GUI button and return whether it was pressed.
    fn button(&mut self, id: WidgetId, pos: V2<f32>, z: f32) -> bool;

    fn draw_char<C: ToColor+Copy, D: ToColor+Copy>(&mut self, c: char, offset: V2<f32>, z: f32, color: &C, border: Option<&D>);

    fn char_width(&self, c: char) -> f32;

    /// Write a timestamped screenshot PNG to disk.
    fn save_screenshot(&mut self, basename: &str);
}

impl<'a> CanvasUtil for Canvas<'a> {
    fn draw_line<C: ToColor+Copy>(&mut self, width: f32, p1: V2<f32>, p2: V2<f32>, layer: f32, color: &C) {
        if p1 == p2 { return; }

        let tex = self.solid_tex_coord();

        // The front vector. Extend by width.
        let v1 = p2 - p1;
        let scalar = v1.dot(v1);
        let scalar = (scalar + width * width) / scalar;
        let v1 = v1 * scalar;

        // The sideways vector, turn into unit vector, then multiply by half the width.
        let v2 = V2(-v1.1, v1.0);
        let scalar = width / 2.0 * 1.0 / v2.dot(v2).sqrt();
        let v2 = v2 * scalar;

        let ind0 = self.num_vertices();
        self.push_vertex(p1 + v2, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 - v2, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 - v2 + v1, layer, tex, color, &color::BLACK);
        self.push_vertex(p1 + v2 + v1, layer, tex, color, &color::BLACK);
        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn image_dim(&self, img: Image) -> V2<u32> {
        self.image_data(img).pos.1.map(|x| x as u32)
    }

    fn draw_image<C: ToColor+Copy, D: ToColor+Copy>(&mut self, img: Image,
        offset: V2<f32>, z: f32, color: &C, back_color: &D) {
        // Use round numbers, fractions seem to cause artifacts to pixels.
        let offset = offset.map(|x| x.floor());
        let mut pos;
        let mut tex;
        {
            let data = self.image_data(img);
            pos = data.pos + offset;
            tex = data.tex;
        }

        let ind0 = self.num_vertices();

        self.push_vertex(pos.point(TopLeft), z, tex.point(TopLeft), color, back_color);
        self.push_vertex(pos.point(TopRight), z, tex.point(TopRight), color, back_color);
        self.push_vertex(pos.point(BottomRight), z, tex.point(BottomRight), color, back_color);
        self.push_vertex(pos.point(BottomLeft), z, tex.point(BottomLeft), color, back_color);

        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn fill_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C) {
        let tex = self.solid_tex_coord();
        let ind0 = self.num_vertices();

        self.push_vertex(rect.point(TopLeft), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(TopRight), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(BottomRight), z, tex, color, &color::BLACK);
        self.push_vertex(rect.point(BottomLeft), z, tex, color, &color::BLACK);

        self.push_triangle(ind0, ind0 + 1, ind0 + 2);
        self.push_triangle(ind0, ind0 + 2, ind0 + 3);

        self.flush();
    }

    fn draw_rect<C: ToColor+Copy>(&mut self, rect: &Rect<f32>, z: f32, color: &C) {
        self.draw_line(1.0, rect.point(TopLeft), rect.point(TopRight) - V2(1.0, 0.0), z, color);
        self.draw_line(1.0, rect.point(TopRight) - V2(1.0, 0.0), rect.point(BottomRight) - V2(1.0, 0.0), z, color);
        self.draw_line(1.0, rect.point(BottomLeft) - V2(0.0, 1.0), rect.point(BottomRight) - V2(1.0, 1.0), z, color);
        self.draw_line(1.0, rect.point(TopLeft), rect.point(BottomLeft), z, color);
    }

    fn button(&mut self, id: WidgetId, pos: V2<f32>, z: f32) -> bool {
        // TODO: Button visual style! Closures?
        let area = Rect(pos, V2(64.0, 16.0));
        let mut color = color::GREEN;
        if area.contains(&self.mouse_pos) {
            self.hot_widget = Some(id);
            if self.active_widget.is_none() && self.mouse_pressed {
                self.active_widget = Some(id);
            }
            color = color::RED;
        }

        self.fill_rect(&area, z, &color);

        return !self.mouse_pressed // Mouse is released
            && self.active_widget == Some(id) // But this button is hot and active
            && self.hot_widget == Some(id);
    }

    fn draw_char<C: ToColor+Copy, D: ToColor+Copy>(&mut self, c: char, offset: V2<f32>, z: f32, color: &C, border: Option<&D>) {
        static BORDER: [V2<f32>; 8] =
            [V2(-1.0, -1.0), V2( 0.0, -1.0), V2( 1.0, -1.0),
             V2(-1.0,  0.0),                 V2( 1.0,  0.0),
             V2(-1.0,  1.0), V2( 0.0,  1.0), V2( 1.0,  1.0)];
        if let Some(img) = self.font_image(c) {
            if let Some(b) = border {
                // Put the border a tiny bit further in the z-buffer so it
                // won't clobber the text on the same layer.
                let border_z = z + 0.00001;
                for &d in BORDER.iter() {
                    self.draw_image(img, offset + d, border_z, b, &color::BLACK);
                }
            }
            self.draw_image(img, offset, z, color, &color::BLACK);
        }
    }

    fn char_width(&self, c: char) -> f32 {
        // Special case for space, the atlas image won't have a width.
        if c == ' ' { return (FONT_W / 2) as f32; }

        // Infer letter width from the cropped atlas image. (Use mx instead of
        // dim on the pos rectangle so that the left-side space will be
        // preserved and the letters are kept slightly apart.)
        if let Some(img) = self.font_image(c) {
            let width = self.image_data(img).pos.mx().0;
            return width;
        }

        // Not a valid letter.
        (FONT_W / 2) as f32
    }

    fn save_screenshot(&mut self, basename: &str) {
        use time;
        use std::path::{Path};
        use std::fs::{self, File};
        use image;

        let shot = self.screenshot();

        let timestamp = time::precise_time_s() as u64;
        // Create screenshot filenames by concatenating the current timestamp in
        // seconds with a running number from 00 to 99. 100 shots per second
        // should be good enough.

        // Default if we fail to generate any of the 100 candidates for this
        // second, just overwrite with the "xx" prefix then.
        let mut filename = format!("{}-{}{}.png", basename, timestamp, "xx");

        // Run through candidates for this second.
        for i in 0..100 {
            let test_filename = format!("{}-{}{:02}.png", basename, timestamp, i);
            // If file does not exist.
            if fs::metadata(&test_filename).is_err() {
                // Thread-safe claiming: create_dir will fail if the dir
                // already exists (it'll exist if another thread is gunning
                // for the same filename and managed to get past us here).
                // At least assuming that create_dir is atomic...
                let squat_dir = format!(".tmp-{}{:02}", timestamp, i);
                if fs::create_dir(&squat_dir).is_ok() {
                    File::create(&test_filename).unwrap();
                    filename = test_filename;
                    fs::remove_dir(&squat_dir).unwrap();
                    break;
                } else {
                    continue;
                }
            }
        }

        let _ = image::save_buffer(&Path::new(&filename), &shot, shot.width(), shot.height(), image::ColorType::RGB(8));
    }
}
<|endoftext|>