<commit_before>use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf;

use cargo_edit::{
    colorize_stderr, find, get_latest_dependency, registry_url, resolve_manifests, set_dep_version,
    shell_note, shell_status, shell_warn, update_registry_index, CargoResult, Context, CrateSpec,
    Dependency, LocalManifest,
};
use clap::Args;
use indexmap::IndexMap;
use semver::{Op, VersionReq};
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};

/// Upgrade dependencies as specified in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[clap(version)]
#[clap(after_help = "\
This command differs from `cargo update`, which updates the dependency versions recorded in the \
local lock file (Cargo.lock).

If `<dependency>`(s) are provided, only the specified dependencies will be upgraded. The version \
to upgrade to for each can be specified with e.g. `docopt@0.8.0` or `serde@>=0.9,<2.0`.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io \
are supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--workspace` flag is supplied. The \
`--workspace` flag may be supplied in the presence of a virtual manifest.

If the '--to-lockfile' flag is supplied, all dependencies will be upgraded to the currently locked \
version as recorded in the Cargo.lock file. This flag requires that the Cargo.lock file is \
up-to-date. If the lock file is missing, or it needs to be updated, cargo-upgrade will exit with \
an error. If the '--to-lockfile' flag is supplied then the network won't be accessed.")]
pub struct UpgradeArgs {
    /// Crates to be upgraded.
    dependency: Vec<String>,

    /// Path to the manifest to upgrade
    #[clap(long, value_name = "PATH", parse(from_os_str))]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to add this dependency to.
    #[clap(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Vec<String>,

    /// Upgrade all packages in the workspace.
    #[clap(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Upgrade all packages in the workspace.
    #[clap(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[clap(long)]
    dry_run: bool,

    /// Upgrade dependencies pinned in the manifest.
    #[clap(long)]
    pinned: bool,

    /// Run without accessing the network
    #[clap(long)]
    offline: bool,

    /// Upgrade all packages to the version in the lockfile.
    #[clap(long, conflicts_with = "dependency")]
    to_lockfile: bool,

    /// Crates to exclude and not upgrade.
    #[clap(long)]
    exclude: Vec<String>,

    /// Require `Cargo.toml` to be up to date
    #[clap(long)]
    locked: bool,

    /// Use verbose output
    #[clap(short, long)]
    verbose: bool,

    /// Unstable (nightly-only) flags
    #[clap(short = 'Z', value_name = "FLAG", global = true, arg_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl UpgradeArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }

    fn workspace(&self) -> bool {
        self.all || self.workspace
    }

    fn resolve_targets(&self) -> CargoResult<Vec<cargo_metadata::Package>> {
        resolve_manifests(
            self.manifest_path.as_deref(),
            self.workspace(),
            self.pkgid.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
        )
    }

    fn verbose<F>(&self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut() -> CargoResult<()>,
    {
        if self.verbose {
            callback()
        } else {
            Ok(())
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ArgEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: UpgradeArgs) -> CargoResult<()> {
    if args.all {
        deprecated_message("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }

    if !args.offline && !args.to_lockfile {
        let url = registry_url(&find(args.manifest_path.as_deref())?, None)?;
        update_registry_index(&url, false)?;
    }

    let manifests = args.resolve_targets()?;
    let locked = args
        .to_lockfile
        .then(|| load_lockfile(&manifests, args.offline))
        .transpose()?
        .unwrap_or_default();

    let selected_dependencies = args
        .dependency
        .iter()
        .map(|name| {
            let spec = CrateSpec::resolve(name)?;
            Ok((spec.name, spec.version_req))
        })
        .collect::<CargoResult<IndexMap<_, _>>>()?;
    let mut processed_keys = BTreeSet::new();

    let mut updated_registries = BTreeSet::new();
    let mut any_crate_modified = false;
    let mut compatible_present = false;
    let mut pinned_present = false;
    for package in manifests {
        let mut manifest = LocalManifest::try_new(package.manifest_path.as_std_path())?;
        let mut crate_modified = false;
        let manifest_path = manifest.path.clone();
        shell_status("Checking", &format!("{}'s dependencies", package.name))?;
        for dep_table in manifest.get_dependency_tables_mut() {
            for (dep_key, dep_item) in dep_table.iter_mut() {
                let dep_key = dep_key.get();
                processed_keys.insert(dep_key.to_owned());
                if !selected_dependencies.is_empty() && !selected_dependencies.contains_key(dep_key)
                {
                    args.verbose(|| {
                        shell_warn(&format!("ignoring {}, excluded by user", dep_key))
                    })?;
                    continue;
                }
                if args.exclude.contains(&dep_key.to_owned()) {
                    args.verbose(|| {
                        shell_warn(&format!("ignoring {}, excluded by user", dep_key))
                    })?;
                    continue;
                }
                let dependency = match Dependency::from_toml(&manifest_path, dep_key, dep_item) {
                    Ok(dependency) => dependency,
                    Err(err) => {
                        shell_warn(&format!("ignoring {}, invalid entry: {}", dep_key, err))?;
                        continue;
                    }
                };
                let old_version_req = match dependency.version() {
                    Some(version_req) => version_req.to_owned(),
                    None => {
                        args.verbose(|| {
                            let source = dependency
                                .source()
                                .map(|s| s.to_string())
                                .unwrap_or_else(|| "unknown".to_owned());
                            shell_warn(&format!(
                                "ignoring {}, source is {}",
                                dependency.toml_key(),
                                source,
                            ))
                        })?;
                        continue;
                    }
                };
                if !args.pinned {
                    if dependency.rename.is_some() {
                        args.verbose(|| {
                            shell_warn(&format!(
                                "ignoring {}, renamed dependencies are pinned",
                                dependency.toml_key(),
                            ))
                        })?;
                        pinned_present = true;
                        continue;
                    }

                    if let Ok(version_req) = VersionReq::parse(&old_version_req) {
                        if version_req.comparators.iter().any(|comparator| {
                            matches!(comparator.op, Op::Exact | Op::Less | Op::LessEq)
                        }) {
                            args.verbose(|| {
                                shell_warn(&format!(
                                    "ignoring {}, version ({}) is pinned",
                                    dependency.toml_key(),
                                    old_version_req
                                ))
                            })?;
                            pinned_present = true;
                            continue;
                        }
                    }
                }

                let new_version_req = if let Some(Some(new_version_req)) =
                    selected_dependencies.get(dependency.toml_key())
                {
                    new_version_req.to_owned()
                } else {
                    // Not checking `selected_dependencies.is_empty`, it was checked earlier
                    let new_version = if args.to_lockfile {
                        match find_locked_version(&dependency.name, &old_version_req, &locked) {
                            Some(new_version) => new_version,
                            None => {
                                args.verbose(|| {
                                    shell_warn(&format!(
                                        "ignoring {}, could not find package: not in lock file",
                                        dependency.toml_key(),
                                    ))
                                })?;
                                continue;
                            }
                        }
                    } else {
                        if dependency
                            .source
                            .as_ref()
                            .and_then(|s| s.as_registry())
                            .is_none()
                        {
                            args.verbose(|| {
                                let source = dependency
                                    .source()
                                    .map(|s| s.to_string())
                                    .unwrap_or_else(|| "unknown".to_owned());
                                shell_warn(&format!(
                                    "ignoring {}, source is {}",
                                    dependency.toml_key(),
                                    source
                                ))
                            })?;
                            continue;
                        }
                        // Update indices for any alternative registries, unless
                        // we're offline.
                        let registry_url = dependency
                            .registry()
                            .map(|registry| registry_url(&manifest_path, Some(registry)))
                            .transpose()?;
                        if !args.offline {
                            if let Some(registry_url) = &registry_url {
                                if updated_registries.insert(registry_url.to_owned()) {
                                    update_registry_index(registry_url, false)?;
                                }
                            }
                        }
                        let is_prerelease = old_version_req.contains('-');
                        let new_version = get_latest_dependency(
                            &dependency.name,
                            is_prerelease,
                            &manifest_path,
                            registry_url.as_ref(),
                        )
                        .map(|d| {
                            d.version()
                                .expect("registry packages always have a version")
                                .to_owned()
                        });
                        let new_version = match new_version {
                            Ok(new_version) => new_version,
                            Err(err) => {
                                args.verbose(|| {
                                    shell_warn(&format!(
                                        "ignoring {}, could not find package: {}",
                                        dependency.toml_key(),
                                        err
                                    ))
                                })?;
                                continue;
                            }
                        };
                        if old_version_compatible(&old_version_req, &new_version) {
                            args.verbose(|| {
                                shell_warn(&format!(
                                    "ignoring {}, version ({}) is compatible with {}",
                                    dependency.toml_key(),
                                    old_version_req,
                                    new_version
                                ))
                            })?;
                            compatible_present = true;
                            continue;
                        }
                        new_version
                    };
                    let mut new_version_req = new_version;
                    let new_ver: semver::Version = new_version_req.parse()?;
                    match cargo_edit::upgrade_requirement(&old_version_req, &new_ver) {
                        Ok(Some(version)) => {
                            new_version_req = version;
                        }
                        Err(_) => {}
                        _ => {
                            new_version_req = old_version_req.clone();
                        }
                    }
                    new_version_req
                };
                if new_version_req == old_version_req {
                    args.verbose(|| {
                        shell_warn(&format!(
                            "ignoring {}, version ({}) is unchanged",
                            dependency.toml_key(),
                            new_version_req
                        ))
                    })?;
                    continue;
                }
                print_upgrade(dependency.toml_key(), &old_version_req, &new_version_req)?;
                set_dep_version(dep_item, &new_version_req)?;
                crate_modified = true;
                any_crate_modified = true;
            }
        }
        if !args.dry_run && !args.locked && crate_modified {
            manifest.write()?;
        }
    }

    if args.locked && any_crate_modified {
        anyhow::bail!("cannot upgrade due to `--locked`");
    }

    let unused = selected_dependencies
        .keys()
        .filter(|k| !processed_keys.contains(k.as_str()))
        .map(|k| k.as_str())
        .collect::<Vec<_>>();
    match unused.len() {
        0 => {}
        1 => anyhow::bail!("dependency {} doesn't exist", unused.join(", ")),
        _ => anyhow::bail!("dependencies {} don't exist", unused.join(", ")),
    }

    if pinned_present {
        shell_note("Re-run with `--pinned` to upgrade pinned version requirements")?;
    }
    if compatible_present {
        shell_note("Re-run with `--to-lockfile` to upgrade compatible version requirements")?;
    }

    if args.dry_run {
        shell_warn("aborting upgrade due to dry run")?;
    }

    Ok(())
}

fn load_lockfile(
    targets: &[cargo_metadata::Package],
    offline: bool,
) -> CargoResult<Vec<cargo_metadata::Package>> {
    // Get locked dependencies. For workspaces with multiple Cargo.toml
    // files, there is only a single lockfile, so it suffices to get
    // metadata for any one of Cargo.toml files.
    let package = targets
        .get(0)
        .ok_or_else(|| anyhow::format_err!("Invalid cargo config"))?;
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.manifest_path(package.manifest_path.clone());
    cmd.features(cargo_metadata::CargoOpt::AllFeatures);
    let mut other = vec!["--locked".to_owned()];
    if offline {
        other.push("--offline".to_owned());
    }
    cmd.other_options(other);

    let result = cmd.exec()?;

    let locked = result.packages;

    Ok(locked)
}

fn find_locked_version(
    dep_name: &str,
    old_version: &str,
    locked: &[cargo_metadata::Package],
) -> Option<String> {
    let req = semver::VersionReq::parse(old_version).ok()?;
    for p in locked {
        if dep_name == p.name && req.matches(&p.version) {
            return Some(p.version.to_string());
        }
    }
    None
}

fn old_version_compatible(old_version_req: &str, new_version: &str) -> bool {
    let old_version_req = match VersionReq::parse(old_version_req) {
        Ok(req) => req,
        Err(_) => return false,
    };

    let new_version = match semver::Version::parse(new_version) {
        Ok(new_version) => new_version,
        // HACK: Skip compatibility checks on incomplete version reqs
        Err(_) => return false,
    };

    old_version_req.matches(&new_version)
}

fn deprecated_message(message: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let mut output = StandardStream::stderr(colorchoice);
    output
        .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    writeln!(output, "{}", message).with_context(|| "Failed to write deprecated message")?;
    output
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    Ok(())
}

/// Print a message if the new dependency version is different from the old one.
fn print_upgrade(dep_name: &str, old_version: &str, new_version: &str) -> CargoResult<()> {
    let old_version = format_version_req(old_version);
    let new_version = format_version_req(new_version);

    shell_status(
        "Upgrading",
        &format!("{dep_name}: {old_version} -> {new_version}"),
    )?;

    Ok(())
}

fn format_version_req(version: &str) -> String {
    if version.chars().next().unwrap_or('0').is_ascii_digit() {
        format!("v{}", version)
    } else {
        version.to_owned()
    }
}
<commit_msg>fix(upgrade): Adjust responsibility for errors<commit_after>use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf;

use cargo_edit::{
    colorize_stderr, find, get_latest_dependency, registry_url, resolve_manifests, set_dep_version,
    shell_note, shell_status, shell_warn, update_registry_index, CargoResult, Context, CrateSpec,
    Dependency, LocalManifest,
};
use clap::Args;
use indexmap::IndexMap;
use semver::{Op, VersionReq};
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};

/// Upgrade dependencies as specified in the local manifest file (i.e. Cargo.toml).
#[derive(Debug, Args)]
#[clap(version)]
#[clap(after_help = "\
This command differs from `cargo update`, which updates the dependency versions recorded in the \
local lock file (Cargo.lock).

If `<dependency>`(s) are provided, only the specified dependencies will be upgraded. The version \
to upgrade to for each can be specified with e.g. `docopt@0.8.0` or `serde@>=0.9,<2.0`.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io \
are supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--workspace` flag is supplied. The \
`--workspace` flag may be supplied in the presence of a virtual manifest.

