<commit_before>/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::sync::Arc;

use arc_swap::ArcSwapOption;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::filters::prelude::*;

/// A mutable memory location with atomic storage rules.
#[derive(Default, Clone, Deserialize, Serialize)]
#[serde(transparent)]
pub struct Slot<T> {
    inner: Arc<ArcSwapOption<T>>,
    #[serde(skip)]
    #[allow(clippy::type_complexity)]
    watcher: Arc<ArcSwapOption<Box<dyn Fn(&T) + Send + Sync>>>,
}

impl<T> Slot<T> {
    /// Creates a new slot for `value`.
    pub fn new(value: impl Into<Option<T>>) -> Self {
        Self {
            inner: Arc::new(ArcSwapOption::new(value.into().map(Arc::new))),
            watcher: <_>::default(),
        }
    }

    /// Adds a watcher to to the slot. The watcher will fire whenever slot's
    /// value changes.
    pub fn watch(&self, watcher: impl Fn(&T) + Send + Sync + 'static) {
        tracing::trace!("Adding new watcher");
        self.watcher.store(Some(Arc::new(Box::new(watcher))));
    }

    /// Returns whether any data is present in the slot.
    pub fn is_some(&self) -> bool {
        self.inner.load().is_some()
    }
}

impl<T: Default> Slot<T> {
    /// Creates a new slot containing the default instance of `T`.
    pub fn with_default() -> Self {
        Self::new(T::default())
    }

    /// Triggers the `watcher` function, if present.
    fn call_watcher(&self) {
        if let Some(watcher) = &*self.watcher.load() {
            tracing::trace!("calling watcher");
            (watcher)(&self.load());
        }
    }

    /// Provides a reference to the underlying data.
    pub fn load(&self) -> Arc<T> {
        self.inner.load_full().unwrap_or_default()
    }

    fn store_opt(&self, value: Option<Arc<T>>) {
        tracing::trace!("storing new value");
        self.inner.store(value);
        self.call_watcher();
    }

    /// Replaces the data in the slot with `value`.
    pub fn store(&self, value: Arc<T>) {
        self.store_opt(Some(value));
    }

    /// Removes any data from the slot.
    pub fn remove(&self) {
        self.store_opt(None);
    }
}

impl<T: Default + PartialEq> Slot<T> {
    /// Replaces the current data in the slot with `value`'s data, if present.
    pub fn try_replace(&self, value: Self) {
        if let Some(value) = value
            .inner
            .load_full()
            .filter(|value| &self.load() != value)
        {
            self.store(value);
        }
    }
}

impl<T: Clone + Default> Slot<T> {
    /// Provides a view into a mutable reference of the current data in the
    /// slot. Any changes made will update the value in the slot.
    pub fn modify(&self, mut modify: impl FnMut(&mut T)) {
        self.inner.rcu(|value| {
            let mut current = value
                .as_deref()
                .map(|value| T::clone(value))
                .unwrap_or_default();
            (modify)(&mut current);
            Some(Arc::new(current))
        });
        self.call_watcher();
    }
}

impl<T: std::fmt::Debug> std::fmt::Debug for Slot<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.inner.load().fmt(f)
    }
}

impl<T: PartialEq> PartialEq for Slot<T> {
    fn eq(&self, rhs: &Self) -> bool {
        *self.inner.load() == *rhs.inner.load()
    }
}

impl<T: Default> From<T> for Slot<T> {
    fn from(data: T) -> Self {
        Self::new(data)
    }
}

impl<T: Default> From<Option<T>> for Slot<T> {
    fn from(data: Option<T>) -> Self {
        Self::new(data)
    }
}

impl<T: JsonSchema + Default> JsonSchema for Slot<T> {
    fn schema_name() -> String {
        <Option<T>>::schema_name()
    }
    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
        <Option<T>>::json_schema(gen)
    }

    fn is_referenceable() -> bool {
        <Option<T>>::is_referenceable()
    }
}

impl<T: crate::filters::Filter + Default> crate::filters::Filter for Slot<T> {
    fn read(&self, ctx: ReadContext) -> Option<ReadResponse> {
        self.load().read(ctx)
    }

    fn write(&self, ctx: WriteContext) -> Option<WriteResponse> {
        self.load().write(ctx)
    }
}

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

    #[test]
    fn watch() {
        static BOOLEAN: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);

        let slot = Slot::new(false);

        slot.watch(|_| {
            BOOLEAN.store(true, std::sync::atomic::Ordering::SeqCst);
        });

        slot.store(Arc::new(true));

        assert_eq!(
            BOOLEAN.load(std::sync::atomic::Ordering::SeqCst),
            *slot.load()
        );
    }
}
<commit_msg>Relax trait bounds for Default, Deserialize, and Serialize for Slot<T> (#581)<commit_after>/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::sync::Arc;

use arc_swap::ArcSwapOption;
use schemars::JsonSchema;

use crate::filters::prelude::*;

/// A mutable memory location with atomic storage rules.
#[derive(Clone)]
pub struct Slot<T> {
    inner: Arc<ArcSwapOption<T>>,
    #[allow(clippy::type_complexity)]
    watcher: Arc<ArcSwapOption<Box<dyn Fn(&T) + Send + Sync>>>,
}

impl<T> Slot<T> {
    /// Creates a new slot for `value`.
    pub fn new(value: impl Into<Option<T>>) -> Self {
        Self {
            inner: Arc::new(ArcSwapOption::new(value.into().map(Arc::new))),
            watcher: <_>::default(),
        }
    }

    /// Adds a watcher to to the slot. The watcher will fire whenever slot's
    /// value changes.
    pub fn watch(&self, watcher: impl Fn(&T) + Send + Sync + 'static) {
        tracing::trace!("Adding new watcher");
        self.watcher.store(Some(Arc::new(Box::new(watcher))));
    }

    /// Returns whether any data is present in the slot.
    pub fn is_some(&self) -> bool {
        self.inner.load().is_some()
    }
}

impl<T: Default> Slot<T> {
    /// Creates a new slot containing the default instance of `T`.
    pub fn with_default() -> Self {
        Self::new(T::default())
    }

    /// Triggers the `watcher` function, if present.
    fn call_watcher(&self) {
        if let Some(watcher) = &*self.watcher.load() {
            tracing::trace!("calling watcher");
            (watcher)(&self.load());
        }
    }

    /// Provides a reference to the underlying data.
    pub fn load(&self) -> Arc<T> {
        self.inner.load_full().unwrap_or_default()
    }

    fn store_opt(&self, value: Option<Arc<T>>) {
        tracing::trace!("storing new value");
        self.inner.store(value);
        self.call_watcher();
    }

    /// Replaces the data in the slot with `value`.
    pub fn store(&self, value: Arc<T>) {
        self.store_opt(Some(value));
    }

    /// Removes any data from the slot.
    pub fn remove(&self) {
        self.store_opt(None);
    }
}

impl<T: Default + PartialEq> Slot<T> {
    /// Replaces the current data in the slot with `value`'s data, if present.
    pub fn try_replace(&self, value: Self) {
        if let Some(value) = value
            .inner
            .load_full()
            .filter(|value| &self.load() != value)
        {
            self.store(value);
        }
    }
}

impl<T: Clone + Default> Slot<T> {
    /// Provides a view into a mutable reference of the current data in the
    /// slot. Any changes made will update the value in the slot.
    pub fn modify(&self, mut modify: impl FnMut(&mut T)) {
        self.inner.rcu(|value| {
            let mut current = value
                .as_deref()
                .map(|value| T::clone(value))
                .unwrap_or_default();
            (modify)(&mut current);
            Some(Arc::new(current))
        });
        self.call_watcher();
    }
}

impl<T> Default for Slot<T> {
    fn default() -> Self {
        Self {
            inner: <_>::default(),
            watcher: <_>::default(),
        }
    }
}

impl<T: std::fmt::Debug> std::fmt::Debug for Slot<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.inner.load().fmt(f)
    }
}