If the '--to-lockfile' flag is supplied, all dependencies will be upgraded to the currently locked \
version as recorded in the Cargo.lock file. This flag requires that the Cargo.lock file is \
up-to-date. If the lock file is missing, or it needs to be updated, cargo-upgrade will exit with \
an error. If the '--to-lockfile' flag is supplied then the network won't be accessed.")]
pub struct UpgradeArgs {
    /// Crates to be upgraded.
    dependency: Vec<String>,

    /// Path to the manifest to upgrade
    #[clap(long, value_name = "PATH", parse(from_os_str))]
    manifest_path: Option<PathBuf>,

    /// Package id of the crate to add this dependency to.
    #[clap(
        long = "package",
        short = 'p',
        value_name = "PKGID",
        conflicts_with = "all",
        conflicts_with = "workspace"
    )]
    pkgid: Vec<String>,

    /// Upgrade all packages in the workspace.
    #[clap(
        long,
        help = "[deprecated in favor of `--workspace`]",
        conflicts_with = "workspace",
        conflicts_with = "pkgid"
    )]
    all: bool,

    /// Upgrade all packages in the workspace.
    #[clap(long, conflicts_with = "all", conflicts_with = "pkgid")]
    workspace: bool,

    /// Print changes to be made without making them.
    #[clap(long)]
    dry_run: bool,

    /// Upgrade dependencies pinned in the manifest.
    #[clap(long)]
    pinned: bool,

    /// Run without accessing the network
    #[clap(long)]
    offline: bool,

    /// Upgrade all packages to the version in the lockfile.
    #[clap(long, conflicts_with = "dependency")]
    to_lockfile: bool,

    /// Crates to exclude and not upgrade.
    #[clap(long)]
    exclude: Vec<String>,

    /// Require `Cargo.toml` to be up to date
    #[clap(long)]
    locked: bool,

    /// Use verbose output
    #[clap(short, long)]
    verbose: bool,

    /// Unstable (nightly-only) flags
    #[clap(short = 'Z', value_name = "FLAG", global = true, arg_enum)]
    unstable_features: Vec<UnstableOptions>,
}

impl UpgradeArgs {
    pub fn exec(self) -> CargoResult<()> {
        exec(self)
    }

    fn workspace(&self) -> bool {
        self.all || self.workspace
    }

    fn resolve_targets(&self) -> CargoResult<Vec<cargo_metadata::Package>> {
        resolve_manifests(
            self.manifest_path.as_deref(),
            self.workspace(),
            self.pkgid.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
        )
    }

    fn verbose<F>(&self, mut callback: F) -> CargoResult<()>
    where
        F: FnMut() -> CargoResult<()>,
    {
        if self.verbose {
            callback()
        } else {
            Ok(())
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, clap::ArgEnum)]
enum UnstableOptions {}

/// Main processing function. Allows us to return a `Result` so that `main` can print pretty error
/// messages.
fn exec(args: UpgradeArgs) -> CargoResult<()> {
    if args.all {
        deprecated_message("The flag `--all` has been deprecated in favor of `--workspace`")?;
    }

    if !args.offline && !args.to_lockfile {
        let url = registry_url(&find(args.manifest_path.as_deref())?, None)?;
        update_registry_index(&url, false)?;
    }

    let manifests = args.resolve_targets()?;
    let locked = args
        .to_lockfile
        .then(|| load_lockfile(&manifests, args.offline))
        .transpose()?
        .unwrap_or_default();

    let selected_dependencies = args
        .dependency
        .iter()
        .map(|name| {
            let spec = CrateSpec::resolve(name)?;
            Ok((spec.name, spec.version_req))
        })
        .collect::<CargoResult<IndexMap<_, _>>>()?;
    let mut processed_keys = BTreeSet::new();

    let mut updated_registries = BTreeSet::new();
    let mut any_crate_modified = false;
    let mut compatible_present = false;
    let mut pinned_present = false;
    for package in manifests {
        let mut manifest = LocalManifest::try_new(package.manifest_path.as_std_path())?;
        let mut crate_modified = false;
        let manifest_path = manifest.path.clone();
        shell_status("Checking", &format!("{}'s dependencies", package.name))?;
        for dep_table in manifest.get_dependency_tables_mut() {
            for (dep_key, dep_item) in dep_table.iter_mut() {
                let dep_key = dep_key.get();
                processed_keys.insert(dep_key.to_owned());
                if !selected_dependencies.is_empty() && !selected_dependencies.contains_key(dep_key)
                {
                    args.verbose(|| {
                        shell_warn(&format!("ignoring {}, excluded by user", dep_key))
                    })?;
                    continue;
                }
                if args.exclude.contains(&dep_key.to_owned()) {
                    args.verbose(|| {
                        shell_warn(&format!("ignoring {}, excluded by user", dep_key))
                    })?;
                    continue;
                }
                let dependency = match Dependency::from_toml(&manifest_path, dep_key, dep_item) {
                    Ok(dependency) => dependency,
                    Err(err) => {
                        shell_warn(&format!("ignoring {}, unsupported entry: {}", dep_key, err))?;
                        continue;
                    }
                };
                let old_version_req = match dependency.version() {
                    Some(version_req) => version_req.to_owned(),
                    None => {
                        args.verbose(|| {
                            let source = dependency
                                .source()
                                .map(|s| s.to_string())
                                .unwrap_or_else(|| "unknown".to_owned());
                            shell_warn(&format!(
                                "ignoring {}, source is {}",
                                dependency.toml_key(),
                                source,
                            ))
                        })?;
                        continue;
                    }
                };
                if !args.pinned {
                    if dependency.rename.is_some() {
                        args.verbose(|| {
                            shell_warn(&format!(
                                "ignoring {}, renamed dependencies are pinned",
                                dependency.toml_key(),
                            ))
                        })?;
                        pinned_present = true;
                        continue;
                    }

                    if let Ok(version_req) = VersionReq::parse(&old_version_req) {
                        if version_req.comparators.iter().any(|comparator| {
                            matches!(comparator.op, Op::Exact | Op::Less | Op::LessEq)
                        }) {
                            args.verbose(|| {
                                shell_warn(&format!(
                                    "ignoring {}, version ({}) is pinned",
                                    dependency.toml_key(),
                                    old_version_req
                                ))
                            })?;
                            pinned_present = true;
                            continue;
                        }
                    }
                }

                let new_version_req = if let Some(Some(new_version_req)) =
                    selected_dependencies.get(dependency.toml_key())
                {
                    new_version_req.to_owned()
                } else {
                    // Not checking `selected_dependencies.is_empty`, it was checked earlier
                    let new_version = if args.to_lockfile {
                        match find_locked_version(&dependency.name, &old_version_req, &locked) {
                            Some(new_version) => new_version,
                            None => {
                                args.verbose(|| {
                                    shell_warn(&format!(
                                        "ignoring {}, could not find package: not in lock file",
                                        dependency.toml_key(),
                                    ))
                                })?;
                                continue;
                            }
                        }
                    } else {
                        if dependency
                            .source
                            .as_ref()
                            .and_then(|s| s.as_registry())
                            .is_none()
                        {
                            args.verbose(|| {
                                let source = dependency
                                    .source()
                                    .map(|s| s.to_string())
                                    .unwrap_or_else(|| "unknown".to_owned());
                                shell_warn(&format!(
                                    "ignoring {}, source is {}",
                                    dependency.toml_key(),
                                    source
                                ))
                            })?;
                            continue;
                        }
                        // Update indices for any alternative registries, unless
                        // we're offline.
                        let registry_url = dependency
                            .registry()
                            .map(|registry| registry_url(&manifest_path, Some(registry)))
                            .transpose()?;
                        if !args.offline {
                            if let Some(registry_url) = &registry_url {
                                if updated_registries.insert(registry_url.to_owned()) {
                                    update_registry_index(registry_url, false)?;
                                }
                            }
                        }
                        let is_prerelease = old_version_req.contains('-');
                        let new_version = get_latest_dependency(
                            &dependency.name,
                            is_prerelease,
                            &manifest_path,
                            registry_url.as_ref(),
                        )
                        .map(|d| {
                            d.version()
                                .expect("registry packages always have a version")
                                .to_owned()
                        });
                        let new_version = match new_version {
                            Ok(new_version) => new_version,
                            Err(err) => {
                                args.verbose(|| {
                                    shell_warn(&format!(
                                        "ignoring {}, could not find package: {}",
                                        dependency.toml_key(),
                                        err
                                    ))
                                })?;
                                continue;
                            }
                        };
                        if old_version_compatible(&old_version_req, &new_version) {
                            args.verbose(|| {
                                shell_warn(&format!(
                                    "ignoring {}, version ({}) is compatible with {}",
                                    dependency.toml_key(),
                                    old_version_req,
                                    new_version
                                ))
                            })?;
                            compatible_present = true;
                            continue;
                        }
                        new_version
                    };
                    let mut new_version_req = new_version;
                    let new_ver: semver::Version = new_version_req.parse()?;
                    match cargo_edit::upgrade_requirement(&old_version_req, &new_ver) {
                        Ok(Some(version)) => {
                            new_version_req = version;
                        }
                        Err(_) => {}
                        _ => {
                            new_version_req = old_version_req.clone();
                        }
                    }
                    new_version_req
                };
                if new_version_req == old_version_req {
                    args.verbose(|| {
                        shell_warn(&format!(
                            "ignoring {}, version ({}) is unchanged",
                            dependency.toml_key(),
                            new_version_req
                        ))
                    })?;
                    continue;
                }
                print_upgrade(dependency.toml_key(), &old_version_req, &new_version_req)?;
                set_dep_version(dep_item, &new_version_req)?;
                crate_modified = true;
                any_crate_modified = true;
            }
        }
        if !args.dry_run && !args.locked && crate_modified {
            manifest.write()?;
        }
    }

    if args.locked && any_crate_modified {
        anyhow::bail!("cannot upgrade due to `--locked`");
    }

    let unused = selected_dependencies
        .keys()
        .filter(|k| !processed_keys.contains(k.as_str()))
        .map(|k| k.as_str())
        .collect::<Vec<_>>();
    match unused.len() {
        0 => {}
        1 => anyhow::bail!("dependency {} doesn't exist", unused.join(", ")),
        _ => anyhow::bail!("dependencies {} don't exist", unused.join(", ")),
    }

    if pinned_present {
        shell_note("Re-run with `--pinned` to upgrade pinned version requirements")?;
    }
    if compatible_present {
        shell_note("Re-run with `--to-lockfile` to upgrade compatible version requirements")?;
    }

    if args.dry_run {
        shell_warn("aborting upgrade due to dry run")?;
    }

    Ok(())
}

fn load_lockfile(
    targets: &[cargo_metadata::Package],
    offline: bool,
) -> CargoResult<Vec<cargo_metadata::Package>> {
    // Get locked dependencies. For workspaces with multiple Cargo.toml
    // files, there is only a single lockfile, so it suffices to get
    // metadata for any one of Cargo.toml files.
    let package = targets
        .get(0)
        .ok_or_else(|| anyhow::format_err!("Invalid cargo config"))?;
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.manifest_path(package.manifest_path.clone());
    cmd.features(cargo_metadata::CargoOpt::AllFeatures);
    let mut other = vec!["--locked".to_owned()];
    if offline {
        other.push("--offline".to_owned());
    }
    cmd.other_options(other);

    let result = cmd.exec()?;

    let locked = result.packages;

    Ok(locked)
}

fn find_locked_version(
    dep_name: &str,
    old_version: &str,
    locked: &[cargo_metadata::Package],
) -> Option<String> {
    let req = semver::VersionReq::parse(old_version).ok()?;
    for p in locked {
        if dep_name == p.name && req.matches(&p.version) {
            return Some(p.version.to_string());
        }
    }
    None
}

fn old_version_compatible(old_version_req: &str, new_version: &str) -> bool {
    let old_version_req = match VersionReq::parse(old_version_req) {
        Ok(req) => req,
        Err(_) => return false,
    };

    let new_version = match semver::Version::parse(new_version) {
        Ok(new_version) => new_version,
        // HACK: Skip compatibility checks on incomplete version reqs
        Err(_) => return false,
    };

    old_version_req.matches(&new_version)
}

fn deprecated_message(message: &str) -> CargoResult<()> {
    let colorchoice = colorize_stderr();
    let mut output = StandardStream::stderr(colorchoice);
    output
        .set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))
        .with_context(|| "Failed to set output colour")?;
    writeln!(output, "{}", message).with_context(|| "Failed to write deprecated message")?;
    output
        .set_color(&ColorSpec::new())
        .with_context(|| "Failed to clear output colour")?;
    Ok(())
}