impl<T: PartialEq> PartialEq for Slot<T> {
    fn eq(&self, rhs: &Self) -> bool {
        *self.inner.load() == *rhs.inner.load()
    }
}

impl<T: Default> From<T> for Slot<T> {
    fn from(data: T) -> Self {
        Self::new(data)
    }
}

impl<T: Default> From<Option<T>> for Slot<T> {
    fn from(data: Option<T>) -> Self {
        Self::new(data)
    }
}

impl<T: serde::Serialize> serde::Serialize for Slot<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.inner.load().serialize(serializer)
    }
}

impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Slot<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        <Option<T>>::deserialize(deserializer).map(Slot::new)
    }
}

impl<T: JsonSchema + Default> JsonSchema for Slot<T> {
    fn schema_name() -> String {
        <Option<T>>::schema_name()
    }
    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
        <Option<T>>::json_schema(gen)
    }

    fn is_referenceable() -> bool {
        <Option<T>>::is_referenceable()
    }
}

impl<T: crate::filters::Filter + Default> crate::filters::Filter for Slot<T> {
    fn read(&self, ctx: ReadContext) -> Option<ReadResponse> {
        self.load().read(ctx)
    }

    fn write(&self, ctx: WriteContext) -> Option<WriteResponse> {
        self.load().write(ctx)
    }
}

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

    #[test]
    fn watch() {
        static BOOLEAN: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);

        let slot = Slot::new(false);

        slot.watch(|_| {
            BOOLEAN.store(true, std::sync::atomic::Ordering::SeqCst);
        });

        slot.store(Arc::new(true));

        assert_eq!(
            BOOLEAN.load(std::sync::atomic::Ordering::SeqCst),
            *slot.load()
        );
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{to_json, PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &Vec<PathAndJson<'reg, 'rc>>,
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Vec<Dynamic> = params.iter().map(|p| convert(p.value())).collect();
    let hash: Dynamic = HashMap::from_iter(
        hash.iter()
            .map(|(k, v)| ((*k).to_owned(), convert(v.value()))),
    )
    .into();

    let mut scope = Scope::new();
    scope.push_dynamic("params", params.into());
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    // FIXME: convert to json instead of string
    let result_string = result.take_string().unwrap_or_else(|e| e.to_owned());

    Ok(Some(ScopedJson::Derived(to_json(result_string))))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

fn convert(j: &Json) -> Dynamic {
    match j {
        Json::Number(n) => Dynamic::from(n.clone()),
        Json::Bool(b) => Dynamic::from(*b),
        Json::Null => Dynamic::from(()),
        Json::String(s) => Dynamic::from(s.clone()),
        Json::Array(ref v) => {
            let dyn_vec: Vec<Dynamic> = v.iter().map(|i| convert(i)).collect();
            Dynamic::from(dyn_vec)
        }
        Json::Object(ref o) => {
            let dyn_map: HashMap<String, Dynamic> = o
                .iter()
                .map(|(k, v)| ((*k).to_owned(), convert(v)))
                .collect();
            Dynamic::from(dyn_map)
        }
    }
}

#[cfg(test)]
mod test {
    use super::{call_script_helper, convert};
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = convert(&j0);
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = convert(&j1);
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<commit_msg>(refactor) dynamic conversion<commit_after>use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{to_json, PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::Value as Json;

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &Vec<PathAndJson<'reg, 'rc>>,
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = params
        .iter()
        .map(|p| convert(p.value()))
        .collect::<Vec<Dynamic>>()
        .into();
    let hash: Dynamic = hash
        .iter()
        .map(|(k, v)| ((*k).to_owned(), convert(v.value())))
        .collect::<HashMap<String, Dynamic>>()
        .into();

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    // FIXME: convert to json instead of string
    let result_string = result.take_string().unwrap_or_else(|e| e.to_owned());

    Ok(Some(ScopedJson::Derived(to_json(result_string))))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

fn convert(j: &Json) -> Dynamic {
    match j {
        Json::Number(n) => Dynamic::from(n.clone()),
        Json::Bool(b) => Dynamic::from(*b),
        Json::Null => Dynamic::from(()),
        Json::String(s) => Dynamic::from(s.clone()),
        Json::Array(ref v) => {
            let dyn_vec: Vec<Dynamic> = v.iter().map(|i| convert(i)).collect();
            Dynamic::from(dyn_vec)
        }
        Json::Object(ref o) => {
            let dyn_map: HashMap<String, Dynamic> = o
                .iter()
                .map(|(k, v)| ((*k).to_owned(), convert(v)))
                .collect();
            Dynamic::from(dyn_map)
        }
    }
}

#[cfg(test)]
mod test {
    use super::{call_script_helper, convert};
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = convert(&j0);
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = convert(&j1);
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::path::Path;

use serde::ser;

use crate::util::{CargoResult, CargoResultExt, Config, RustfixDiagnosticServer};

/// Configuration information for a rustc build.
#[derive(Debug)]
pub struct BuildConfig {
    /// The target arch triple.
    /// Default: host arch.
    pub requested_target: Option<String>,
    /// Number of rustc jobs to run in parallel.
    pub jobs: u32,
    /// `true` if we are building for release.
    pub release: bool,
    /// The mode we are compiling in.
    pub mode: CompileMode,
    /// `true` to print stdout in JSON format (for machine reading).
    pub message_format: MessageFormat,
    /// Force Cargo to do a full rebuild and treat each target as changed.
    pub force_rebuild: bool,
    /// Output a build plan to stdout instead of actually compiling.
    pub build_plan: bool,
    /// Use Cargo itself as the wrapper around rustc, only used for `cargo fix`.
    pub cargo_as_rustc_wrapper: bool,
    /// Extra env vars to inject into rustc commands.
    pub extra_rustc_env: Vec<(String, String)>,
    /// Extra args to inject into rustc commands.
    pub extra_rustc_args: Vec<String>,
    pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>,
}

impl BuildConfig {
    /// Parses all config files to learn about build configuration. Currently
    /// configured options are:
    ///
    /// * `build.jobs`
    /// * `build.target`
    /// * `target.$target.ar`
    /// * `target.$target.linker`
    /// * `target.$target.libfoo.metadata`
    pub fn new(
        config: &Config,
        jobs: Option<u32>,
        requested_target: &Option<String>,
        mode: CompileMode,
    ) -> CargoResult<BuildConfig> {
        let requested_target = match requested_target {
            &Some(ref target) if target.ends_with(".json") => {
                let path = Path::new(target).canonicalize().chain_err(|| {
                    failure::format_err!("Target path {:?} is not a valid file", target)
                })?;
                Some(
                    path.into_os_string()
                        .into_string()
                        .map_err(|_| failure::format_err!("Target path is not valid unicode"))?,
                )
            }
            other => other.clone(),
        };
        if let Some(ref s) = requested_target {
            if s.trim().is_empty() {
                failure::bail!("target was empty")
            }
        }
        let cfg_target = config.get_string("build.target")?.map(|s| s.val);
        let target = requested_target.or(cfg_target);

        if jobs == Some(0) {
            failure::bail!("jobs must be at least 1")
        }
        if jobs.is_some() && config.jobserver_from_env().is_some() {
            config.shell().warn(
                "a `-j` argument was passed to Cargo but Cargo is \
                 also configured with an external jobserver in \
                 its environment, ignoring the `-j` parameter",
            )?;
        }
        let cfg_jobs: Option<u32> = config.get("build.jobs")?;
        let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32);
        Ok(BuildConfig {
            requested_target: target,
            jobs,
            release: false,
            mode,
            message_format: MessageFormat::Human,
            force_rebuild: false,
            build_plan: false,
            cargo_as_rustc_wrapper: false,
            extra_rustc_env: Vec::new(),
            extra_rustc_args: Vec::new(),
            rustfix_diagnostic_server: RefCell::new(None),
        })
    }

    pub fn json_messages(&self) -> bool {
        self.message_format == MessageFormat::Json
    }

    pub fn test(&self) -> bool {
        self.mode == CompileMode::Test || self.mode == CompileMode::Bench
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MessageFormat {
    Human,
    Json,
    Short,
}

/// The general "mode" for what to do.
/// This is used for two purposes. The commands themselves pass this in to
/// `compile_ws` to tell it the general execution strategy. This influences
/// the default targets selected. The other use is in the `Unit` struct
/// to indicate what is being done with a specific target.
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
pub enum CompileMode {
    /// A target being built for a test.
    Test,
    /// Building a target with `rustc` (lib or bin).
    Build,
    /// Building a target with `rustc` to emit `rmeta` metadata only. If
    /// `test` is true, then it is also compiled with `--test` to check it like
    /// a test.
    Check { test: bool },
    /// Used to indicate benchmarks should be built. This is not used in
    /// `Target`, because it is essentially the same as `Test` (indicating
    /// `--test` should be passed to rustc) and by using `Test` instead it
    /// allows some de-duping of Units to occur.
    Bench,
    /// A target that will be documented with `rustdoc`.
    /// If `deps` is true, then it will also document all dependencies.
    Doc { deps: bool },
    /// A target that will be tested with `rustdoc`.
    Doctest,
    /// A marker for Units that represent the execution of a `build.rs` script.
    RunCustomBuild,
}

impl ser::Serialize for CompileMode {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        use self::CompileMode::*;
        match *self {
            Test => "test".serialize(s),
            Build => "build".serialize(s),
            Check { .. } => "check".serialize(s),
            Bench => "bench".serialize(s),
            Doc { .. } => "doc".serialize(s),
            Doctest => "doctest".serialize(s),
            RunCustomBuild => "run-custom-build".serialize(s),
        }
    }
}

impl CompileMode {
    /// Returns `true` if the unit is being checked.
    pub fn is_check(self) -> bool {
        match self {
            CompileMode::Check { .. } => true,
            _ => false,
        }
    }

    /// Returns `true` if this is a doc or doc test. Be careful using this.
    /// Although both run rustdoc, the dependencies for those two modes are
    /// very different.
    pub fn is_doc(self) -> bool {
        match self {
            CompileMode::Doc { .. } | CompileMode::Doctest => true,
            _ => false,
        }
    }

    /// Returns `true` if this is any type of test (test, benchmark, doc test, or
    /// check test).
    pub fn is_any_test(self) -> bool {
        match self {
            CompileMode::Test
            | CompileMode::Bench
            | CompileMode::Check { test: true }
            | CompileMode::Doctest => true,
            _ => false,
        }
    }

    /// Returns `true` if this is the *execution* of a `build.rs` script.
    pub fn is_run_custom_build(self) -> bool {
        self == CompileMode::RunCustomBuild
    }

    /// List of all modes (currently used by `cargo clean -p` for computing
    /// all possible outputs).
    pub fn all_modes() -> &'static [CompileMode] {
        static ALL: [CompileMode; 9] = [
            CompileMode::Test,
            CompileMode::Build,
            CompileMode::Check { test: true },
            CompileMode::Check { test: false },
            CompileMode::Bench,
            CompileMode::Doc { deps: true },
            CompileMode::Doc { deps: false },
            CompileMode::Doctest,
            CompileMode::RunCustomBuild,
        ];
        &ALL
    }
}
<commit_msg>Canonicalize default target if it ends with `.json`<commit_after>use std::cell::RefCell;
use std::path::Path;

use serde::ser;

use crate::util::{CargoResult, CargoResultExt, Config, RustfixDiagnosticServer};

/// Configuration information for a rustc build.
#[derive(Debug)]
pub struct BuildConfig {
    /// The target arch triple.
    /// Default: host arch.
    pub requested_target: Option<String>,
    /// Number of rustc jobs to run in parallel.
    pub jobs: u32,
    /// `true` if we are building for release.
    pub release: bool,
    /// The mode we are compiling in.
    pub mode: CompileMode,
    /// `true` to print stdout in JSON format (for machine reading).
    pub message_format: MessageFormat,
    /// Force Cargo to do a full rebuild and treat each target as changed.
    pub force_rebuild: bool,
    /// Output a build plan to stdout instead of actually compiling.
    pub build_plan: bool,
    /// Use Cargo itself as the wrapper around rustc, only used for `cargo fix`.
    pub cargo_as_rustc_wrapper: bool,
    /// Extra env vars to inject into rustc commands.
    pub extra_rustc_env: Vec<(String, String)>,
    /// Extra args to inject into rustc commands.
    pub extra_rustc_args: Vec<String>,
    pub rustfix_diagnostic_server: RefCell<Option<RustfixDiagnosticServer>>,
}

impl BuildConfig {
    /// Parses all config files to learn about build configuration. Currently
    /// configured options are:
    ///
    /// * `build.jobs`
    /// * `build.target`
    /// * `target.$target.ar`
    /// * `target.$target.linker`
    /// * `target.$target.libfoo.metadata`
    pub fn new(
        config: &Config,
        jobs: Option<u32>,
        requested_target: &Option<String>,
        mode: CompileMode,
    ) -> CargoResult<BuildConfig> {
        let requested_target = match requested_target {
            &Some(ref target) if target.ends_with(".json") => {
                let path = Path::new(target).canonicalize().chain_err(|| {
                    failure::format_err!("Target path {:?} is not a valid file", target)
                })?;
                Some(
                    path.into_os_string()
                        .into_string()
                        .map_err(|_| failure::format_err!("Target path is not valid unicode"))?,
                )
            }
            other => other.clone(),
        };
        if let Some(ref s) = requested_target {
            if s.trim().is_empty() {
                failure::bail!("target was empty")
            }
        }
        let cfg_target = match config.get_string("build.target")? {
            Some(ref target) if target.val.ends_with(".json") => {
                let path = target.definition.root(config).join(&target.val);
                let path_string = path
                    .into_os_string()
                    .into_string()
                    .map_err(|_| failure::format_err!("Target path is not valid unicode"));
                Some(path_string?)
            }
            other => other.map(|t| t.val),
        };
        let target = requested_target.or(cfg_target);

        if jobs == Some(0) {
            failure::bail!("jobs must be at least 1")
        }
        if jobs.is_some() && config.jobserver_from_env().is_some() {
            config.shell().warn(
                "a `-j` argument was passed to Cargo but Cargo is \
                 also configured with an external jobserver in \
                 its environment, ignoring the `-j` parameter",
            )?;
        }
        let cfg_jobs: Option<u32> = config.get("build.jobs")?;
        let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32);
        Ok(BuildConfig {
            requested_target: target,
            jobs,
            release: false,
            mode,
            message_format: MessageFormat::Human,
            force_rebuild: false,
            build_plan: false,
            cargo_as_rustc_wrapper: false,
            extra_rustc_env: Vec::new(),
            extra_rustc_args: Vec::new(),
            rustfix_diagnostic_server: RefCell::new(None),
        })
    }

    pub fn json_messages(&self) -> bool {
        self.message_format == MessageFormat::Json
    }

    pub fn test(&self) -> bool {
        self.mode == CompileMode::Test || self.mode == CompileMode::Bench
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MessageFormat {
    Human,
    Json,
    Short,
}

/// The general "mode" for what to do.
/// This is used for two purposes. The commands themselves pass this in to
/// `compile_ws` to tell it the general execution strategy. This influences
/// the default targets selected. The other use is in the `Unit` struct
/// to indicate what is being done with a specific target.
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash, PartialOrd, Ord)]
pub enum CompileMode {
    /// A target being built for a test.
    Test,
    /// Building a target with `rustc` (lib or bin).
    Build,
    /// Building a target with `rustc` to emit `rmeta` metadata only. If
    /// `test` is true, then it is also compiled with `--test` to check it like
    /// a test.
    Check { test: bool },
    /// Used to indicate benchmarks should be built. This is not used in
    /// `Target`, because it is essentially the same as `Test` (indicating
    /// `--test` should be passed to rustc) and by using `Test` instead it
    /// allows some de-duping of Units to occur.
    Bench,
    /// A target that will be documented with `rustdoc`.
    /// If `deps` is true, then it will also document all dependencies.
    Doc { deps: bool },
    /// A target that will be tested with `rustdoc`.
    Doctest,
    /// A marker for Units that represent the execution of a `build.rs` script.
    RunCustomBuild,
}

impl ser::Serialize for CompileMode {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        use self::CompileMode::*;
        match *self {
            Test => "test".serialize(s),
            Build => "build".serialize(s),
            Check { .. } => "check".serialize(s),
            Bench => "bench".serialize(s),
            Doc { .. } => "doc".serialize(s),
            Doctest => "doctest".serialize(s),
            RunCustomBuild => "run-custom-build".serialize(s),
        }
    }
}

impl CompileMode {
    /// Returns `true` if the unit is being checked.
    pub fn is_check(self) -> bool {
        match self {
            CompileMode::Check { .. } => true,
            _ => false,
        }
    }

    /// Returns `true` if this is a doc or doc test. Be careful using this.
    /// Although both run rustdoc, the dependencies for those two modes are
    /// very different.
    pub fn is_doc(self) -> bool {
        match self {
            CompileMode::Doc { .. } | CompileMode::Doctest => true,
            _ => false,
        }
    }

    /// Returns `true` if this is any type of test (test, benchmark, doc test, or
    /// check test).
    pub fn is_any_test(self) -> bool {
        match self {
            CompileMode::Test
            | CompileMode::Bench
            | CompileMode::Check { test: true }
            | CompileMode::Doctest => true,
            _ => false,
        }
    }

    /// Returns `true` if this is the *execution* of a `build.rs` script.
    pub fn is_run_custom_build(self) -> bool {
        self == CompileMode::RunCustomBuild
    }

    /// List of all modes (currently used by `cargo clean -p` for computing
    /// all possible outputs).
    pub fn all_modes() -> &'static [CompileMode] {
        static ALL: [CompileMode; 9] = [
            CompileMode::Test,
            CompileMode::Build,
            CompileMode::Check { test: true },
            CompileMode::Check { test: false },
            CompileMode::Bench,
            CompileMode::Doc { deps: true },
            CompileMode::Doc { deps: false },
            CompileMode::Doctest,
            CompileMode::RunCustomBuild,
        ];
        &ALL
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, Clone)]
pub struct TransportMetrics {
    pub bytes_read: u64,
    pub bytes_written: u64,
}