/// Print a message if the new dependency version is different from the old one.
fn print_upgrade(dep_name: &str, old_version: &str, new_version: &str) -> CargoResult<()> {
    let old_version = format_version_req(old_version);
    let new_version = format_version_req(new_version);

    shell_status(
        "Upgrading",
        &format!("{dep_name}: {old_version} -> {new_version}"),
    )?;

    Ok(())
}

fn format_version_req(version: &str) -> String {
    if version.chars().next().unwrap_or('0').is_ascii_digit() {
        format!("v{}", version)
    } else {
        version.to_owned()
    }
}
<|endoftext|>


<commit_before>use std::ops::Add;
use typenum::uint::Unsigned;
use typenum::consts::U1;

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

impl<T, U> Variance for (T, U)
    where T: Variance,
          <T::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          U: TensorIndex
{
    type Rank = <T::Rank as Add<U1>>::Output;

    fn variance() -> Vec<IndexType> {
        let mut result = T::variance();
        result.push(U::index_type());
        result
    }
}

pub trait Concat<T> {
    type Output;
}

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: Variance,
          U: TensorIndex,
          V: TensorIndex
{
    type Output = ((T, U), V);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex + Concat<U>,
          U: Variance,
          V: TensorIndex,
          <T as Concat<U>>::Output: Concat<V>
{
    type Output = <<T as Concat<U>>::Output as Concat<V>>::Output;
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: Variance,
          U: TensorIndex,
          (T, U): Concat<V>,
          V: Variance,
          W: TensorIndex
{
    type Output = (<(T, U) as Concat<V>>::Output, W);
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<<CovariantIndex as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(CovariantIndex, CovariantIndex) as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<CovariantIndex as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(ContravariantIndex, CovariantIndex) as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }
}<commit_msg>Added Index trait for variances<commit_after>use std::ops::{Add, Sub};
use typenum::uint::{Unsigned, UInt};
use typenum::bit::Bit;
use typenum::consts::{U0, U1};

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

/// Trait identifying a type as representing a tensor variance. It is implemented for
/// `CovariantIndex`, `ContravariantIndex` and tuples (Variance, Index).
pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

/// Trait identifying a type as representing a tensor index. It is implemented
/// for `CovariantIndex` and `ContravariantIndex`.
pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

/// Type representing a contravariant (upper) tensor index.
pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

/// Type representing a covariant (lower) tensor index.
pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

impl<T, U> Variance for (T, U)
    where U: Variance,
          <U::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          T: TensorIndex
{
    type Rank = <U::Rank as Add<U1>>::Output;

    fn variance() -> Vec<IndexType> {
        let mut result = vec![T::index_type()];
        result.append(&mut U::variance());
        result
    }
}

/// Operator trait used for concatenating two variances.
pub trait Concat<T> {
    type Output;
}

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: TensorIndex,
          V: TensorIndex,
          U: Variance + Concat<V>,
          <U as Concat<V>>::Output: Variance
{
    type Output = (T, <U as Concat<V>>::Output);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex,
          U: TensorIndex,
          V: Variance
{
    type Output = (T, (U, V));
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: TensorIndex,
          U: Variance + Concat<(V, W)>,
          V: TensorIndex,
          W: Variance
{
    type Output = (T, <U as Concat<(V, W)>>::Output);
}

/// Indexing operator trait: Output is equal to the index type at the given position
///
/// Warning: Indices are numbered starting from 0!
pub trait Index<T: Unsigned> {
    type Output;
}

impl Index<U0> for CovariantIndex {
    type Output = CovariantIndex;
}

impl Index<U0> for ContravariantIndex {
    type Output = ContravariantIndex;
}

impl<T, V, U, B> Index<UInt<U, B>> for (V, T)
    where V: TensorIndex,
          U: Unsigned,
          B: Bit,
          UInt<U, B>: Sub<U1>,
          <UInt<U, B> as Sub<U1>>::Output: Unsigned,
          T: Variance + Index<<UInt<U, B> as Sub<U1>>::Output>
{
    type Output = <T as Index<<UInt<U, B> as Sub<U1>>::Output>>::Output;
}

impl<T, V> Index<U0> for (V, T)
    where V: TensorIndex,
          T: Variance
{
    type Output = V;
}

#[cfg(test)]
mod test {
    use super::*;
    use typenum::consts::{U0, U1, U2};

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<<CovariantIndex as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(CovariantIndex, CovariantIndex) as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<CovariantIndex as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(ContravariantIndex, CovariantIndex) as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_index() {
        assert_eq!(<CovariantIndex as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U1>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U0>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U2>>::Output::index_type(),
            IndexType::Covariant);
    }
}<|endoftext|>


<commit_before>extern crate ransid;

use std::collections::{BTreeSet, VecDeque};

use orbclient::{Color, Event, EventOption, Window};
use syscall::Result;

#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[cold]
pub unsafe fn fast_copy64(dst: *mut u64, src: *const u64, len: usize) {
    asm!("cld
        rep movsq"
        :
        : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len)
        : "cc", "memory", "rdi", "rsi", "rcx"
        : "intel", "volatile");
}

#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[cold]
pub unsafe fn fast_set64(dst: *mut u64, src: u64, len: usize) {
    asm!("cld
        rep stosq"
        :
        : "{rdi}"(dst as usize), "{rax}"(src), "{rcx}"(len)
        : "cc", "memory", "rdi", "rcx"
        : "intel", "volatile");
}

pub struct Console {
    pub console: ransid::Console,
    pub window: Window,
    pub changed: BTreeSet<usize>,
    pub ctrl: bool,
    pub input: Vec<u8>,
    pub end_of_input: bool,
    pub cooked: VecDeque<u8>,
    pub requested: usize
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            console: ransid::Console::new(width as usize / 8, height as usize / 16),
            window: window,
            changed: BTreeSet::new(),
            ctrl: false,
            input: Vec::new(),
            end_of_input: false,
            cooked: VecDeque::new(),
            requested: 0
        }
    }

    pub fn input(&mut self, event: &Event) {
        let mut buf = vec![];

        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.scancode == 0x1D {
                    self.ctrl = key_event.pressed;
                } else if key_event.pressed {
                    match key_event.scancode {
                        0x47 => { // Home
                            buf.extend_from_slice(b"\x1B[H");
                        },
                        0x48 => { // Up
                            buf.extend_from_slice(b"\x1B[A");
                        },
                        0x49 => { // Page up
                            buf.extend_from_slice(b"\x1B[5~");
                        },
                        0x4B => { // Left
                            buf.extend_from_slice(b"\x1B[D");
                        },
                        0x4D => { // Right
                            buf.extend_from_slice(b"\x1B[C");
                        },
                        0x4F => { // End
                            buf.extend_from_slice(b"\x1B[F");
                        },
                        0x50 => { // Down
                            buf.extend_from_slice(b"\x1B[B");
                        },
                        0x51 => { // Page down
                            buf.extend_from_slice(b"\x1B[6~");
                        },
                        0x52 => { // Insert
                            buf.extend_from_slice(b"\x1B[2~");
                        },
                        0x53 => { // Delete
                            buf.extend_from_slice(b"\x1B[3~");
                        },
                        _ => {
                            let c = match key_event.character {
                                c @ 'A' ... 'Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char,
                                c @ 'a' ... 'z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char,
                                c => c
                            };

                            if c != '\0' {
                                buf.extend_from_slice(&[c as u8]);
                            }
                        }
                    }
                }
            },
            _ => () //TODO: Mouse in terminal
        }

        if self.console.raw_mode {
            for &b in buf.iter() {
                self.input.push(b);
            }
        } else {
            for &b in buf.iter() {
                match b {
                    b'\x03' => {
                        self.end_of_input = true;
                        let _ = self.write(b"^C\n", true);
                    },
                    b'\x08' | b'\x7F' => {
                        if let Some(_c) = self.cooked.pop_back() {
                            let _ = self.write(b"\x08", true);
                        }
                    },
                    b'\n' | b'\r' => {
                        self.cooked.push_back(b);
                        while let Some(c) = self.cooked.pop_front() {
                            self.input.push(c);
                        }
                        let _ = self.write(b"\n", true);
                    },
                    _ => {
                        self.cooked.push_back(b);
                        let _ = self.write(&[b], true);
                    }
                }
            }
        }
    }

    pub fn write(&mut self, buf: &[u8], sync: bool) -> Result<usize> {
        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            let color = self.console.background;
            self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color { data: color.data });
            self.changed.insert(y);
        }

        {
            let window = &mut self.window;
            let changed = &mut self.changed;
            self.console.write(buf, |event| {
                match event {
                    ransid::Event::Char { x, y, c, color, .. } => {
                        window.char(x as i32 * 8, y as i32 * 16, c, Color { data: color.data });/*, bold, false);*/
                        changed.insert(y);
                    },
                    ransid::Event::Rect { x, y, w, h, color } => {
                        window.rect(x as i32 * 8, y as i32 * 16, w as u32 * 8, h as u32 * 16, Color { data: color.data });
                        for y2 in y..y + h {
                            changed.insert(y2);
                        }
                    },
                    ransid::Event::Scroll { rows, color } => {
                        let rows = rows as u32 * 16;
                        let data = (color.data as u64) << 32 | color.data as u64;

                        let width = window.width()/2;
                        let height = window.height();
                        if rows > 0 && rows < height {
                            let off1 = rows * width;
                            let off2 = height * width - off1;
                            unsafe {
                                let data_ptr = window.data_mut().as_mut_ptr() as *mut u64;
                                fast_copy64(data_ptr, data_ptr.offset(off1 as isize), off2 as usize);
                                fast_set64(data_ptr.offset(off2 as isize), data, off1 as usize);
                            }
                        }

                        for y in 0..window.height()/16 {
                            changed.insert(y as usize);
                        }
                    }
                }
            });
        }

        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            let color = self.console.foreground;
            self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color { data: color.data });
            self.changed.insert(y as usize);
        }

        if ! self.console.raw_mode && sync {
            self.sync();
        }

        Ok(buf.len())
    }

    fn sync(&mut self) {
        /*
        let width = self.window.width;
        for change in self.changed.iter() {
            self.display.sync(0, change * 16, width, 16);
        }
        */
        if ! self.changed.is_empty() {
            self.window.sync();
        }
        self.changed.clear();
    }

    pub fn redraw(&mut self) {
        /*
        let width = self.window.width;
        let height = self.window.height;
        */
        self.window.sync();
        self.changed.clear();
    }
}
<commit_msg>Invert on cursor<commit_after>extern crate ransid;

use std::cmp;
use std::collections::{BTreeSet, VecDeque};

use orbclient::{Color, Event, EventOption, Window};
use syscall::Result;

#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[cold]
pub unsafe fn fast_copy64(dst: *mut u64, src: *const u64, len: usize) {
    asm!("cld
        rep movsq"
        :
        : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len)
        : "cc", "memory", "rdi", "rsi", "rcx"
        : "intel", "volatile");
}

#[cfg(target_arch = "x86_64")]
#[inline(always)]
#[cold]
pub unsafe fn fast_set64(dst: *mut u64, src: u64, len: usize) {
    asm!("cld
        rep stosq"
        :
        : "{rdi}"(dst as usize), "{rax}"(src), "{rcx}"(len)
        : "cc", "memory", "rdi", "rcx"
        : "intel", "volatile");
}

pub struct Console {
    pub console: ransid::Console,
    pub window: Window,
    pub changed: BTreeSet<usize>,
    pub ctrl: bool,
    pub input: Vec<u8>,
    pub end_of_input: bool,
    pub cooked: VecDeque<u8>,
    pub requested: usize
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            console: ransid::Console::new(width as usize / 8, height as usize / 16),
            window: window,
            changed: BTreeSet::new(),
            ctrl: false,
            input: Vec::new(),
            end_of_input: false,
            cooked: VecDeque::new(),
            requested: 0
        }
    }

    pub fn input(&mut self, event: &Event) {
        let mut buf = vec![];

        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.scancode == 0x1D {
                    self.ctrl = key_event.pressed;
                } else if key_event.pressed {
                    match key_event.scancode {
                        0x47 => { // Home
                            buf.extend_from_slice(b"\x1B[H");
                        },
                        0x48 => { // Up
                            buf.extend_from_slice(b"\x1B[A");
                        },
                        0x49 => { // Page up
                            buf.extend_from_slice(b"\x1B[5~");
                        },
                        0x4B => { // Left
                            buf.extend_from_slice(b"\x1B[D");
                        },
                        0x4D => { // Right
                            buf.extend_from_slice(b"\x1B[C");
                        },
                        0x4F => { // End
                            buf.extend_from_slice(b"\x1B[F");
                        },
                        0x50 => { // Down
                            buf.extend_from_slice(b"\x1B[B");
                        },
                        0x51 => { // Page down
                            buf.extend_from_slice(b"\x1B[6~");
                        },
                        0x52 => { // Insert
                            buf.extend_from_slice(b"\x1B[2~");
                        },
                        0x53 => { // Delete
                            buf.extend_from_slice(b"\x1B[3~");
                        },
                        _ => {
                            let c = match key_event.character {
                                c @ 'A' ... 'Z' if self.ctrl => ((c as u8 - b'A') + b'\x01') as char,
                                c @ 'a' ... 'z' if self.ctrl => ((c as u8 - b'a') + b'\x01') as char,
                                c => c
                            };

                            if c != '\0' {
                                buf.extend_from_slice(&[c as u8]);
                            }
                        }
                    }
                }
            },
            _ => () //TODO: Mouse in terminal
        }

        if self.console.raw_mode {
            for &b in buf.iter() {
                self.input.push(b);
            }
        } else {
            for &b in buf.iter() {
                match b {
                    b'\x03' => {
                        self.end_of_input = true;
                        let _ = self.write(b"^C\n", true);
                    },
                    b'\x08' | b'\x7F' => {
                        if let Some(_c) = self.cooked.pop_back() {
                            let _ = self.write(b"\x08", true);
                        }
                    },
                    b'\n' | b'\r' => {
                        self.cooked.push_back(b);
                        while let Some(c) = self.cooked.pop_front() {
                            self.input.push(c);
                        }
                        let _ = self.write(b"\n", true);
                    },
                    _ => {
                        self.cooked.push_back(b);
                        let _ = self.write(&[b], true);
                    }
                }
            }
        }
    }

    pub fn invert(&mut self, x: usize, y: usize, w: usize, h: usize) {
        let width = self.window.width() as usize;
        let height = self.window.height() as usize;

        let start_y = cmp::min(height - 1, y);
        let end_y = cmp::min(height, y + h);

        let start_x = cmp::min(width - 1, x);
        let len = cmp::min(width, x + w) - start_x;

        let mut offscreen_ptr = self.window.data_mut().as_mut_ptr() as usize;

        let stride = width * 4;

        let offset = y * stride + start_x * 4;
        offscreen_ptr += offset;

        let mut rows = end_y - start_y;
        while rows > 0 {
            let mut row_ptr = offscreen_ptr;
            let mut cols = len;
            while cols > 0 {
                unsafe {
                    let color = *(row_ptr as *mut u32);
                    *(row_ptr as *mut u32) = !color;
                }
                row_ptr += 4;
                cols -= 1;
            }
            offscreen_ptr += stride;
            rows -= 1;
        }
    }

    pub fn write(&mut self, buf: &[u8], sync: bool) -> Result<usize> {
        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            self.invert(x * 8, y * 16, 8, 16);
            self.changed.insert(y);
        }

        {
            let window = &mut self.window;
            let changed = &mut self.changed;
            self.console.write(buf, |event| {
                match event {
                    ransid::Event::Char { x, y, c, color, .. } => {
                        window.char(x as i32 * 8, y as i32 * 16, c, Color { data: color.data });/*, bold, false);*/
                        changed.insert(y);
                    },
                    ransid::Event::Rect { x, y, w, h, color } => {
                        window.rect(x as i32 * 8, y as i32 * 16, w as u32 * 8, h as u32 * 16, Color { data: color.data });
                        for y2 in y..y + h {
                            changed.insert(y2);
                        }
                    },
                    ransid::Event::Scroll { rows, color } => {
                        let rows = rows as u32 * 16;
                        let data = (color.data as u64) << 32 | color.data as u64;

                        let width = window.width()/2;
                        let height = window.height();
                        if rows > 0 && rows < height {
                            let off1 = rows * width;
                            let off2 = height * width - off1;
                            unsafe {
                                let data_ptr = window.data_mut().as_mut_ptr() as *mut u64;
                                fast_copy64(data_ptr, data_ptr.offset(off1 as isize), off2 as usize);
                                fast_set64(data_ptr.offset(off2 as isize), data, off1 as usize);
                            }
                        }

                        for y in 0..window.height()/16 {
                            changed.insert(y as usize);
                        }
                    }
                }
            });
        }

        if self.console.cursor && self.console.x < self.console.w && self.console.y < self.console.h {
            let x = self.console.x;
            let y = self.console.y;
            self.invert(x * 8, y * 16, 8, 16);
            self.changed.insert(y as usize);
        }

        if ! self.console.raw_mode && sync {
            self.sync();
        }

        Ok(buf.len())
    }

    fn sync(&mut self) {
        /*
        let width = self.window.width;
        for change in self.changed.iter() {
            self.display.sync(0, change * 16, width, 16);
        }
        */
        if ! self.changed.is_empty() {
            self.window.sync();
        }
        self.changed.clear();
    }

    pub fn redraw(&mut self) {
        /*
        let width = self.window.width;
        let height = self.window.height;
        */
        self.window.sync();
        self.changed.clear();
    }
}
<|endoftext|>