impl TransportMetrics {
    pub fn new() -> TransportMetrics {
        TransportMetrics {
            bytes_read: 0,
            bytes_written: 0,
        }
    }
}
<commit_msg>add comment<commit_after>#[derive(Debug, Clone)]
pub struct TransportMetrics {
    // These numbers are snapshots given that metrics are recorded concurrently
    // by each transport and transmitted to the protocol at regular intervals.
    pub bytes_read: u64,
    pub bytes_written: u64,
}

impl TransportMetrics {
    pub fn new() -> TransportMetrics {
        TransportMetrics {
            bytes_read: 0,
            bytes_written: 0,
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    let _ = main as usize as *const fn();
}

// END RUST SOURCE
// START rustc.main.ConstProp.before.mir
//  bb0: {
//      ...
//      _3 = const main as fn() (Pointer(ReifyFnPointer));
//      _2 = move _3 as usize (Misc);
//      ...
//      _1 = move _2 as *const fn() (Misc);
//      ...
//  }
// END rustc.main.ConstProp.before.mir
// START rustc.main.ConstProp.after.mir
//  bb0: {
//      ...
//      _3 = const Scalar(AllocId(1).0x0) : fn();
//      _2 = move _3 as usize (Misc);
//      ...
//      _1 = const Scalar(AllocId(1).0x0) : *const fn();
//      ...
//  }
// END rustc.main.ConstProp.after.mir
<commit_msg>const_prop no longer does ptr-to-int casts<commit_after>fn main() {
    let _ = main as usize as *const fn();
}

// END RUST SOURCE
// START rustc.main.ConstProp.before.mir
//  bb0: {
//      ...
//      _3 = const main as fn() (Pointer(ReifyFnPointer));
//      _2 = move _3 as usize (Misc);
//      ...
//      _1 = move _2 as *const fn() (Misc);
//      ...
//  }
// END rustc.main.ConstProp.before.mir
// START rustc.main.ConstProp.after.mir
//  bb0: {
//      ...
//      _3 = const Scalar(AllocId(1).0x0) : fn();
//      _2 = move _3 as usize (Misc);
//      ...
//      _1 = move _2 as *const fn() (Misc);
//      ...
//  }
// END rustc.main.ConstProp.after.mir
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(nll)]

enum Either {
    One(X),
    Two(X),
}

struct X(Y);

struct Y;

pub fn main() {
    let e = Either::One(X(Y));
    let mut em = Either::One(X(Y));

    let r = &e;
    let rm = &mut Either::One(X(Y));

    let x = X(Y);
    let mut xm = X(Y);

    let s = &x;
    let sm = &mut X(Y);

    let ve = vec![Either::One(X(Y))];

    let vr = &ve;
    let vrm = &mut vec![Either::One(X(Y))];

    let vx = vec![X(Y)];

    let vs_ = &vx;
    let vsm = &mut vec![X(Y)];

    // --------

    let X(_t) = *s;
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION s
    if let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION r
    while let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION r
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION r
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION r
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }

    let X(_t) = *sm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION sm
    if let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION rm
    while let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION rm
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
        // TODO: should suggest removing `ref mut` too
    }

    let X(_t) = vs_[0];
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vs_[0]
    if let Either::One(_t) = vr[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vr[0]
    while let Either::One(_t) = vr[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vr[0]
    match vr[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vr[0]
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match vr[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vr[0]
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }

    let X(_t) = vsm[0];
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vsm[0]
    if let Either::One(_t) = vrm[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vrm[0]
    while let Either::One(_t) = vrm[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vrm[0]
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
        // TODO: should suggest removing `ref mut` too
    }

    // --------

    let &X(_t) = s;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    match r {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f1(&X(_t): &X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)

    let &mut X(_t) = sm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::Two(_t)
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f2(&mut X(_t): &mut X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)

    // --------

    let &X(_t) = &x;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }

    let &mut X(_t) = &mut xm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t)
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        | &mut Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
}
<commit_msg>Change TODOs to FIXMEs<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(nll)]

enum Either {
    One(X),
    Two(X),
}

struct X(Y);

struct Y;

pub fn main() {
    let e = Either::One(X(Y));
    let mut em = Either::One(X(Y));

    let r = &e;
    let rm = &mut Either::One(X(Y));

    let x = X(Y);
    let mut xm = X(Y);

    let s = &x;
    let sm = &mut X(Y);

    let ve = vec![Either::One(X(Y))];

    let vr = &ve;
    let vrm = &mut vec![Either::One(X(Y))];

    let vx = vec![X(Y)];

    let vs_ = &vx;
    let vsm = &mut vec![X(Y)];

    // --------

    let X(_t) = *s;
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION s
    if let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION r
    while let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION r
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION r
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION r
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // FIXME: should suggest removing `ref` too
    }

    let X(_t) = *sm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION sm
    if let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION rm
    while let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `*`
    //~| SUGGESTION rm
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // FIXME: should suggest removing `ref` too
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing the `*`
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
        // FIXME: should suggest removing `ref mut` too
    }

    let X(_t) = vs_[0];
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vs_[0]
    if let Either::One(_t) = vr[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vr[0]
    while let Either::One(_t) = vr[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vr[0]
    match vr[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vr[0]
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match vr[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vr[0]
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // FIXME: should suggest removing `ref` too
    }

    let X(_t) = vsm[0];
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vsm[0]
    if let Either::One(_t) = vrm[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vrm[0]
    while let Either::One(_t) = vrm[0] { }
    //~^ ERROR cannot move
    //~| HELP consider borrowing here
    //~| SUGGESTION &vrm[0]
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // FIXME: should suggest removing `ref` too
    }
    match vrm[0] {
        //~^ ERROR cannot move
        //~| HELP consider borrowing here
        //~| SUGGESTION &vrm[0]
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
        // FIXME: should suggest removing `ref mut` too
    }

    // --------

    let &X(_t) = s;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    match r {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // FIXME: would really like a suggestion here too
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f1(&X(_t): &X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)

    let &mut X(_t) = sm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::Two(_t)
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f2(&mut X(_t): &mut X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)

    // --------

    let &X(_t) = &x;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&`
    //~| SUGGESTION Either::One(_t)
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // FIXME: would really like a suggestion here too
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing the `&`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }

    let &mut X(_t) = &mut xm;
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing the `&mut`
    //~| SUGGESTION Either::One(_t)
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t)
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        | &mut Either::Two(_t) => (),
        // FIXME: would really like a suggestion here too
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing the `&mut`
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
}
<|endoftext|>


<commit_before>// spell-checker:ignore (vars) charf decf floatf intf scif strf Cninety

//! Sub is a token that represents a
//! segment of the format string that is a substitution
//! it is created by Sub's implementation of the Tokenizer trait
//! Subs which have numeric field chars make use of the num_format
//! submodule
use crate::error::{UResult, UUsageError};
use itertools::{put_back_n, PutBackN};
use std::iter::Peekable;
use std::process::exit;
use std::slice::Iter;
use std::str::Chars;
// use std::collections::HashSet;

use super::num_format::format_field::{FieldType, FormatField};
use super::num_format::num_format;
use super::token;
use super::unescaped_text::UnescapedText;

const EXIT_ERR: i32 = 1;

fn convert_asterisk_arg_int(asterisk_arg: &str) -> isize {
    // this is a costly way to parse the
    // args used for asterisk values into integers
    // from various bases. Actually doing it correctly
    // (going through the pipeline to intf, but returning
    // the integer instead of writing it to string and then
    // back) is on the refactoring TODO
    let field_type = FieldType::Intf;
    let field_char = 'i';
    let field_info = FormatField {
        min_width: Some(0),
        second_field: Some(0),
        orig: &asterisk_arg.to_string(),
        field_type: &field_type,
        field_char: &field_char,
    };
    num_format::num_format(&field_info, Some(&asterisk_arg.to_string()))
        .unwrap()
        .parse::<isize>()
        .unwrap()
}

pub enum CanAsterisk<T> {
    Fixed(T),
    Asterisk,
}

// Sub is a tokenizer which creates tokens
// for substitution segments of a format string
pub struct Sub {
    min_width: CanAsterisk<Option<isize>>,
    second_field: CanAsterisk<Option<u32>>,
    field_char: char,
    field_type: FieldType,
    orig: String,
    prefix_char: char,
}
impl Sub {
    pub fn new(
        min_width: CanAsterisk<Option<isize>>,
        second_field: CanAsterisk<Option<u32>>,
        field_char: char,
        orig: String,
        prefix_char: char,
    ) -> Self {
        // for more dry printing, field characters are grouped
        // in initialization of token.
        let field_type = match field_char {
            's' | 'b' => FieldType::Strf,
            'd' | 'i' | 'u' | 'o' | 'x' | 'X' => FieldType::Intf,
            'f' | 'F' => FieldType::Floatf,
            'a' | 'A' => FieldType::CninetyNineHexFloatf,
            'e' | 'E' => FieldType::Scif,
            'g' | 'G' => FieldType::Decf,
            'c' => FieldType::Charf,
            _ => {
                // should be unreachable.
                println!("Invalid field type");
                exit(EXIT_ERR);
            }
        };
        Self {
            min_width,
            second_field,
            field_char,
            field_type,
            orig,
            prefix_char,
        }
    }
}

#[derive(Default)]
struct SubParser {
    min_width_tmp: Option<String>,
    min_width_is_asterisk: bool,
    past_decimal: bool,
    second_field_tmp: Option<String>,
    second_field_is_asterisk: bool,
    specifiers_found: bool,
    field_char: Option<char>,
    text_so_far: String,
}

impl SubParser {
    fn new() -> Self {
        Self::default()
    }
    fn from_it(
        it: &mut PutBackN<Chars>,
        args: &mut Peekable<Iter<String>>,
    ) -> UResult<Option<Box<dyn token::Token>>> {
        let mut parser = Self::new();
        if parser.sub_vals_retrieved(it)? {
            let t: Box<dyn token::Token> = Self::build_token(parser);
            t.print(args);
            Ok(Some(t))
        } else {
            Ok(None)
        }
    }
    fn build_token(parser: Self) -> Box<dyn token::Token> {
        // not a self method so as to allow move of sub-parser vals.
        // return new Sub struct as token
        let prefix_char = match &parser.min_width_tmp {
            Some(width) if width.starts_with('0') => '0',
            _ => ' ',
        };

        let t: Box<dyn token::Token> = Box::new(Sub::new(
            if parser.min_width_is_asterisk {
                CanAsterisk::Asterisk
            } else {
                CanAsterisk::Fixed(parser.min_width_tmp.map(|x| x.parse::<isize>().unwrap()))
            },
            if parser.second_field_is_asterisk {
                CanAsterisk::Asterisk
            } else {
                CanAsterisk::Fixed(parser.second_field_tmp.map(|x| x.parse::<u32>().unwrap()))
            },
            parser.field_char.unwrap(),
            parser.text_so_far,
            prefix_char,
        ));
        t
    }
    fn sub_vals_retrieved(&mut self, it: &mut PutBackN<Chars>) -> UResult<bool> {
        if !Self::successfully_eat_prefix(it, &mut self.text_so_far)? {
            return Ok(false);
        }
        // this fn in particular is much longer than it needs to be
        // .could get a lot
        // of code savings just by cleaning it up. shouldn't use a regex
        // though, as we want to mimic the original behavior of printing
        // the field as interpreted up until the error in the field.

        let mut legal_fields = vec![
            // 'a', 'A', //c99 hex float implementation not yet complete
            'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'i', 'o', 's', 'u', 'x', 'X',
        ];
        let mut specifiers = vec!['h', 'j', 'l', 'L', 't', 'z'];
        legal_fields.sort_unstable();
        specifiers.sort_unstable();

        // divide substitution from %([0-9]+)?(.[0-9+])?([a-zA-Z])
        // into min_width, second_field, field_char
        for ch in it {
            self.text_so_far.push(ch);
            match ch as char {
                '-' | '*' | '0'..='9' => {
                    if !self.past_decimal {
                        if self.min_width_is_asterisk || self.specifiers_found {
                            return Err(UUsageError::new(
                                1,
                                format!("%{}: invalid conversion specification", &self.text_so_far),
                            ));
                        }
                        if self.min_width_tmp.is_none() {
                            self.min_width_tmp = Some(String::new());
                        }
                        match self.min_width_tmp.as_mut() {
                            Some(x) => {
                                if (ch == '-' || ch == '*') && !x.is_empty() {
                                    return Err(UUsageError::new(
                                        1,
                                        format!(
                                            "%{}: invalid conversion specification",
                                            &self.text_so_far
                                        ),
                                    ));
                                }
                                if ch == '*' {
                                    self.min_width_is_asterisk = true;
                                }
                                x.push(ch);
                            }
                            None => {
                                panic!("should be unreachable");
                            }
                        }
                    } else {
                        // second field should never have a
                        // negative value
                        if self.second_field_is_asterisk || ch == '-' || self.specifiers_found {
                            return Err(UUsageError::new(
                                1,
                                format!("%{}: invalid conversion specification", &self.text_so_far),
                            ));
                        }
                        if self.second_field_tmp.is_none() {
                            self.second_field_tmp = Some(String::new());
                        }
                        match self.second_field_tmp.as_mut() {
                            Some(x) => {
                                if ch == '*' && !x.is_empty() {
                                    return Err(UUsageError::new(
                                        1,
                                        format!(
                                            "%{}: invalid conversion specification",
                                            &self.text_so_far
                                        ),
                                    ));
                                }
                                if ch == '*' {
                                    self.second_field_is_asterisk = true;
                                }
                                x.push(ch);
                            }
                            None => {
                                panic!("should be unreachable");
                            }
                        }
                    }
                }
                '.' => {
                    if !self.past_decimal {
                        self.past_decimal = true;
                    } else {
                        return Err(UUsageError::new(
                            1,
                            format!("%{}: invalid conversion specification", &self.text_so_far),
                        ));
                    }
                }
                x if legal_fields.binary_search(&x).is_ok() => {
                    self.field_char = Some(ch);
                    self.text_so_far.push(ch);
                    break;
                }
                x if specifiers.binary_search(&x).is_ok() => {
                    if !self.past_decimal {
                        self.past_decimal = true;
                    }
                    if !self.specifiers_found {
                        self.specifiers_found = true;
                    }
                }
                _ => {
                    return Err(UUsageError::new(
                        1,
                        format!("%{}: invalid conversion specification", &self.text_so_far),
                    ));
                }
            }
        }
        if self.field_char.is_none() {
            return Err(UUsageError::new(
                1,
                format!("%{}: invalid conversion specification", &self.text_so_far),
            ));
        }
        let field_char_retrieved = self.field_char.unwrap();
        if self.past_decimal && self.second_field_tmp.is_none() {
            self.second_field_tmp = Some(String::from("0"));
        }
        self.validate_field_params(field_char_retrieved)?;
        // if the dot is provided without a second field
        // printf interprets it as 0.
        if let Some(x) = self.second_field_tmp.as_mut() {
            if x.is_empty() {
                self.min_width_tmp = Some(String::from("0"));
            }
        }

        Ok(true)
    }
    fn successfully_eat_prefix(
        it: &mut PutBackN<Chars>,
        text_so_far: &mut String,
    ) -> UResult<bool> {
        // get next two chars,
        // if they're '%%' we're not tokenizing it
        // else put chars back
        let preface = it.next();
        let n_ch = it.next();
        if preface == Some('%') && n_ch != Some('%') {
            match n_ch {
                Some(x) => {
                    it.put_back(x);
                    Ok(true)
                }
                None => {
                    text_so_far.push('%');
                    return Err(UUsageError::new(
                        1,
                        format!("%{}: invalid conversion specification", &text_so_far[..]),
                    ));
                }
            }
        } else {
            if let Some(x) = n_ch {
                it.put_back(x);
            };
            if let Some(x) = preface {
                it.put_back(x);
            };
            Ok(false)
        }
    }
    fn validate_field_params(&self, field_char: char) -> UResult<()> {
        // check for illegal combinations here when possible vs
        // on each application so we check less per application
        // to do: move these checks to Sub::new
        if (field_char == 's' && self.min_width_tmp == Some(String::from("0")))
            || (field_char == 'c'
                && (self.min_width_tmp == Some(String::from("0")) || self.past_decimal))
            || (field_char == 'b'
                && (self.min_width_tmp.is_some()
                    || self.past_decimal
                    || self.second_field_tmp.is_some()))
        {
            // invalid string substitution
            // to do: include information about an invalid
            // string substitution
            return Err(UUsageError::new(
                1,
                format!("%{}: invalid conversion specification", &self.text_so_far),
            ));
        }
        Ok(())
    }
}

impl token::Tokenizer for Sub {
    fn from_it(
        it: &mut PutBackN<Chars>,
        args: &mut Peekable<Iter<String>>,
    ) -> UResult<Option<Box<dyn token::Token>>> {
        SubParser::from_it(it, args)
    }
}
impl token::Token for Sub {
    fn print(&self, pf_args_it: &mut Peekable<Iter<String>>) {
        let field = FormatField {
            min_width: match self.min_width {
                CanAsterisk::Fixed(x) => x,
                CanAsterisk::Asterisk => {
                    match pf_args_it.next() {
                        // temporary, use intf.rs instead
                        Some(x) => Some(convert_asterisk_arg_int(x)),
                        None => Some(0),
                    }
                }
            },
            second_field: match self.second_field {
                CanAsterisk::Fixed(x) => x,
                CanAsterisk::Asterisk => {
                    match pf_args_it.next() {
                        // temporary, use intf.rs instead
                        Some(x) => {
                            let result = convert_asterisk_arg_int(x);
                            if result < 0 {
                                None
                            } else {
                                Some(result as u32)
                            }
                        }
                        None => Some(0),
                    }
                }
            },
            field_char: &self.field_char,
            field_type: &self.field_type,
            orig: &self.orig,
        };
        let pf_arg = pf_args_it.next();

        // minimum width is handled independently of actual
        // field char
        let pre_min_width_opt: Option<String> = match *field.field_type {
            // if %s just return arg
            // if %b use UnescapedText module's unescape-fn
            // if %c return first char of arg
            FieldType::Strf | FieldType::Charf => {
                match pf_arg {
                    Some(arg_string) => {
                        match *field.field_char {
                            's' => Some(match field.second_field {
                                Some(max) => String::from(&arg_string[..max as usize]),
                                None => arg_string.clone(),
                            }),
                            'b' => {
                                let mut a_it = put_back_n(arg_string.chars());
                                UnescapedText::from_it_core(&mut a_it, true);
                                None
                            }
                            // for 'c': get iter of string vals,
                            // get opt<char> of first val
                            // and map it to opt<String>
                            /* 'c' | */
                            _ => arg_string.chars().next().map(|x| x.to_string()),
                        }
                    }
                    None => None,
                }
            }
            _ => {
                // non string/char fields are delegated to num_format
                num_format::num_format(&field, pf_arg)
            }
        };
        if let Some(pre_min_width) = pre_min_width_opt {
            // if have a string, print it, ensuring minimum width is met.
            print!(
                "{}",
                match field.min_width {
                    Some(min_width) => {
                        let diff: isize = min_width.abs() as isize - pre_min_width.len() as isize;
                        if diff > 0 {
                            let mut final_str = String::new();
                            // definitely more efficient ways
                            //  to do this.
                            let pad_before = min_width > 0;
                            if !pad_before {
                                final_str.push_str(&pre_min_width);
                            }
                            for _ in 0..diff {
                                final_str.push(self.prefix_char);
                            }
                            if pad_before {
                                final_str.push_str(&pre_min_width);
                            }
                            final_str
                        } else {
                            pre_min_width
                        }
                    }
                    None => pre_min_width,
                }
            );
        }
    }
}
<commit_msg>uucore(memo): refactor error propogation with new SubError enum<commit_after>// spell-checker:ignore (vars) charf decf floatf intf scif strf Cninety

//! Sub is a token that represents a
//! segment of the format string that is a substitution
//! it is created by Sub's implementation of the Tokenizer trait
//! Subs which have numeric field chars make use of the num_format
//! submodule
use crate::error::{UError, UResult};
use itertools::{put_back_n, PutBackN};
use std::error::Error;
use std::fmt::Display;
use std::iter::Peekable;
use std::process::exit;
use std::slice::Iter;
use std::str::Chars;
// use std::collections::HashSet;

use super::num_format::format_field::{FieldType, FormatField};
use super::num_format::num_format;
use super::token;
use super::unescaped_text::UnescapedText;

const EXIT_ERR: i32 = 1;

#[derive(Debug)]
pub enum SubError {
    InvalidSpec(String),
}

impl Display for SubError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            Self::InvalidSpec(s) => write!(f, "%{}: invalid conversion specification", s),
        }
    }
}

impl Error for SubError {}

impl UError for SubError {}

fn convert_asterisk_arg_int(asterisk_arg: &str) -> isize {
    // this is a costly way to parse the
    // args used for asterisk values into integers
    // from various bases. Actually doing it correctly
    // (going through the pipeline to intf, but returning
    // the integer instead of writing it to string and then
    // back) is on the refactoring TODO
    let field_type = FieldType::Intf;
    let field_char = 'i';
    let field_info = FormatField {
        min_width: Some(0),
        second_field: Some(0),
        orig: &asterisk_arg.to_string(),
        field_type: &field_type,
        field_char: &field_char,
    };
    num_format::num_format(&field_info, Some(&asterisk_arg.to_string()))
        .unwrap()
        .parse::<isize>()
        .unwrap()
}

pub enum CanAsterisk<T> {
    Fixed(T),
    Asterisk,
}

// Sub is a tokenizer which creates tokens
// for substitution segments of a format string
pub struct Sub {
    min_width: CanAsterisk<Option<isize>>,
    second_field: CanAsterisk<Option<u32>>,
    field_char: char,
    field_type: FieldType,
    orig: String,
    prefix_char: char,
}
impl Sub {
    pub fn new(
        min_width: CanAsterisk<Option<isize>>,
        second_field: CanAsterisk<Option<u32>>,
        field_char: char,
        orig: String,
        prefix_char: char,
    ) -> Self {
        // for more dry printing, field characters are grouped
        // in initialization of token.
        let field_type = match field_char {
            's' | 'b' => FieldType::Strf,
            'd' | 'i' | 'u' | 'o' | 'x' | 'X' => FieldType::Intf,
            'f' | 'F' => FieldType::Floatf,
            'a' | 'A' => FieldType::CninetyNineHexFloatf,
            'e' | 'E' => FieldType::Scif,
            'g' | 'G' => FieldType::Decf,
            'c' => FieldType::Charf,
            _ => {
                // should be unreachable.
                println!("Invalid field type");
                exit(EXIT_ERR);
            }
        };
        Self {
            min_width,
            second_field,
            field_char,
            field_type,
            orig,
            prefix_char,
        }
    }
}

#[derive(Default)]
struct SubParser {
    min_width_tmp: Option<String>,
    min_width_is_asterisk: bool,
    past_decimal: bool,
    second_field_tmp: Option<String>,
    second_field_is_asterisk: bool,
    specifiers_found: bool,
    field_char: Option<char>,
    text_so_far: String,
}

impl SubParser {
    fn new() -> Self {
        Self::default()
    }
    fn from_it(
        it: &mut PutBackN<Chars>,
        args: &mut Peekable<Iter<String>>,
    ) -> UResult<Option<Box<dyn token::Token>>> {
        let mut parser = Self::new();
        if parser.sub_vals_retrieved(it)? {
            let t: Box<dyn token::Token> = Self::build_token(parser);
            t.print(args);
            Ok(Some(t))
        } else {
            Ok(None)
        }
    }
    fn build_token(parser: Self) -> Box<dyn token::Token> {
        // not a self method so as to allow move of sub-parser vals.
        // return new Sub struct as token
        let prefix_char = match &parser.min_width_tmp {
            Some(width) if width.starts_with('0') => '0',
            _ => ' ',
        };

        let t: Box<dyn token::Token> = Box::new(Sub::new(
            if parser.min_width_is_asterisk {
                CanAsterisk::Asterisk
            } else {
                CanAsterisk::Fixed(parser.min_width_tmp.map(|x| x.parse::<isize>().unwrap()))
            },
            if parser.second_field_is_asterisk {
                CanAsterisk::Asterisk
            } else {
                CanAsterisk::Fixed(parser.second_field_tmp.map(|x| x.parse::<u32>().unwrap()))
            },
            parser.field_char.unwrap(),
            parser.text_so_far,
            prefix_char,
        ));
        t
    }
    fn sub_vals_retrieved(&mut self, it: &mut PutBackN<Chars>) -> UResult<bool> {
        if !Self::successfully_eat_prefix(it, &mut self.text_so_far)? {
            return Ok(false);
        }
        // this fn in particular is much longer than it needs to be
        // .could get a lot
        // of code savings just by cleaning it up. shouldn't use a regex
        // though, as we want to mimic the original behavior of printing
        // the field as interpreted up until the error in the field.

        let mut legal_fields = vec![
            // 'a', 'A', //c99 hex float implementation not yet complete
            'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'i', 'o', 's', 'u', 'x', 'X',
        ];
        let mut specifiers = vec!['h', 'j', 'l', 'L', 't', 'z'];
        legal_fields.sort_unstable();
        specifiers.sort_unstable();

        // divide substitution from %([0-9]+)?(.[0-9+])?([a-zA-Z])
        // into min_width, second_field, field_char
        for ch in it {
            self.text_so_far.push(ch);
            match ch as char {
                '-' | '*' | '0'..='9' => {
                    if !self.past_decimal {
                        if self.min_width_is_asterisk || self.specifiers_found {
                            return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
                        }
                        if self.min_width_tmp.is_none() {
                            self.min_width_tmp = Some(String::new());
                        }
                        match self.min_width_tmp.as_mut() {
                            Some(x) => {
                                if (ch == '-' || ch == '*') && !x.is_empty() {
                                    return Err(
                                        SubError::InvalidSpec(self.text_so_far.clone()).into()
                                    );
                                }
                                if ch == '*' {
                                    self.min_width_is_asterisk = true;
                                }
                                x.push(ch);
                            }
                            None => {
                                panic!("should be unreachable");
                            }
                        }
                    } else {
                        // second field should never have a
                        // negative value
                        if self.second_field_is_asterisk || ch == '-' || self.specifiers_found {
                            return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
                        }
                        if self.second_field_tmp.is_none() {
                            self.second_field_tmp = Some(String::new());
                        }
                        match self.second_field_tmp.as_mut() {
                            Some(x) => {
                                if ch == '*' && !x.is_empty() {
                                    return Err(
                                        SubError::InvalidSpec(self.text_so_far.clone()).into()
                                    );
                                }
                                if ch == '*' {
                                    self.second_field_is_asterisk = true;
                                }
                                x.push(ch);
                            }
                            None => {
                                panic!("should be unreachable");
                            }
                        }
                    }
                }
                '.' => {
                    if !self.past_decimal {
                        self.past_decimal = true;
                    } else {
                        return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
                    }
                }
                x if legal_fields.binary_search(&x).is_ok() => {
                    self.field_char = Some(ch);
                    self.text_so_far.push(ch);
                    break;
                }
                x if specifiers.binary_search(&x).is_ok() => {
                    if !self.past_decimal {
                        self.past_decimal = true;
                    }
                    if !self.specifiers_found {
                        self.specifiers_found = true;
                    }
                }
                _ => {
                    return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
                }
            }
        }
        if self.field_char.is_none() {
            return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
        }
        let field_char_retrieved = self.field_char.unwrap();
        if self.past_decimal && self.second_field_tmp.is_none() {
            self.second_field_tmp = Some(String::from("0"));
        }
        self.validate_field_params(field_char_retrieved)?;
        // if the dot is provided without a second field
        // printf interprets it as 0.
        if let Some(x) = self.second_field_tmp.as_mut() {
            if x.is_empty() {
                self.min_width_tmp = Some(String::from("0"));
            }
        }

        Ok(true)
    }
    fn successfully_eat_prefix(
        it: &mut PutBackN<Chars>,
        text_so_far: &mut String,
    ) -> UResult<bool> {
        // get next two chars,
        // if they're '%%' we're not tokenizing it
        // else put chars back
        let preface = it.next();
        let n_ch = it.next();
        if preface == Some('%') && n_ch != Some('%') {
            match n_ch {
                Some(x) => {
                    it.put_back(x);
                    Ok(true)
                }
                None => {
                    text_so_far.push('%');
                    Err(SubError::InvalidSpec(text_so_far.clone()).into())
                }
            }
        } else {
            if let Some(x) = n_ch {
                it.put_back(x);
            };
            if let Some(x) = preface {
                it.put_back(x);
            };
            Ok(false)
        }
    }
    fn validate_field_params(&self, field_char: char) -> UResult<()> {
        // check for illegal combinations here when possible vs
        // on each application so we check less per application
        // to do: move these checks to Sub::new
        if (field_char == 's' && self.min_width_tmp == Some(String::from("0")))
            || (field_char == 'c'
                && (self.min_width_tmp == Some(String::from("0")) || self.past_decimal))
            || (field_char == 'b'
                && (self.min_width_tmp.is_some()
                    || self.past_decimal
                    || self.second_field_tmp.is_some()))
        {
            // invalid string substitution
            // to do: include information about an invalid
            // string substitution
            return Err(SubError::InvalidSpec(self.text_so_far.clone()).into());
        }
        Ok(())
    }
}

impl token::Tokenizer for Sub {
    fn from_it(
        it: &mut PutBackN<Chars>,
        args: &mut Peekable<Iter<String>>,
    ) -> UResult<Option<Box<dyn token::Token>>> {
        SubParser::from_it(it, args)
    }
}
impl token::Token for Sub {
    fn print(&self, pf_args_it: &mut Peekable<Iter<String>>) {
        let field = FormatField {
            min_width: match self.min_width {
                CanAsterisk::Fixed(x) => x,
                CanAsterisk::Asterisk => {
                    match pf_args_it.next() {
                        // temporary, use intf.rs instead
                        Some(x) => Some(convert_asterisk_arg_int(x)),
                        None => Some(0),
                    }
                }
            },
            second_field: match self.second_field {
                CanAsterisk::Fixed(x) => x,
                CanAsterisk::Asterisk => {
                    match pf_args_it.next() {
                        // temporary, use intf.rs instead
                        Some(x) => {
                            let result = convert_asterisk_arg_int(x);
                            if result < 0 {
                                None
                            } else {
                                Some(result as u32)
                            }
                        }
                        None => Some(0),
                    }
                }
            },
            field_char: &self.field_char,
            field_type: &self.field_type,
            orig: &self.orig,
        };
        let pf_arg = pf_args_it.next();

        // minimum width is handled independently of actual
        // field char
        let pre_min_width_opt: Option<String> = match *field.field_type {
            // if %s just return arg
            // if %b use UnescapedText module's unescape-fn
            // if %c return first char of arg
            FieldType::Strf | FieldType::Charf => {
                match pf_arg {
                    Some(arg_string) => {
                        match *field.field_char {
                            's' => Some(match field.second_field {
                                Some(max) => String::from(&arg_string[..max as usize]),
                                None => arg_string.clone(),
                            }),
                            'b' => {
                                let mut a_it = put_back_n(arg_string.chars());
                                UnescapedText::from_it_core(&mut a_it, true);
                                None
                            }
                            // for 'c': get iter of string vals,
                            // get opt<char> of first val
                            // and map it to opt<String>
                            /* 'c' | */
                            _ => arg_string.chars().next().map(|x| x.to_string()),
                        }
                    }
                    None => None,
                }
            }
            _ => {
                // non string/char fields are delegated to num_format
                num_format::num_format(&field, pf_arg)
            }
        };
        if let Some(pre_min_width) = pre_min_width_opt {
            // if have a string, print it, ensuring minimum width is met.
            print!(
                "{}",
                match field.min_width {
                    Some(min_width) => {
                        let diff: isize = min_width.abs() as isize - pre_min_width.len() as isize;
                        if diff > 0 {
                            let mut final_str = String::new();
                            // definitely more efficient ways
                            //  to do this.
                            let pad_before = min_width > 0;
                            if !pad_before {
                                final_str.push_str(&pre_min_width);
                            }
                            for _ in 0..diff {
                                final_str.push(self.prefix_char);
                            }
                            if pad_before {
                                final_str.push_str(&pre_min_width);
                            }
                            final_str
                        } else {
                            pre_min_width
                        }
                    }
                    None => pre_min_width,
                }
            );
        }
    }
}
<|endoftext|>