<commit_before>use petgraph::graph::NodeIndex;
use uuid::Uuid;
use rustwlc::{Geometry, Point};
use super::super::LayoutTree;
use super::super::core::container::{Container, ContainerType};

// TODO This module needs to be updated like the other modules...
// Need to add some errors for this (such as when trying to move a non-container/view,
// or when trying to grab a workspace whos name already exists)
//
// Also the code is generally pretty crap, cause it's pretty old (mid-2016)

impl LayoutTree {
    /// Gets a workspace by name or creates it
    fn get_or_make_workspace(&mut self, name: &str) -> NodeIndex {
        let active_index = self.active_ix_of(ContainerType::Output)
            .or_else(|| {
                self.tree.follow_path_until(self.tree.root_ix(), ContainerType::Output).ok()
            })
            .expect("get_or_make_wksp: Couldn't get output");
        let workspace_ix = self.tree.workspace_ix_by_name(name).unwrap_or_else(|| {
            let root_ix = self.init_workspace(name.to_string(), active_index);
            self.tree.parent_of(root_ix)
                .expect("Workspace was not properly initialized with a root container")
        });
        self.validate();
        workspace_ix
    }

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let size = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output").size;
        let worksp = Container::new_workspace(name.to_string(), size.clone());

        trace!("Adding workspace {:?}", worksp);
        let worksp_ix = self.tree.add_child(output_ix, worksp, false);
        let geometry = Geometry {
            size: size, origin: Point { x: 0, y: 0 }
        };
        let container_ix = self.tree.add_child(worksp_ix,
                                               Container::new_container(geometry), false);
        self.tree.set_ancestor_paths_active(container_ix);
        self.validate();
        container_ix
    }

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        // Set the old one to invisible
        self.tree.set_family_visible(old_worksp_ix, false);
        // Set the new one to visible
        self.tree.set_family_visible(workspace_ix, true);
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                   .expect("Workspace had no name"));
            self.remove_container(old_worksp_ix);
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { ref handle, ..} => {
                        trace!("View found, focusing on {:?}", handle);
                        handle.focus();
                    },
                    _ => unreachable!()
                }
                self.active_container = Some(active_ix);
                if !self.tree[active_ix].floating() {
                    self.tree.set_ancestor_paths_active(active_ix);
                } else {
                    let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                        .expect("The workspace we are switching to had no root container");
                    self.tree.set_ancestor_paths_active(root_c_ix);
                }
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree.descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { floating, .. } => {
                        if !floating {
                            self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                        } else {
                            let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                            .expect("The workspace we are switching to had no root container");
                            self.tree.set_ancestor_paths_active(root_c_ix);
                        }
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.tree.set_family_visible(curr_work_ix, false);

            // Save the parent of this view for focusing
            let maybe_active_parent = self.tree.parent_of(active_ix);

            // Get the root container of the next workspace
            let next_work_children = self.tree.children_of(next_work_ix);
            if cfg!(debug_assertions) {
                assert!(next_work_children.len() == 1,
                        "Next workspace has multiple roots!");
            }
            let next_work_root_ix = next_work_children[0];

            // Move the container
            info!("Moving container {:?} to workspace {}",
                self.get_active_container(), name);
            self.tree.move_node(active_ix, next_work_root_ix);

            // Update the active container
            if let Ok(parent_ix) = maybe_active_parent {
                let ctype = self.tree.node_type(parent_ix).unwrap_or(ContainerType::Root);
                if ctype == ContainerType::Container {
                    self.focus_on_next_container(parent_ix);
                } else {
                    trace!("Send to container invalidated a NodeIndex: {:?} to {:?}",
                    parent_ix, ctype);
                }
                if self.tree.can_remove_empty_parent(parent_ix) {
                    self.remove_view_or_container(parent_ix);
                }
            }
            else {
                self.focus_on_next_container(curr_work_ix);
            }

            self.tree.set_family_visible(curr_work_ix, true);

            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix);
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
    }
}
<commit_msg>Fixed new active path bug from switching wrkspaces<commit_after>use petgraph::graph::NodeIndex;
use uuid::Uuid;
use rustwlc::{Geometry, Point};
use super::super::LayoutTree;
use super::super::core::container::{Container, ContainerType};

// TODO This module needs to be updated like the other modules...
// Need to add some errors for this (such as when trying to move a non-container/view,
// or when trying to grab a workspace whos name already exists)
//
// Also the code is generally pretty crap, cause it's pretty old (mid-2016)

impl LayoutTree {
    /// Gets a workspace by name or creates it
    fn get_or_make_workspace(&mut self, name: &str) -> NodeIndex {
        let active_index = self.active_ix_of(ContainerType::Output)
            .or_else(|| {
                self.tree.follow_path_until(self.tree.root_ix(), ContainerType::Output).ok()
            })
            .expect("get_or_make_wksp: Couldn't get output");
        let workspace_ix = self.tree.workspace_ix_by_name(name).unwrap_or_else(|| {
            let root_ix = self.init_workspace(name.to_string(), active_index);
            self.tree.parent_of(root_ix)
                .expect("Workspace was not properly initialized with a root container")
        });
        self.validate();
        workspace_ix
    }

    /// Initializes a workspace and gets the index of the root container
    pub fn init_workspace(&mut self, name: String, output_ix: NodeIndex)
                      -> NodeIndex {
        let size = self.tree.get(output_ix)
            .expect("init_workspace: invalid output").get_geometry()
            .expect("init_workspace: no geometry for output").size;
        let worksp = Container::new_workspace(name.to_string(), size.clone());

        trace!("Adding workspace {:?}", worksp);
        let worksp_ix = self.tree.add_child(output_ix, worksp, false);
        let geometry = Geometry {
            size: size, origin: Point { x: 0, y: 0 }
        };
        let container_ix = self.tree.add_child(worksp_ix,
                                               Container::new_container(geometry), false);
        self.tree.set_ancestor_paths_active(container_ix);
        self.validate();
        container_ix
    }

    /// Switch to the specified workspace
    pub fn switch_to_workspace(&mut self, name: &str) {
        let maybe_active_ix = self.active_container
            .or_else(|| {
                let new_active = self.tree.follow_path(self.tree.root_ix());
                match self.tree[new_active].get_type() {
                    ContainerType::View | ContainerType::Container => {
                        Some(new_active)
                    },
                    // else try and get the root container
                    _ => self.tree.descendant_of_type(new_active, ContainerType::Container).ok()
                }
            });
        if maybe_active_ix.is_none() {
            warn!("{:#?}", self);
            warn!("No active container, cannot switch");
            return;
        }
        let active_ix = maybe_active_ix.unwrap();
        // Get the old (current) workspace
        let old_worksp_ix: NodeIndex;
        if let Ok(index) = self.tree.ancestor_of_type(active_ix, ContainerType::Workspace) {
            old_worksp_ix = index;
            trace!("Switching to workspace {}", name);
        } else {
            match self.tree[active_ix].get_type() {
                ContainerType::Workspace => {
                    old_worksp_ix = active_ix;
                    trace!("Switching to workspace {}", name);
                },
                _ => {
                    warn!("Could not find old workspace, could not set invisible");
                    return;
                }
            }
        }
        // Get the new workspace, or create one if it doesn't work
        let mut workspace_ix = self.get_or_make_workspace(name);
        if old_worksp_ix == workspace_ix {
            return;
        }
        // Set the old one to invisible
        self.tree.set_family_visible(old_worksp_ix, false);
        // Set the new one to visible
        self.tree.set_family_visible(workspace_ix, true);
        // Delete the old workspace if it has no views on it
        self.active_container = None;
        if self.tree.descendant_of_type(old_worksp_ix, ContainerType::View).is_err() {
            trace!("Removing workspace: {:?}", self.tree[old_worksp_ix].get_name()
                   .expect("Workspace had no name"));
            self.remove_container(old_worksp_ix);
        }
        workspace_ix = self.tree.workspace_ix_by_name(name)
            .expect("Workspace we just made was deleted!");
        let active_ix = self.tree.follow_path(workspace_ix);
        match self.tree[active_ix].get_type() {
            ContainerType::View  => {
                match self.tree[active_ix] {
                    Container::View { ref handle, ..} => {
                        trace!("View found, focusing on {:?}", handle);
                        handle.focus();
                    },
                    _ => unreachable!()
                }
                self.active_container = Some(active_ix);
                if !self.tree[active_ix].floating() {
                    self.tree.set_ancestor_paths_active(active_ix);
                } else {
                    let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                        .expect("The workspace we are switching to had no root container");
                    self.tree.set_ancestor_paths_active(root_c_ix);
                }
                self.validate();
                self.validate_path();
                return;
            },
            _ => {
                self.active_container = self.tree.descendant_of_type(active_ix, ContainerType::View)
                    .or_else(|_| self.tree.descendant_of_type(active_ix,
                                                              ContainerType::Container)).ok();
                match self.tree[self.active_container.expect("Workspace had NO children!")] {
                    Container::View { floating, .. } => {
                        if !floating {
                            self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                        } else {
                            let root_c_ix = *self.tree.children_of(workspace_ix).get(0)
                            .expect("The workspace we are switching to had no root container");
                            self.tree.set_ancestor_paths_active(root_c_ix);
                        }
                    },
                    Container::Container { .. } => {
                        self.tree.set_ancestor_paths_active(self.active_container.unwrap());
                    }
                    _ => unreachable!()
                };
            }
        }
        trace!("Focusing on next container");
        self.focus_on_next_container(workspace_ix);
        self.validate();
        self.validate_path();
    }

    /// Moves the active container to a new workspace.
    pub fn send_active_to_workspace(&mut self, name: &str) {
        if let Some(active_ix) = self.active_container {
            let id = self.tree[active_ix].get_id();
            self.send_to_workspace(id, name);
        }
    }
    /// Moves a container to a new workspace
    pub fn send_to_workspace(&mut self, id: Uuid, name: &str) {
        let node_ix = self.tree.lookup_id(id);
        // Ensure focus
        if let Some(active_ix) = node_ix.or(self.active_container) {
            let curr_work_ix = self.active_ix_of(ContainerType::Workspace)
                .expect("send_active: Not currently in a workspace!");
            if active_ix == self.tree.children_of(curr_work_ix)[0] {
                warn!("Tried to move the root container of a workspace, aborting move");
                return;
            }
            let next_work_ix = self.get_or_make_workspace(name);

            // Check if the workspaces are the same
            if next_work_ix == curr_work_ix {
                trace!("Attempted to move a view to the same workspace {}!", name);
                return;
            }
            self.tree.set_family_visible(curr_work_ix, false);

            // Save the parent of this view for focusing
            let maybe_active_parent = self.tree.parent_of(active_ix);

            // Get the root container of the next workspace
            let next_work_children = self.tree.children_of(next_work_ix);
            if cfg!(debug_assertions) {
                assert!(next_work_children.len() == 1,
                        "Next workspace has multiple roots!");
            }
            let next_work_root_ix = next_work_children[0];

            // Move the container
            info!("Moving container {:?} to workspace {}",
                self.get_active_container(), name);
            self.tree.move_node(active_ix, next_work_root_ix);

            // Update the active container
            if let Ok(parent_ix) = maybe_active_parent {
                self.tree.set_ancestor_paths_active(parent_ix);
                let ctype = self.tree.node_type(parent_ix).unwrap_or(ContainerType::Root);
                if ctype == ContainerType::Container {
                    self.focus_on_next_container(parent_ix);
                } else {
                    trace!("Send to container invalidated a NodeIndex: {:?} to {:?}",
                    parent_ix, ctype);
                }
                if self.tree.can_remove_empty_parent(parent_ix) {
                    self.remove_view_or_container(parent_ix);
                }
            }
            else {
                self.focus_on_next_container(curr_work_ix);
            }

            self.tree.set_family_visible(curr_work_ix, true);

            if !self.tree[active_ix].floating() {
                self.normalize_container(active_ix);
            }
        }
        let root_ix = self.tree.root_ix();
        self.layout(root_ix);
        self.validate();
    }
}
<|endoftext|>


<commit_before>//! Serialization for client-server communication.

use std::any::Any;
use std::char;
use std::io::Write;
use std::num::NonZeroU32;
use std::ops::Bound;
use std::str;

pub(super) type Writer = super::buffer::Buffer<u8>;

pub(super) trait Encode<S>: Sized {
    fn encode(self, w: &mut Writer, s: &mut S);
}

pub(super) type Reader<'a> = &'a [u8];

pub(super) trait Decode<'a, 's, S>: Sized {
    fn decode(r: &mut Reader<'a>, s: &'s S) -> Self;
}

pub(super) trait DecodeMut<'a, 's, S>: Sized {
    fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
}

macro_rules! rpc_encode_decode {
    (uleb128 $ty:ty) => {
        impl<S> Encode<S> for $ty {
            fn encode(mut self, w: &mut Writer, s: &mut S) {
                let mut byte = 0x80;
                while byte & 0x80 != 0 {
                    byte = (self & 0x7f) as u8;
                    self >>= 7;
                    if self != 0 {
                        byte |= 0x80;
                    }
                    byte.encode(w, s);
                }
            }
        }

        impl<S> DecodeMut<'_, '_, S> for $ty {
            fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
                let mut byte = 0x80;
                let mut v = 0;
                let mut shift = 0;
                while byte & 0x80 != 0 {
                    byte = u8::decode(r, s);
                    v |= ((byte & 0x7f) as Self) << shift;
                    shift += 7;
                }
                v
            }
        }
    };
    (struct $name:ident { $($field:ident),* $(,)? }) => {
        impl<S> Encode<S> for $name {
            fn encode(self, w: &mut Writer, s: &mut S) {
                $(self.$field.encode(w, s);)*
            }
        }

        impl<S> DecodeMut<'_, '_, S> for $name {
            fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
                $name {
                    $($field: DecodeMut::decode(r, s)),*
                }
            }
        }
    };
    (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)* {
            fn encode(self, w: &mut Writer, s: &mut S) {
                // HACK(eddyb): `Tag` enum duplicated between the
                // two impls as there's no other place to stash it.
                #[allow(non_upper_case_globals)]
                mod tag {
                    #[repr(u8)] enum Tag { $($variant),* }

                    $(pub const $variant: u8 = Tag::$variant as u8;)*
                }

                match self {
                    $($name::$variant $(($field))* => {
                        tag::$variant.encode(w, s);
                        $($field.encode(w, s);)*
                    })*
                }
            }
        }

        impl<S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)*> DecodeMut<'a, '_, S>
            for $name $(<$($T),+>)*
        {
            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
                // HACK(eddyb): `Tag` enum duplicated between the
                // two impls as there's no other place to stash it.
                #[allow(non_upper_case_globals)]
                mod tag {
                    #[repr(u8)] enum Tag { $($variant),* }

                    $(pub const $variant: u8 = Tag::$variant as u8;)*
                }

                match u8::decode(r, s) {
                    $(tag::$variant => {
                        $(let $field = DecodeMut::decode(r, s);)*
                        $name::$variant $(($field))*
                    })*
                    _ => unreachable!(),
                }
            }
        }
    }
}

impl<S> Encode<S> for () {
    fn encode(self, _: &mut Writer, _: &mut S) {}
}

impl<S> DecodeMut<'_, '_, S> for () {
    fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
}

impl<S> Encode<S> for u8 {
    fn encode(self, w: &mut Writer, _: &mut S) {
        w.write_all(&[self]).unwrap();
    }
}

impl<S> DecodeMut<'_, '_, S> for u8 {
    fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
        let x = r[0];
        *r = &r[1..];
        x
    }
}

rpc_encode_decode!(uleb128 u32);
rpc_encode_decode!(uleb128 usize);

impl<S> Encode<S> for bool {
    fn encode(self, w: &mut Writer, s: &mut S) {
        (self as u8).encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for bool {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        match u8::decode(r, s) {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
}

impl<S> Encode<S> for char {
    fn encode(self, w: &mut Writer, s: &mut S) {
        (self as u32).encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for char {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        char::from_u32(u32::decode(r, s)).unwrap()
    }
}

impl<S> Encode<S> for NonZeroU32 {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.get().encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        Self::new(u32::decode(r, s)).unwrap()
    }
}

impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.0.encode(w, s);
        self.1.encode(w, s);
    }
}

impl<S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
    for (A, B)
{
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        (DecodeMut::decode(r, s), DecodeMut::decode(r, s))
    }
}

rpc_encode_decode!(
    enum Bound<T> {
        Included(x),
        Excluded(x),
        Unbounded,
    }
);

rpc_encode_decode!(
    enum Option<T> {
        None,
        Some(x),
    }
);

rpc_encode_decode!(
    enum Result<T, E> {
        Ok(x),
        Err(e),
    }
);

impl<S> Encode<S> for &[u8] {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.len().encode(w, s);
        w.write_all(self).unwrap();
    }
}

impl<S> DecodeMut<'a, '_, S> for &'a [u8] {
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        let len = usize::decode(r, s);
        let xs = &r[..len];
        *r = &r[len..];
        xs
    }
}

impl<S> Encode<S> for &str {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.as_bytes().encode(w, s);
    }
}

impl<S> DecodeMut<'a, '_, S> for &'a str {
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
    }
}

impl<S> Encode<S> for String {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self[..].encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for String {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        <&str>::decode(r, s).to_string()
    }
}

/// Simplied version of panic payloads, ignoring
/// types other than `&'static str` and `String`.
pub enum PanicMessage {
    StaticStr(&'static str),
    String(String),
    Unknown,
}

impl From<Box<dyn Any + Send>> for PanicMessage {
    fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
        if let Some(s) = payload.downcast_ref::<&'static str>() {
            return PanicMessage::StaticStr(s);
        }
        if let Ok(s) = payload.downcast::<String>() {
            return PanicMessage::String(*s);
        }
        PanicMessage::Unknown
    }
}

impl Into<Box<dyn Any + Send>> for PanicMessage {
    fn into(self) -> Box<dyn Any + Send> {
        match self {
            PanicMessage::StaticStr(s) => Box::new(s),
            PanicMessage::String(s) => Box::new(s),
            PanicMessage::Unknown => {
                struct UnknownPanicMessage;
                Box::new(UnknownPanicMessage)
            }
        }
    }
}

impl PanicMessage {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            PanicMessage::StaticStr(s) => Some(s),
            PanicMessage::String(s) => Some(s),
            PanicMessage::Unknown => None,
        }
    }
}

impl<S> Encode<S> for PanicMessage {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.as_str().encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for PanicMessage {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        match Option::<String>::decode(r, s) {
            Some(s) => PanicMessage::String(s),
            None => PanicMessage::Unknown,
        }
    }
}
<commit_msg>Rollup merge of #59820 - eddyb:proc-macro-rpc-opt, r=nnethercote<commit_after>//! Serialization for client-server communication.

use std::any::Any;
use std::char;
use std::io::Write;
use std::num::NonZeroU32;
use std::ops::Bound;
use std::str;

pub(super) type Writer = super::buffer::Buffer<u8>;

pub(super) trait Encode<S>: Sized {
    fn encode(self, w: &mut Writer, s: &mut S);
}

pub(super) type Reader<'a> = &'a [u8];

pub(super) trait Decode<'a, 's, S>: Sized {
    fn decode(r: &mut Reader<'a>, s: &'s S) -> Self;
}

pub(super) trait DecodeMut<'a, 's, S>: Sized {
    fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
}

macro_rules! rpc_encode_decode {
    (le $ty:ty) => {
        impl<S> Encode<S> for $ty {
            fn encode(self, w: &mut Writer, _: &mut S) {
                w.write_all(&self.to_le_bytes()).unwrap();
            }
        }

        impl<S> DecodeMut<'_, '_, S> for $ty {
            fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
                const N: usize = ::std::mem::size_of::<$ty>();

                let mut bytes = [0; N];
                bytes.copy_from_slice(&r[..N]);
                *r = &r[N..];

                Self::from_le_bytes(bytes)
            }
        }
    };
    (struct $name:ident { $($field:ident),* $(,)? }) => {
        impl<S> Encode<S> for $name {
            fn encode(self, w: &mut Writer, s: &mut S) {
                $(self.$field.encode(w, s);)*
            }
        }

        impl<S> DecodeMut<'_, '_, S> for $name {
            fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
                $name {
                    $($field: DecodeMut::decode(r, s)),*
                }
            }
        }
    };
    (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
        impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)* {
            fn encode(self, w: &mut Writer, s: &mut S) {
                // HACK(eddyb): `Tag` enum duplicated between the
                // two impls as there's no other place to stash it.
                #[allow(non_upper_case_globals)]
                mod tag {
                    #[repr(u8)] enum Tag { $($variant),* }

                    $(pub const $variant: u8 = Tag::$variant as u8;)*
                }

                match self {
                    $($name::$variant $(($field))* => {
                        tag::$variant.encode(w, s);
                        $($field.encode(w, s);)*
                    })*
                }
            }
        }

        impl<S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)*> DecodeMut<'a, '_, S>
            for $name $(<$($T),+>)*
        {
            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
                // HACK(eddyb): `Tag` enum duplicated between the
                // two impls as there's no other place to stash it.
                #[allow(non_upper_case_globals)]
                mod tag {
                    #[repr(u8)] enum Tag { $($variant),* }

                    $(pub const $variant: u8 = Tag::$variant as u8;)*
                }

                match u8::decode(r, s) {
                    $(tag::$variant => {
                        $(let $field = DecodeMut::decode(r, s);)*
                        $name::$variant $(($field))*
                    })*
                    _ => unreachable!(),
                }
            }
        }
    }
}

impl<S> Encode<S> for () {
    fn encode(self, _: &mut Writer, _: &mut S) {}
}

impl<S> DecodeMut<'_, '_, S> for () {
    fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
}

impl<S> Encode<S> for u8 {
    fn encode(self, w: &mut Writer, _: &mut S) {
        w.write_all(&[self]).unwrap();
    }
}

impl<S> DecodeMut<'_, '_, S> for u8 {
    fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
        let x = r[0];
        *r = &r[1..];
        x
    }
}

rpc_encode_decode!(le u32);
rpc_encode_decode!(le usize);

impl<S> Encode<S> for bool {
    fn encode(self, w: &mut Writer, s: &mut S) {
        (self as u8).encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for bool {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        match u8::decode(r, s) {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
}

impl<S> Encode<S> for char {
    fn encode(self, w: &mut Writer, s: &mut S) {
        (self as u32).encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for char {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        char::from_u32(u32::decode(r, s)).unwrap()
    }
}

impl<S> Encode<S> for NonZeroU32 {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.get().encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        Self::new(u32::decode(r, s)).unwrap()
    }
}

impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.0.encode(w, s);
        self.1.encode(w, s);
    }
}

impl<S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
    for (A, B)
{
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        (DecodeMut::decode(r, s), DecodeMut::decode(r, s))
    }
}

rpc_encode_decode!(
    enum Bound<T> {
        Included(x),
        Excluded(x),
        Unbounded,
    }
);

rpc_encode_decode!(
    enum Option<T> {
        None,
        Some(x),
    }
);

rpc_encode_decode!(
    enum Result<T, E> {
        Ok(x),
        Err(e),
    }
);

impl<S> Encode<S> for &[u8] {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.len().encode(w, s);
        w.write_all(self).unwrap();
    }
}

impl<S> DecodeMut<'a, '_, S> for &'a [u8] {
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        let len = usize::decode(r, s);
        let xs = &r[..len];
        *r = &r[len..];
        xs
    }
}

impl<S> Encode<S> for &str {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.as_bytes().encode(w, s);
    }
}

impl<S> DecodeMut<'a, '_, S> for &'a str {
    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
        str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
    }
}

impl<S> Encode<S> for String {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self[..].encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for String {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        <&str>::decode(r, s).to_string()
    }
}

/// Simplied version of panic payloads, ignoring
/// types other than `&'static str` and `String`.
pub enum PanicMessage {
    StaticStr(&'static str),
    String(String),
    Unknown,
}

impl From<Box<dyn Any + Send>> for PanicMessage {
    fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
        if let Some(s) = payload.downcast_ref::<&'static str>() {
            return PanicMessage::StaticStr(s);
        }
        if let Ok(s) = payload.downcast::<String>() {
            return PanicMessage::String(*s);
        }
        PanicMessage::Unknown
    }
}

impl Into<Box<dyn Any + Send>> for PanicMessage {
    fn into(self) -> Box<dyn Any + Send> {
        match self {
            PanicMessage::StaticStr(s) => Box::new(s),
            PanicMessage::String(s) => Box::new(s),
            PanicMessage::Unknown => {
                struct UnknownPanicMessage;
                Box::new(UnknownPanicMessage)
            }
        }
    }
}

impl PanicMessage {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            PanicMessage::StaticStr(s) => Some(s),
            PanicMessage::String(s) => Some(s),
            PanicMessage::Unknown => None,
        }
    }
}

impl<S> Encode<S> for PanicMessage {
    fn encode(self, w: &mut Writer, s: &mut S) {
        self.as_str().encode(w, s);
    }
}

impl<S> DecodeMut<'_, '_, S> for PanicMessage {
    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
        match Option::<String>::decode(r, s) {
            Some(s) => PanicMessage::String(s),
            None => PanicMessage::Unknown,
        }
    }
}
<|endoftext|>