<commit_before>// Copyright 2013 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.

//! The `Default` trait for types which may have meaningful default values.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A trait for giving a type a useful default value.
///
/// Sometimes, you want to fall back to some kind of default value, and
/// don't particularly care what it is. This comes up often with `struct`s
/// that define a set of options:
///
/// ```
/// # #[allow(dead_code)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
/// ```
///
/// How can we define some default values? You can use `Default`:
///
/// ```
/// # #[allow(dead_code)]
/// #[derive(Default)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
///
///
/// fn main() {
///     let options: SomeOptions = Default::default();
/// }
/// ```
///
/// Now, you get all of the default values. Rust implements `Default` for various primitives types.
///
/// If you want to override a particular option, but still retain the other defaults:
///
/// ```
/// # #[allow(dead_code)]
/// # #[derive(Default)]
/// # struct SomeOptions {
/// #     foo: i32,
/// #     bar: f32,
/// # }
/// fn main() {
///     let options = SomeOptions { foo: 42, ..Default::default() };
/// }
/// ```
///
/// ## Derivable
///
/// This trait can be used with `#[derive]` if all of the type's fields implement
/// `Default`. When `derive`d, it will use the default value for each field's type.
///
/// ## How can I implement `Default`?
///
/// Provide an implementation for the `default()` method that returns the value of
/// your type that should be the default:
///
/// ```
/// # #![allow(dead_code)]
/// enum Kind {
///     A,
///     B,
///     C,
/// }
///
/// impl Default for Kind {
///     fn default() -> Kind { Kind::A }
/// }
/// ```
///
/// # Examples
///
/// ```
/// # #[allow(dead_code)]
/// #[derive(Default)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Default: Sized {
    /// Returns the "default value" for a type.
    ///
    /// Default values are often some kind of initial value, identity value, or anything else that
    /// may make sense as a default.
    ///
    /// # Examples
    ///
    /// Using built-in default values:
    ///
    /// ```
    /// let i: i8 = Default::default();
    /// let (x, y): (Option<String>, f64) = Default::default();
    /// let (a, b, (c, d)): (i32, u32, (bool, bool)) = Default::default();
    /// ```
    ///
    /// Making your own:
    ///
    /// ```
    /// # #[allow(dead_code)]
    /// enum Kind {
    ///     A,
    ///     B,
    ///     C,
    /// }
    ///
    /// impl Default for Kind {
    ///     fn default() -> Kind { Kind::A }
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn default() -> Self;
}

macro_rules! default_impl {
    ($t:ty, $v:expr) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Default for $t {
            #[inline]
            fn default() -> $t { $v }
        }
    }
}

default_impl! { (), () }
default_impl! { bool, false }
default_impl! { char, '\x00' }

default_impl! { usize, 0 }
default_impl! { u8, 0 }
default_impl! { u16, 0 }
default_impl! { u32, 0 }
default_impl! { u64, 0 }

default_impl! { isize, 0 }
default_impl! { i8, 0 }
default_impl! { i16, 0 }
default_impl! { i32, 0 }
default_impl! { i64, 0 }

default_impl! { f32, 0.0f32 }
default_impl! { f64, 0.0f64 }
<commit_msg>Rollup merge of #35949 - tshepang:excess, r=GuillaumeGomez<commit_after>// Copyright 2013 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.

//! The `Default` trait for types which may have meaningful default values.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A trait for giving a type a useful default value.
///
/// Sometimes, you want to fall back to some kind of default value, and
/// don't particularly care what it is. This comes up often with `struct`s
/// that define a set of options:
///
/// ```
/// # #[allow(dead_code)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
/// ```
///
/// How can we define some default values? You can use `Default`:
///
/// ```
/// # #[allow(dead_code)]
/// #[derive(Default)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
///
/// fn main() {
///     let options: SomeOptions = Default::default();
/// }
/// ```
///
/// Now, you get all of the default values. Rust implements `Default` for various primitives types.
///
/// If you want to override a particular option, but still retain the other defaults:
///
/// ```
/// # #[allow(dead_code)]
/// # #[derive(Default)]
/// # struct SomeOptions {
/// #     foo: i32,
/// #     bar: f32,
/// # }
/// fn main() {
///     let options = SomeOptions { foo: 42, ..Default::default() };
/// }
/// ```
///
/// ## Derivable
///
/// This trait can be used with `#[derive]` if all of the type's fields implement
/// `Default`. When `derive`d, it will use the default value for each field's type.
///
/// ## How can I implement `Default`?
///
/// Provide an implementation for the `default()` method that returns the value of
/// your type that should be the default:
///
/// ```
/// # #![allow(dead_code)]
/// enum Kind {
///     A,
///     B,
///     C,
/// }
///
/// impl Default for Kind {
///     fn default() -> Kind { Kind::A }
/// }
/// ```
///
/// # Examples
///
/// ```
/// # #[allow(dead_code)]
/// #[derive(Default)]
/// struct SomeOptions {
///     foo: i32,
///     bar: f32,
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Default: Sized {
    /// Returns the "default value" for a type.
    ///
    /// Default values are often some kind of initial value, identity value, or anything else that
    /// may make sense as a default.
    ///
    /// # Examples
    ///
    /// Using built-in default values:
    ///
    /// ```
    /// let i: i8 = Default::default();
    /// let (x, y): (Option<String>, f64) = Default::default();
    /// let (a, b, (c, d)): (i32, u32, (bool, bool)) = Default::default();
    /// ```
    ///
    /// Making your own:
    ///
    /// ```
    /// # #[allow(dead_code)]
    /// enum Kind {
    ///     A,
    ///     B,
    ///     C,
    /// }
    ///
    /// impl Default for Kind {
    ///     fn default() -> Kind { Kind::A }
    /// }
    /// ```
    #[stable(feature = "rust1", since = "1.0.0")]
    fn default() -> Self;
}

macro_rules! default_impl {
    ($t:ty, $v:expr) => {
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Default for $t {
            #[inline]
            fn default() -> $t { $v }
        }
    }
}

default_impl! { (), () }
default_impl! { bool, false }
default_impl! { char, '\x00' }

default_impl! { usize, 0 }
default_impl! { u8, 0 }
default_impl! { u16, 0 }
default_impl! { u32, 0 }
default_impl! { u64, 0 }

default_impl! { isize, 0 }
default_impl! { i8, 0 }
default_impl! { i16, 0 }
default_impl! { i32, 0 }
default_impl! { i64, 0 }

default_impl! { f32, 0.0f32 }
default_impl! { f64, 0.0f64 }
<|endoftext|>


<commit_before>//! Bi-directional Dijkstra distance labeling.

use std::hash::Hash;

use timely::order::Product;
use timely::dataflow::*;

use ::{Collection, ExchangeData};
use ::operators::*;
use ::lattice::Lattice;
use ::operators::iterate::Variable;

/// Returns the subset of `goals` that can reach each other in `edges`, with distance.
///
/// This method performs bidirectional search, from both ends of each goal in forward
/// and reverse direction, for the sources and targets respectively. Each search can
/// examine a fraction of the graph before meeting, and multiple searches can be managed
/// concurrently.
///
/// Goals that cannot reach from the source to the target are relatively expensive, as
/// the entire graph must be explored to confirm this. A graph connectivity pre-filter
/// could be good insurance here.
pub fn bidijkstra<G, N>(edges: &Collection<G, (N,N)>, goals: &Collection<G, (N,N)>) -> Collection<G, ((N,N), u32)>
where
    G: Scope,
    G::Timestamp: Lattice+Ord,
    N: ExchangeData+Hash,
{
    edges.scope().iterative::<u64,_,_>(|inner| {

        // Our plan is to start evolving distances from both sources and destinations.
        // The evolution from a source or destination should continue as long as there
        // is a corresponding destination or source that has not yet been reached.

        // forward and reverse (node, (root, dist))
        let forward = Variable::new_from(goals.map(|(x,_)| (x.clone(),(x.clone(),0))).enter(inner), Product::new(Default::default(), 1));
        let reverse = Variable::new_from(goals.map(|(_,y)| (y.clone(),(y.clone(),0))).enter(inner), Product::new(Default::default(), 1));

        let goals = goals.enter(inner);
        let edges = edges.enter(inner);

        // Let's determine which (src, dst) pairs are ready to return.
        //
        //   done(src, dst) := forward(src, med), reverse(dst, med), goal(src, dst).
        //
        // This is a cyclic join, which should scare us a bunch.
        let reached =
        forward
            .join_map(&reverse, |_, (src,d1), (dst,d2)| ((src.clone(), dst.clone()), *d1 + *d2))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)));

        let active =
        reached
            .semijoin(&goals)
            .negate()
            .map(|(srcdst,_)| srcdst)
            .concat(&goals)
            .consolidate();

        // Let's expand out forward queries that are active.
        let forward_active = active.map(|(x,_y)| x).distinct();
        let forward_next =
        forward
            .map(|(med, (src, dist))| (src, (med, dist)))
            .semijoin(&forward_active)
            .map(|(src, (med, dist))| (med, (src, dist)))
            .join_map(&edges, |_med, (src, dist), next| (next.clone(), (src.clone(), *dist+1)))
            .concat(&forward)
            .map(|(next, (src, dist))| ((next, src), dist))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)))
            .map(|((next, src), dist)| (next, (src, dist)));

        forward.set(&forward_next);

        // Let's expand out reverse queries that are active.
        let reverse_active = active.map(|(_x,y)| y).distinct();
        let reverse_next =
        reverse
            .map(|(med, (rev, dist))| (rev, (med, dist)))
            .semijoin(&reverse_active)
            .map(|(rev, (med, dist))| (med, (rev, dist)))
            .join_map(&edges.map(|(x,y)| (y,x)), |_med, (rev, dist), next| (next.clone(), (rev.clone(), *dist+1)))
            .concat(&reverse)
            .map(|(next, (rev, dist))| ((next, rev), dist))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)))
            .map(|((next,rev), dist)| (next, (rev, dist)));

        reverse.set(&reverse_next);

        reached.leave()
    })
}<commit_msg>restrict reached to goals<commit_after>//! Bi-directional Dijkstra distance labeling.

use std::hash::Hash;

use timely::order::Product;
use timely::dataflow::*;

use ::{Collection, ExchangeData};
use ::operators::*;
use ::lattice::Lattice;
use ::operators::iterate::Variable;

/// Returns the subset of `goals` that can reach each other in `edges`, with distance.
///
/// This method performs bidirectional search, from both ends of each goal in forward
/// and reverse direction, for the sources and targets respectively. Each search can
/// examine a fraction of the graph before meeting, and multiple searches can be managed
/// concurrently.
///
/// Goals that cannot reach from the source to the target are relatively expensive, as
/// the entire graph must be explored to confirm this. A graph connectivity pre-filter
/// could be good insurance here.
pub fn bidijkstra<G, N>(edges: &Collection<G, (N,N)>, goals: &Collection<G, (N,N)>) -> Collection<G, ((N,N), u32)>
where
    G: Scope,
    G::Timestamp: Lattice+Ord,
    N: ExchangeData+Hash,
{
    edges.scope().iterative::<u64,_,_>(|inner| {

        // Our plan is to start evolving distances from both sources and destinations.
        // The evolution from a source or destination should continue as long as there
        // is a corresponding destination or source that has not yet been reached.

        // forward and reverse (node, (root, dist))
        let forward = Variable::new_from(goals.map(|(x,_)| (x.clone(),(x.clone(),0))).enter(inner), Product::new(Default::default(), 1));
        let reverse = Variable::new_from(goals.map(|(_,y)| (y.clone(),(y.clone(),0))).enter(inner), Product::new(Default::default(), 1));

        let goals = goals.enter(inner);
        let edges = edges.enter(inner);

        // Let's determine which (src, dst) pairs are ready to return.
        //
        //   done(src, dst) := forward(src, med), reverse(dst, med), goal(src, dst).
        //
        // This is a cyclic join, which should scare us a bunch.
        let reached =
        forward
            .join_map(&reverse, |_, (src,d1), (dst,d2)| ((src.clone(), dst.clone()), *d1 + *d2))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)))
            .semijoin(&goals);

        let active =
        reached
            .negate()
            .map(|(srcdst,_)| srcdst)
            .concat(&goals)
            .consolidate();

        // Let's expand out forward queries that are active.
        let forward_active = active.map(|(x,_y)| x).distinct();
        let forward_next =
        forward
            .map(|(med, (src, dist))| (src, (med, dist)))
            .semijoin(&forward_active)
            .map(|(src, (med, dist))| (med, (src, dist)))
            .join_map(&edges, |_med, (src, dist), next| (next.clone(), (src.clone(), *dist+1)))
            .concat(&forward)
            .map(|(next, (src, dist))| ((next, src), dist))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)))
            .map(|((next, src), dist)| (next, (src, dist)));

        forward.set(&forward_next);

        // Let's expand out reverse queries that are active.
        let reverse_active = active.map(|(_x,y)| y).distinct();
        let reverse_next =
        reverse
            .map(|(med, (rev, dist))| (rev, (med, dist)))
            .semijoin(&reverse_active)
            .map(|(rev, (med, dist))| (med, (rev, dist)))
            .join_map(&edges.map(|(x,y)| (y,x)), |_med, (rev, dist), next| (next.clone(), (rev.clone(), *dist+1)))
            .concat(&reverse)
            .map(|(next, (rev, dist))| ((next, rev), dist))
            .reduce(|_key, s, t| t.push((s[0].0.clone(), 1)))
            .map(|((next,rev), dist)| (next, (rev, dist)));

        reverse.set(&reverse_next);

        reached.leave()
    })
}<|endoftext|>


<commit_before>use crate::{
    analysis::{
        functions::{Info as FuncInfo, Visibility},
        imports::Imports,
    },
    config::GObject,
    library::{Type as LibType, TypeId},
    version::Version,
};
use std::{collections::BTreeMap, str::FromStr};

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    Display,
    Unref,
    Hash,
}

impl FromStr for Type {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" | "destroy" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" | "ref_" => Ok(Ref),
            "unref" => Ok(Unref),
            "hash" => Ok(Hash),
            _ => Err(format!("Unknown type '{}'", s)),
        }
    }
}

#[derive(Debug, Clone)]
pub struct TraitInfo {
    pub glib_name: String,
    pub version: Option<Version>,
    pub first_parameter_mut: bool,
}

type TraitInfos = BTreeMap<Type, TraitInfo>;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum FunctionType {
    StaticStringify,
}

#[derive(Debug, Clone)]
pub struct FunctionInfo {
    pub type_: FunctionType,
    pub version: Option<Version>,
}

type FunctionInfos = BTreeMap<String, FunctionInfo>;

#[derive(Debug, Default)]
pub struct Infos {
    traits: TraitInfos,
    functions: FunctionInfos,
}

impl Infos {
    pub fn traits(&self) -> &TraitInfos {
        &self.traits
    }

    pub fn traits_mut(&mut self) -> &mut TraitInfos {
        &mut self.traits
    }

    pub fn has_trait(&self, type_: Type) -> bool {
        self.traits.contains_key(&type_)
    }

    pub fn functions(&self) -> &FunctionInfos {
        &self.functions
    }
}

/// Returns true on functions that take an instance as single argument and
/// return a string as result.
fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool {
    if func.parameters.c_parameters.len() != 1 {
        return false;
    }
    if !func.parameters.c_parameters[0].instance_parameter {
        return false;
    }

    if let Some(ret) = func.ret.parameter.as_mut() {
        if ret.lib_par.typ != TypeId::tid_utf8() {
            return false;
        }

        if func.name == "to_string" {
            // Rename to to_str to make sure it doesn't clash with ToString::to_string
            assert!(func.new_name.is_none(), "A `to_string` function can't be renamed manually. It's automatically renamed to `to_str`");
            func.new_name = Some("to_str".to_owned());

            // As to not change old code behaviour, assume non-nullability outside
            // enums and flags only, and exclusively for to_string. Function inside
            // enums and flags have been appropriately marked in Gir.
            if !obj.trust_return_value_nullability
                && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
            {
                *ret.lib_par.nullable = false;
            }
        }

        // Cannot generate Display implementation for Option<>
        !*ret.lib_par.nullable
    } else {
        false
    }
}

fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
    if func.visibility != Visibility::Comment {
        func.visibility = visibility(type_);
    }
    true
}

pub fn extract(functions: &mut Vec<FuncInfo>, parent_type: &LibType, obj: &GObject) -> Infos {
    let mut specials = Infos::default();
    let mut has_copy = false;
    let mut has_free = false;
    let mut destroy = None;

    for (pos, func) in functions.iter_mut().enumerate() {
        if is_stringify(func, parent_type, obj) {
            let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| {
                ret.lib_par.transfer == crate::library::Transfer::None
            });

            // Assume only enumerations and bitfields can return static strings
            let returns_static_ref = return_transfer_none
                && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
                // We cannot mandate returned lifetime if this is not generated.
                // (And this prevents an unused std::ffi::CStr from being emitted below)
                && func.status.need_generate();

            if returns_static_ref {
                // Override the function with a &'static (non allocating) -returning string
                // if the transfer type is none and it matches the above heuristics.
                specials.functions.insert(
                    func.glib_name.clone(),
                    FunctionInfo {
                        type_: FunctionType::StaticStringify,
                        version: func.version,
                    },
                );
            }

            // Some stringifying functions can serve as Display implementation
            if matches!(
                func.name.as_str(),
                "to_string" | "to_str" | "name" | "get_name"
            ) {
                // FUTURE: Decide which function gets precedence if multiple Display prospects exist.
                specials.traits.insert(
                    Type::Display,
                    TraitInfo {
                        glib_name: func.glib_name.clone(),
                        version: func.version,
                        first_parameter_mut: false,
                    },
                );
            }
        } else if let Ok(type_) = func.name.parse() {
            if func.name == "destroy" {
                destroy = Some((func.glib_name.clone(), pos));
                continue;
            }
            if !update_func(func, type_) {
                continue;
            }
            if func.name == "copy" {
                has_copy = true;
            } else if func.name == "free" {
                has_free = true;
            }

            let first_parameter_mut = func
                .parameters
                .c_parameters
                .first()
                .map_or(false, |p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);

            specials.traits.insert(
                type_,
                TraitInfo {
                    glib_name: func.glib_name.clone(),
                    version: func.version,
                    first_parameter_mut,
                },
            );
        }
    }

    if has_copy && !has_free {
        if let Some((glib_name, pos)) = destroy {
            let ty_ = Type::from_str("destroy").unwrap();
            let func = &mut functions[pos];
            update_func(func, ty_);
            specials.traits.insert(
                ty_,
                TraitInfo {
                    glib_name,
                    version: func.version,
                    first_parameter_mut: true,
                },
            );
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy | Free | Ref | Unref => Visibility::Hidden,
        Hash | Compare | Equal => Visibility::Private,
        Display => Visibility::Public,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.traits().get(&type_) {
        let func = functions
            .iter_mut()
            .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment);
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    for (type_, info) in specials.traits() {
        use self::Type::*;
        match type_ {
            Copy if info.first_parameter_mut => {
                imports.add_with_version("glib::translate::*", info.version)
            }
            Compare => imports.add_with_version("std::cmp", info.version),
            Display => imports.add_with_version("std::fmt", info.version),
            Hash => imports.add_with_version("std::hash", info.version),
            _ => {}
        }
    }
    for info in specials.functions().values() {
        match info.type_ {
            FunctionType::StaticStringify => {
                imports.add_with_version("std::ffi::CStr", info.version)
            }
        }
    }
}
<commit_msg>Add `use glib::translate::*` for generating the equal special function<commit_after>use crate::{
    analysis::{
        functions::{Info as FuncInfo, Visibility},
        imports::Imports,
    },
    config::GObject,
    library::{Type as LibType, TypeId},
    version::Version,
};
use std::{collections::BTreeMap, str::FromStr};

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum Type {
    Compare,
    Copy,
    Equal,
    Free,
    Ref,
    Display,
    Unref,
    Hash,
}

impl FromStr for Type {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::Type::*;
        match s {
            "compare" => Ok(Compare),
            "copy" => Ok(Copy),
            "equal" => Ok(Equal),
            "free" | "destroy" => Ok(Free),
            "is_equal" => Ok(Equal),
            "ref" | "ref_" => Ok(Ref),
            "unref" => Ok(Unref),
            "hash" => Ok(Hash),
            _ => Err(format!("Unknown type '{}'", s)),
        }
    }
}

#[derive(Debug, Clone)]
pub struct TraitInfo {
    pub glib_name: String,
    pub version: Option<Version>,
    pub first_parameter_mut: bool,
}

type TraitInfos = BTreeMap<Type, TraitInfo>;

#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
pub enum FunctionType {
    StaticStringify,
}

#[derive(Debug, Clone)]
pub struct FunctionInfo {
    pub type_: FunctionType,
    pub version: Option<Version>,
}

type FunctionInfos = BTreeMap<String, FunctionInfo>;

#[derive(Debug, Default)]
pub struct Infos {
    traits: TraitInfos,
    functions: FunctionInfos,
}

impl Infos {
    pub fn traits(&self) -> &TraitInfos {
        &self.traits
    }

    pub fn traits_mut(&mut self) -> &mut TraitInfos {
        &mut self.traits
    }

    pub fn has_trait(&self, type_: Type) -> bool {
        self.traits.contains_key(&type_)
    }

    pub fn functions(&self) -> &FunctionInfos {
        &self.functions
    }
}

/// Returns true on functions that take an instance as single argument and
/// return a string as result.
fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool {
    if func.parameters.c_parameters.len() != 1 {
        return false;
    }
    if !func.parameters.c_parameters[0].instance_parameter {
        return false;
    }

    if let Some(ret) = func.ret.parameter.as_mut() {
        if ret.lib_par.typ != TypeId::tid_utf8() {
            return false;
        }

        if func.name == "to_string" {
            // Rename to to_str to make sure it doesn't clash with ToString::to_string
            assert!(func.new_name.is_none(), "A `to_string` function can't be renamed manually. It's automatically renamed to `to_str`");
            func.new_name = Some("to_str".to_owned());

            // As to not change old code behaviour, assume non-nullability outside
            // enums and flags only, and exclusively for to_string. Function inside
            // enums and flags have been appropriately marked in Gir.
            if !obj.trust_return_value_nullability
                && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
            {
                *ret.lib_par.nullable = false;
            }
        }

        // Cannot generate Display implementation for Option<>
        !*ret.lib_par.nullable
    } else {
        false
    }
}

fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
    if func.visibility != Visibility::Comment {
        func.visibility = visibility(type_);
    }
    true
}

pub fn extract(functions: &mut Vec<FuncInfo>, parent_type: &LibType, obj: &GObject) -> Infos {
    let mut specials = Infos::default();
    let mut has_copy = false;
    let mut has_free = false;
    let mut destroy = None;

    for (pos, func) in functions.iter_mut().enumerate() {
        if is_stringify(func, parent_type, obj) {
            let return_transfer_none = func.ret.parameter.as_ref().map_or(false, |ret| {
                ret.lib_par.transfer == crate::library::Transfer::None
            });

            // Assume only enumerations and bitfields can return static strings
            let returns_static_ref = return_transfer_none
                && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
                // We cannot mandate returned lifetime if this is not generated.
                // (And this prevents an unused std::ffi::CStr from being emitted below)
                && func.status.need_generate();

            if returns_static_ref {
                // Override the function with a &'static (non allocating) -returning string
                // if the transfer type is none and it matches the above heuristics.
                specials.functions.insert(
                    func.glib_name.clone(),
                    FunctionInfo {
                        type_: FunctionType::StaticStringify,
                        version: func.version,
                    },
                );
            }

            // Some stringifying functions can serve as Display implementation
            if matches!(
                func.name.as_str(),
                "to_string" | "to_str" | "name" | "get_name"
            ) {
                // FUTURE: Decide which function gets precedence if multiple Display prospects exist.
                specials.traits.insert(
                    Type::Display,
                    TraitInfo {
                        glib_name: func.glib_name.clone(),
                        version: func.version,
                        first_parameter_mut: false,
                    },
                );
            }
        } else if let Ok(type_) = func.name.parse() {
            if func.name == "destroy" {
                destroy = Some((func.glib_name.clone(), pos));
                continue;
            }
            if !update_func(func, type_) {
                continue;
            }
            if func.name == "copy" {
                has_copy = true;
            } else if func.name == "free" {
                has_free = true;
            }

            let first_parameter_mut = func
                .parameters
                .c_parameters
                .first()
                .map_or(false, |p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);

            specials.traits.insert(
                type_,
                TraitInfo {
                    glib_name: func.glib_name.clone(),
                    version: func.version,
                    first_parameter_mut,
                },
            );
        }
    }

    if has_copy && !has_free {
        if let Some((glib_name, pos)) = destroy {
            let ty_ = Type::from_str("destroy").unwrap();
            let func = &mut functions[pos];
            update_func(func, ty_);
            specials.traits.insert(
                ty_,
                TraitInfo {
                    glib_name,
                    version: func.version,
                    first_parameter_mut: true,
                },
            );
        }
    }

    specials
}

fn visibility(t: Type) -> Visibility {
    use self::Type::*;
    match t {
        Copy | Free | Ref | Unref => Visibility::Hidden,
        Hash | Compare | Equal => Visibility::Private,
        Display => Visibility::Public,
    }
}

// Some special functions (e.g. `copy` on refcounted types) should be exposed
pub fn unhide(functions: &mut Vec<FuncInfo>, specials: &Infos, type_: Type) {
    if let Some(func) = specials.traits().get(&type_) {
        let func = functions
            .iter_mut()
            .find(|f| f.glib_name == func.glib_name && f.visibility != Visibility::Comment);
        if let Some(func) = func {
            func.visibility = Visibility::Public;
        }
    }
}

pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
    for (type_, info) in specials.traits() {
        use self::Type::*;
        match type_ {
            Copy if info.first_parameter_mut => {
                imports.add_with_version("glib::translate::*", info.version)
            }
            Compare => imports.add_with_version("std::cmp", info.version),
            Display => imports.add_with_version("std::fmt", info.version),
            Hash => imports.add_with_version("std::hash", info.version),
            Equal => imports.add_with_version("glib::translate::*", info.version),
            _ => {}
        }
    }
    for info in specials.functions().values() {
        match info.type_ {
            FunctionType::StaticStringify => {
                imports.add_with_version("std::ffi::CStr", info.version)
            }
        }
    }
}
<|endoftext|>


<commit_before>use metrics::metric::{Metric, MetricValue};

// This can be much better with a different datatype
#[derive(Copy, Clone, Debug, Default)]
pub struct StdCounter {
    pub value: f64,
}

pub trait Counter {
    fn clear(&mut self);
    fn inc(&mut self);
    fn add(&mut self, value: f64);
    fn snapshot(self) -> Self;
}

impl Counter for StdCounter {
    fn clear(&mut self) {
        self.value = 0.0;
    }

    // inc(): Increment the counter by 1
    fn inc(&mut self) {
        self.value += 1.0;
    }

    // inc(double v): Increment the counter by the given amount. MUST check that v >= 0.
    // We crash with integer overflow
    fn add(&mut self, value: f64) {
        self.value += value;
    }

    fn snapshot(self) -> StdCounter {
        StdCounter { value: self.value }
    }
}

impl Metric for StdCounter {
    fn export_metric(&self) -> MetricValue {
        MetricValue::Counter(self.snapshot())
    }
}

impl StdCounter {
    pub fn new() -> StdCounter {
        StdCounter { value: 0.0 }
    }
}

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

    #[test]
    fn a_counting_counter() {
        let mut c: StdCounter = StdCounter::new();
        c.add(1.0);

        assert!(c.value == 1.0);

        let mut c: StdCounter = StdCounter::new();
        c.inc();

        assert!(c.value == 1.0);
    }

    #[test]
    fn make_sure_we_can_actually_export_metrics() {
        let c: StdCounter = StdCounter::new();
        let mut c_snapshot = c.snapshot();

        c_snapshot.add(1.0);

        assert!(c.value == 0.0);
        assert!(c_snapshot.value == 1.0);
    }
}
<commit_msg>Use assert_eq!(x, y) and not assert!(x == y).<commit_after>use metrics::metric::{Metric, MetricValue};

// This can be much better with a different datatype
#[derive(Copy, Clone, Debug, Default)]
pub struct StdCounter {
    pub value: f64,
}

pub trait Counter {
    fn clear(&mut self);
    fn inc(&mut self);
    fn add(&mut self, value: f64);
    fn snapshot(self) -> Self;
}

impl Counter for StdCounter {
    fn clear(&mut self) {
        self.value = 0.0;
    }

    // inc(): Increment the counter by 1
    fn inc(&mut self) {
        self.value += 1.0;
    }

    // inc(double v): Increment the counter by the given amount. MUST check that v >= 0.
    // We crash with integer overflow
    fn add(&mut self, value: f64) {
        self.value += value;
    }

    fn snapshot(self) -> StdCounter {
        StdCounter { value: self.value }
    }
}

impl Metric for StdCounter {
    fn export_metric(&self) -> MetricValue {
        MetricValue::Counter(self.snapshot())
    }
}

impl StdCounter {
    pub fn new() -> StdCounter {
        StdCounter { value: 0.0 }
    }
}

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

    #[test]
    fn a_counting_counter() {
        let mut c: StdCounter = StdCounter::new();
        c.add(1.0);

        assert_eq!(c.value, 1.0);

        let mut c: StdCounter = StdCounter::new();
        c.inc();

        assert_eq!(c.value, 1.0);
    }

    #[test]
    fn make_sure_we_can_actually_export_metrics() {
        let c: StdCounter = StdCounter::new();
        let mut c_snapshot = c.snapshot();

        c_snapshot.add(1.0);

        assert_eq!(c.value, 0.0);
        assert_eq!(c_snapshot.value, 1.0);
    }
}
<|endoftext|>


<commit_before>//! Utilities for parsing and formatting RFC 3339 timestamps.
//!
//! The [`Timestamp`] newtype wraps `chrono::DateTime<Utc>` or `time::OffsetDateTime` if the `time`
//! feature is enabled.
//!
//! # Formatting
//! ```
//! # use serenity::model::id::GuildId;
//! # use serenity::model::Timestamp;
//! #
//! let timestamp: Timestamp = GuildId(175928847299117063).created_at();
//! assert_eq!(timestamp.unix_timestamp(), 1462015105);
//! assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
//! ```
//!
//! # Parsing RFC 3339 string
//! ```
//! # use serenity::model::Timestamp;
//! #
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
//!
//! let timestamp: Timestamp = "2016-04-30T11:18:25Z".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25+00:00".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25.796Z".parse().unwrap();
//!
//! assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
//! assert!(Timestamp::parse("2016-04-30T11:18").is_err());
//! ```

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// Discord's epoch starts at "2015-01-01T00:00:00+00:00"
const DISCORD_EPOCH: u64 = 1_420_070_400_000;

cfg_if::cfg_if! {
    if #[cfg(all(feature = "chrono", not(feature = "time")))] {
        use chrono::{DateTime, NaiveDateTime, ParseError as InnerError, SecondsFormat, TimeZone, Utc};

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
        #[serde(transparent)]
        pub struct Timestamp(DateTime<Utc>);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                Self(Utc.timestamp_millis(((id >> 22) + DISCORD_EPOCH) as i64))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            pub fn now() -> Self {
                Self(Utc::now())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid.
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = NaiveDateTime::from_timestamp_opt(secs, 0).ok_or(InvalidTimestamp)?;
                Ok(Self(DateTime::from_utc(dt, Utc)))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            pub fn unix_timestamp(&self) -> i64 {
                self.0.timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                DateTime::parse_from_rfc3339(input).map(|d| Self(d.with_timezone(&Utc))).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.to_rfc3339_opts(SecondsFormat::Millis, true);
                f.write_str(&s)
            }
        }
    } else {
        use dep_time::format_description::well_known::Rfc3339;
        use dep_time::serde::rfc3339;
        use dep_time::{Duration, OffsetDateTime};
        use dep_time::error::Parse as InnerError;

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
        #[serde(transparent)]
        pub struct Timestamp(#[serde(with = "rfc3339")] OffsetDateTime);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                let ns = Duration::milliseconds(((id >> 22) + DISCORD_EPOCH) as i64).whole_nanoseconds();
                // This can't fail because of the bit shifting
                // `(u64::MAX >> 22) + DISCORD_EPOCH` = 5818116911103 = "Wed May 15 2154 07:35:11 GMT+0000"
                Self(OffsetDateTime::from_unix_timestamp_nanos(ns).expect("can't fail"))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            pub fn now() -> Self {
                Self(OffsetDateTime::now_utc())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// Returns `Err` if the value is invalid. The valid range of the value may vary depending on
            /// the feature flags enabled (`time` with `large-dates`).
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| InvalidTimestamp)?;
                Ok(Self(dt))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            pub fn unix_timestamp(&self) -> i64 {
                self.0.unix_timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                OffsetDateTime::parse(input, &Rfc3339).map(Self).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.format(&Rfc3339).map_err(|_| fmt::Error)?;
                f.write_str(&s)
            }
        }
    }
}

cfg_if::cfg_if! {
    if #[cfg(feature = "time")] {
        impl std::ops::Deref for Timestamp {
            type Target = OffsetDateTime;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl From<OffsetDateTime> for Timestamp {
            fn from(dt: OffsetDateTime) -> Self {
                Self(dt)
            }
        }
    } else if #[cfg(feature = "chrono")] {
        impl std::ops::Deref for Timestamp {
            type Target = DateTime<Utc>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<Tz: TimeZone> From<DateTime<Tz>> for Timestamp {
            fn from(dt: DateTime<Tz>) -> Self {
                Self(dt.with_timezone(&Utc))
            }
        }
    }
}

#[derive(Debug)]
pub struct InvalidTimestamp;

impl std::error::Error for InvalidTimestamp {}

impl fmt::Display for InvalidTimestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid UNIX timestamp value")
    }
}

/// Signifies the failure to parse the `Timestamp` from an RFC 3339 string.
#[derive(Debug)]
pub struct ParseError(InnerError);

impl std::error::Error for ParseError {}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for Timestamp {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Timestamp::parse(s)
    }
}

impl From<String> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: String) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(&s).unwrap()
    }
}

impl<'a> From<&'a str> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: &'a str) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(s).unwrap()
    }
}

impl From<&Timestamp> for Timestamp {
    fn from(ts: &Timestamp) -> Self {
        *ts
    }
}

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

    #[test]
    fn from_unix_timestamp() {
        let timestamp = Timestamp::from_unix_timestamp(1462015105).unwrap();
        assert_eq!(timestamp.unix_timestamp(), 1462015105);
        if cfg!(all(feature = "chrono", not(feature = "time"))) {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.000Z");
        } else {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25Z");
        }
    }
}
<commit_msg>Implement `Ord` and `PartialOrd` for `Timestamp` (#1882)<commit_after>//! Utilities for parsing and formatting RFC 3339 timestamps.
//!
//! The [`Timestamp`] newtype wraps `chrono::DateTime<Utc>` or `time::OffsetDateTime` if the `time`
//! feature is enabled.
//!
//! # Formatting
//! ```
//! # use serenity::model::id::GuildId;
//! # use serenity::model::Timestamp;
//! #
//! let timestamp: Timestamp = GuildId(175928847299117063).created_at();
//! assert_eq!(timestamp.unix_timestamp(), 1462015105);
//! assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
//! ```
//!
//! # Parsing RFC 3339 string
//! ```
//! # use serenity::model::Timestamp;
//! #
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
//! let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
//!
//! let timestamp: Timestamp = "2016-04-30T11:18:25Z".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25+00:00".parse().unwrap();
//! let timestamp: Timestamp = "2016-04-30T11:18:25.796Z".parse().unwrap();
//!
//! assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
//! assert!(Timestamp::parse("2016-04-30T11:18").is_err());
//! ```

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// Discord's epoch starts at "2015-01-01T00:00:00+00:00"
const DISCORD_EPOCH: u64 = 1_420_070_400_000;

cfg_if::cfg_if! {
    if #[cfg(all(feature = "chrono", not(feature = "time")))] {
        use chrono::{DateTime, NaiveDateTime, ParseError as InnerError, SecondsFormat, TimeZone, Utc};

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(DateTime<Utc>);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                Self(Utc.timestamp_millis(((id >> 22) + DISCORD_EPOCH) as i64))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            pub fn now() -> Self {
                Self(Utc::now())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// # Errors
            ///
            /// Returns `Err` if the value is invalid.
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = NaiveDateTime::from_timestamp_opt(secs, 0).ok_or(InvalidTimestamp)?;
                Ok(Self(DateTime::from_utc(dt, Utc)))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            pub fn unix_timestamp(&self) -> i64 {
                self.0.timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                DateTime::parse_from_rfc3339(input).map(|d| Self(d.with_timezone(&Utc))).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.to_rfc3339_opts(SecondsFormat::Millis, true);
                f.write_str(&s)
            }
        }
    } else {
        use dep_time::format_description::well_known::Rfc3339;
        use dep_time::serde::rfc3339;
        use dep_time::{Duration, OffsetDateTime};
        use dep_time::error::Parse as InnerError;

        /// Representation of a Unix timestamp.
        ///
        /// The struct implements the `std::fmt::Display` trait to format the underlying type as
        /// an RFC 3339 date and string such as `2016-04-30T11:18:25.796Z`.
        ///
        /// ```
        /// # use serenity::model::id::GuildId;
        /// # use serenity::model::Timestamp;
        /// #
        /// let timestamp: Timestamp = GuildId(175928847299117063).created_at();
        /// assert_eq!(timestamp.unix_timestamp(), 1462015105);
        /// assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.796Z");
        /// ```
        #[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize, Ord, PartialOrd)]
        #[serde(transparent)]
        pub struct Timestamp(#[serde(with = "rfc3339")] OffsetDateTime);

        impl Timestamp {
            pub(crate) fn from_discord_id(id: u64) -> Timestamp {
                let ns = Duration::milliseconds(((id >> 22) + DISCORD_EPOCH) as i64).whole_nanoseconds();
                // This can't fail because of the bit shifting
                // `(u64::MAX >> 22) + DISCORD_EPOCH` = 5818116911103 = "Wed May 15 2154 07:35:11 GMT+0000"
                Self(OffsetDateTime::from_unix_timestamp_nanos(ns).expect("can't fail"))
            }

            /// Create a new `Timestamp` with the current date and time in UTC.
            pub fn now() -> Self {
                Self(OffsetDateTime::now_utc())
            }

            /// Create a new `Timestamp` from a UNIX timestamp.
            ///
            /// Returns `Err` if the value is invalid. The valid range of the value may vary depending on
            /// the feature flags enabled (`time` with `large-dates`).
            pub fn from_unix_timestamp(secs: i64) -> Result<Self, InvalidTimestamp> {
                let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| InvalidTimestamp)?;
                Ok(Self(dt))
            }

            /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
            pub fn unix_timestamp(&self) -> i64 {
                self.0.unix_timestamp()
            }

            /// Parse a timestamp from an RFC 3339 date and time string.
            ///
            /// # Examples
            /// ```
            /// # use serenity::model::Timestamp;
            /// #
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25Z").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25+00:00").unwrap();
            /// let timestamp = Timestamp::parse("2016-04-30T11:18:25.796Z").unwrap();
            ///
            /// assert!(Timestamp::parse("2016-04-30T11:18:25").is_err());
            /// assert!(Timestamp::parse("2016-04-30T11:18").is_err());
            /// ```
            ///
            /// # Errors
            ///
            /// Returns `Err` if the string is not a valid RFC 3339 date and time string.
            pub fn parse(input: &str) -> Result<Timestamp, ParseError> {
                OffsetDateTime::parse(input, &Rfc3339).map(Self).map_err(ParseError)
            }
        }

        impl fmt::Display for Timestamp {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                let s = self.0.format(&Rfc3339).map_err(|_| fmt::Error)?;
                f.write_str(&s)
            }
        }
    }
}

cfg_if::cfg_if! {
    if #[cfg(feature = "time")] {
        impl std::ops::Deref for Timestamp {
            type Target = OffsetDateTime;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl From<OffsetDateTime> for Timestamp {
            fn from(dt: OffsetDateTime) -> Self {
                Self(dt)
            }
        }
    } else if #[cfg(feature = "chrono")] {
        impl std::ops::Deref for Timestamp {
            type Target = DateTime<Utc>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<Tz: TimeZone> From<DateTime<Tz>> for Timestamp {
            fn from(dt: DateTime<Tz>) -> Self {
                Self(dt.with_timezone(&Utc))
            }
        }
    }
}

#[derive(Debug)]
pub struct InvalidTimestamp;

impl std::error::Error for InvalidTimestamp {}

impl fmt::Display for InvalidTimestamp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid UNIX timestamp value")
    }
}

/// Signifies the failure to parse the `Timestamp` from an RFC 3339 string.
#[derive(Debug)]
pub struct ParseError(InnerError);

impl std::error::Error for ParseError {}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for Timestamp {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Timestamp::parse(s)
    }
}

impl From<String> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: String) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(&s).unwrap()
    }
}

impl<'a> From<&'a str> for Timestamp {
    /// Parses an RFC 3339 date and time string such as `2016-04-30T11:18:25.796Z`.
    ///
    /// Panics on invalid value.
    fn from(s: &'a str) -> Self {
        #[allow(clippy::unwrap_used)]
        Timestamp::parse(s).unwrap()
    }
}

impl From<&Timestamp> for Timestamp {
    fn from(ts: &Timestamp) -> Self {
        *ts
    }
}

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

    #[test]
    fn from_unix_timestamp() {
        let timestamp = Timestamp::from_unix_timestamp(1462015105).unwrap();
        assert_eq!(timestamp.unix_timestamp(), 1462015105);
        if cfg!(all(feature = "chrono", not(feature = "time"))) {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25.000Z");
        } else {
            assert_eq!(timestamp.to_string(), "2016-04-30T11:18:25Z");
        }
    }
}
<|endoftext|>


<commit_before>//! Utilities for managing and calling external binaries

use binary::{
    eyre::{bail, Result},
    Binary, BinaryInstallation, BinaryTrait,
};
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, HashMap};
use tokio::sync::RwLock;

///! A module for locating, running and installing third party binaries.
///!
///! Binaries may be used as runtimes for plugins (e.g. Node.js, Python) or
///! are used in created (e.g Pandoc by the `codec-pandoc` crate).

/// A global store of binaries
///
/// This is an immutable, lazily initialized list of "registered" binaries
/// that Stencila knows how to install, get the version for etc. However,
/// the functions below can be used for any other binary as well.
static BINARIES: Lazy<BTreeMap<String, Box<dyn BinaryTrait>>> = Lazy::new(|| {
    let mut map: BTreeMap<String, Box<dyn BinaryTrait>> = BTreeMap::new();

    #[cfg(feature = "binary-chrome")]
    {
        let bin = binary_chrome::ChromeBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-node")]
    {
        let bin = binary_node::NodeBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-pandoc")]
    {
        let bin = binary_pandoc::PandocBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-python")]
    {
        let bin = binary_python::PythonBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    map
});

/// A cache of installations used to memoize calls to `installation`.
static INSTALLATIONS: Lazy<RwLock<HashMap<String, BinaryInstallation>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Get an installation
///
/// This is a relatively expensive function, even if the binary is already installed,
/// because it searches the file system and executes commands to get their version.
/// Therefore, this function memoizes installations in `INSTALLATIONS` for each `name` and `semver`.
/// Each cached result is removed if the binary is installed or uninstalled.
pub async fn installation(name: &str, semver: &str) -> Result<BinaryInstallation> {
    let name_semver = [name, "@", semver].concat();

    if let Some(installation) = INSTALLATIONS.read().await.get(&name_semver) {
        return Ok(installation.clone());
    }

    let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(name, &[], &[]));
    let binary = BINARIES.get(name).unwrap_or(&unregistered);

    let semver = if semver == "*" {
        None
    } else {
        Some(semver.into())
    };

    if let Some(installation) = binary.installed(semver)? {
        INSTALLATIONS
            .write()
            .await
            .insert(name_semver, installation.clone());
        Ok(installation)
    } else {
        bail!("No matching installation found")
    }
}

/// Is a binary installation meeting semantic versioning requirements installed?
pub async fn installed(name: &str, semver: &str) -> bool {
    installation(name, semver).await.is_ok()
}

/// Get a binary installation meeting semantic versioning requirements.
///
/// If the binary is already available, or automatic installs are configured, returns
/// a `BinaryInstallation` that can be used to run commands. Otherwise, errors
/// with a message that the required binary is not yet installed, or failed to install.
pub async fn require(name: &str, semver: &str) -> Result<BinaryInstallation> {
    if let Ok(installation) = installation(name, semver).await {
        return Ok(installation);
    }

    // TODO: Use an env var to set this?
    let auto = true;
    if auto {
        let name_semver = [name, "@", semver].concat();
        let semver = if semver == "*" {
            None
        } else {
            Some(semver.into())
        };

        let binary = match BINARIES.get(name) {
            Some(binary) => binary,
            None => bail!("Unable to automatically install binary `{}`", name),
        };
        binary.install(semver.clone(), None, None).await?;

        if let Some(installation) = binary.installed(semver)? {
            let mut installations = INSTALLATIONS.write().await;
            installations.insert(name_semver, installation.clone());
            Ok(installation)
        } else {
            bail!("Failed to automatically install binary `{}`", name)
        }
    } else {
        bail!("Required binary `{}` is not installed", name)
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::structopt::StructOpt;
    use cli_utils::{async_trait::async_trait, result, Result, Run};

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

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Show(Show),
        Installable(Installable),
        Install(Install),
        Uninstall(Uninstall),
        Run(Run_),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::Show(action) => action.run().await,
                Action::Installable(action) => action.run().await,
                Action::Install(action) => action.run().await,
                Action::Uninstall(action) => action.run().await,
                Action::Run(action) => action.run().await,
            }
        }
    }

    /// Show information on a binary
    ///
    /// Searches for the binary on your path and in Stencila's "binaries"
    /// folder for versions that are installed. Use the `semver` argument
    /// to show the latest version that meets the semantic version requirement.
    ///
    /// This command should find any binary that is on your PATH
    /// (i.e. including those not in the `stencila binaries installable` list).
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The name of the binary e.g. pandoc
        pub name: String,

        /// The semantic version requirement for the binary e.g. >2
        ///
        /// If this is supplied and the binary does not meet the semver
        /// requirement nothing will be shown
        pub semver: Option<String>,
    }

    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            // Try to get registered binary (because has potential aliases and extracting versions) but fall
            // back to unregistered for others
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(&self.name, &[], &[]));
            let binary = BINARIES.get(&self.name).unwrap_or(&unregistered);
            if self.semver.is_some() {
                if let Ok(installation) = binary.installed(self.semver.clone()) {
                    result::value(installation)
                } else {
                    tracing::info!(
                        "No matching binary found. Perhaps use `stencila binaries install`?"
                    );
                    result::nothing()
                }
            } else {
                let installations = binary.installations();
                result::value(installations)
            }
        }
    }

    /// List binaries (and their versions) that can be installed using Stencila
    ///
    /// The returned list is a list of the binaries/versions that Stencila knows how to install.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Installable {}

    #[async_trait]
    impl Run for Installable {
        async fn run(&self) -> Result {
            let list: Vec<serde_json::Value> = BINARIES
                .values()
                .map(|binary| {
                    let spec = binary.spec();
                    serde_json::json!({
                        "name": spec.name,
                        "versions": spec.installable
                    })
                })
                .collect();
            result::value(list)
        }
    }

    /// Install a binary
    ///
    /// Installs the latest version of the binary, that also meets any
    /// semantic version requirement supplied, into the Stencila "binaries"
    /// folder.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Install {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The semantic version requirement (the most recent matching version will be installed)
        pub semver: Option<String>,

        /// The operating system to install for (defaults to the current)
        #[structopt(short, long, possible_values = &OS_VALUES )]
        pub os: Option<String>,

        /// The architecture to install for (defaults to the current)
        #[structopt(short, long, possible_values = &ARCH_VALUES)]
        pub arch: Option<String>,
    }

    const OS_VALUES: [&str; 3] = ["macos", "windows", "linux"];
    const ARCH_VALUES: [&str; 3] = ["x86", "x86_64", "arm"];

    #[async_trait]
    impl Run for Install {
        async fn run(&self) -> Result {
            match BINARIES.get(&self.name) {
                Some(binary) => {
                    binary
                        .install(self.semver.clone(), self.os.clone(), self.arch.clone())
                        .await?;
                    tracing::info!("📦 Installed {}", self.name);
                }
                None => {
                    tracing::error!("Stencila is unable to install `{}`", self.name);
                }
            }
            result::nothing()
        }
    }

    /// Uninstall a binary
    ///
    /// Removes the binary (optionally, just a specific version) from the Stencila
    /// "binaries" folder. No other installations of the binary on the system will
    /// will be removed.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Uninstall {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The specific version of the binary to uninstall
        ///
        /// If this is not provided, all versions will be removed.
        pub version: Option<String>,
    }
    #[async_trait]
    impl Run for Uninstall {
        async fn run(&self) -> Result {
            // Fallback to unregistered since that is sufficient for uninstall
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(&self.name, &[], &[]));
            let binary = BINARIES.get(&self.name).unwrap_or(&unregistered);
            binary.uninstall(self.version.clone()).await?;

            tracing::info!("🗑 Uninstalled {}", self.name);
            result::nothing()
        }
    }

    /// Run a command using a binary
    ///
    /// Pass arguments and options to the binary after the `--` flag.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Run_ {
        /// The name of the binary e.g. node
        pub name: String,

        /// The semantic version requirement e.g. 16
        pub semver: Option<String>,

        /// The arguments and options to pass to the binary
        #[structopt(raw(true))]
        pub args: Vec<String>,
    }

    #[async_trait]
    impl Run for Run_ {
        async fn run(&self) -> Result {
            let installation = require(
                &self.name,
                &self.semver.clone().unwrap_or_else(|| "*".to_string()),
            )
            .await?;
            let output = installation.run(&self.args).await?;

            use std::io::Write;
            std::io::stdout().write_all(output.stdout.as_ref())?;
            std::io::stderr().write_all(output.stderr.as_ref())?;

            result::nothing()
        }
    }
}

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

    // End to end CLI test that install, show and uninstall
    // the latest version of each binary. Intended as a coarse
    // tests of installation of each binary. These tests are
    // tagged with #[ignore] because they are slow, so in development
    // you don't want to run them, and because if they are run in
    // parallel with other tests that use `require()` they can cause deadlocks
    // and other on-disk conflicts.

    // Run this test at the start of CI tests using
    //   cargo test binaries::tests::install -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    //#[ignore]
    async fn install() -> Result<()> {
        use super::commands::{Install, Installable, Show};
        use cli_utils::Run;
        use test_utils::assert_json_eq;

        Installable {}.run().await?;

        for name in BINARIES.keys() {
            eprintln!("Testing {}", name);

            Install {
                name: name.clone(),
                semver: None,
                os: None,
                arch: None,
            }
            .run()
            .await?;

            let display = Show {
                name: name.clone(),
                semver: None,
            }
            .run()
            .await?;

            let value = if let Some(value) = display.value {
                value
            } else {
                bail!("Expected value")
            };
            assert_json_eq!(value.get("name"), Some(name.clone()));
            assert!(!value
                .get("installs")
                .expect("To have installs")
                .as_array()
                .expect("To be array")
                .is_empty());
        }

        Ok(())
    }

    // Run this test at the end of CI tests using
    //   cargo test binaries::tests::uninstall -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    //#[ignore]
    async fn uninstall() -> Result<()> {
        use super::commands::Uninstall;
        use cli_utils::Run;

        for name in BINARIES.keys() {
            Uninstall {
                name: name.clone(),
                version: None,
            }
            .run()
            .await?;
        }

        Ok(())
    }
}
<commit_msg>test(Binaries): Ignore tests<commit_after>//! Utilities for managing and calling external binaries

use binary::{
    eyre::{bail, Result},
    Binary, BinaryInstallation, BinaryTrait,
};
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, HashMap};
use tokio::sync::RwLock;

///! A module for locating, running and installing third party binaries.
///!
///! Binaries may be used as runtimes for plugins (e.g. Node.js, Python) or
///! are used in created (e.g Pandoc by the `codec-pandoc` crate).

/// A global store of binaries
///
/// This is an immutable, lazily initialized list of "registered" binaries
/// that Stencila knows how to install, get the version for etc. However,
/// the functions below can be used for any other binary as well.
static BINARIES: Lazy<BTreeMap<String, Box<dyn BinaryTrait>>> = Lazy::new(|| {
    let mut map: BTreeMap<String, Box<dyn BinaryTrait>> = BTreeMap::new();

    #[cfg(feature = "binary-chrome")]
    {
        let bin = binary_chrome::ChromeBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-node")]
    {
        let bin = binary_node::NodeBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-pandoc")]
    {
        let bin = binary_pandoc::PandocBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    #[cfg(feature = "binary-python")]
    {
        let bin = binary_python::PythonBinary {};
        map.insert(bin.spec().name, Box::new(bin));
    }

    map
});

/// A cache of installations used to memoize calls to `installation`.
static INSTALLATIONS: Lazy<RwLock<HashMap<String, BinaryInstallation>>> =
    Lazy::new(|| RwLock::new(HashMap::new()));

/// Get an installation
///
/// This is a relatively expensive function, even if the binary is already installed,
/// because it searches the file system and executes commands to get their version.
/// Therefore, this function memoizes installations in `INSTALLATIONS` for each `name` and `semver`.
/// Each cached result is removed if the binary is installed or uninstalled.
pub async fn installation(name: &str, semver: &str) -> Result<BinaryInstallation> {
    let name_semver = [name, "@", semver].concat();

    if let Some(installation) = INSTALLATIONS.read().await.get(&name_semver) {
        return Ok(installation.clone());
    }

    let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(name, &[], &[]));
    let binary = BINARIES.get(name).unwrap_or(&unregistered);

    let semver = if semver == "*" {
        None
    } else {
        Some(semver.into())
    };

    if let Some(installation) = binary.installed(semver)? {
        INSTALLATIONS
            .write()
            .await
            .insert(name_semver, installation.clone());
        Ok(installation)
    } else {
        bail!("No matching installation found")
    }
}

/// Is a binary installation meeting semantic versioning requirements installed?
pub async fn installed(name: &str, semver: &str) -> bool {
    installation(name, semver).await.is_ok()
}

/// Get a binary installation meeting semantic versioning requirements.
///
/// If the binary is already available, or automatic installs are configured, returns
/// a `BinaryInstallation` that can be used to run commands. Otherwise, errors
/// with a message that the required binary is not yet installed, or failed to install.
pub async fn require(name: &str, semver: &str) -> Result<BinaryInstallation> {
    if let Ok(installation) = installation(name, semver).await {
        return Ok(installation);
    }

    // TODO: Use an env var to set this?
    let auto = true;
    if auto {
        let name_semver = [name, "@", semver].concat();
        let semver = if semver == "*" {
            None
        } else {
            Some(semver.into())
        };

        let binary = match BINARIES.get(name) {
            Some(binary) => binary,
            None => bail!("Unable to automatically install binary `{}`", name),
        };
        binary.install(semver.clone(), None, None).await?;

        if let Some(installation) = binary.installed(semver)? {
            let mut installations = INSTALLATIONS.write().await;
            installations.insert(name_semver, installation.clone());
            Ok(installation)
        } else {
            bail!("Failed to automatically install binary `{}`", name)
        }
    } else {
        bail!("Required binary `{}` is not installed", name)
    }
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use cli_utils::structopt::StructOpt;
    use cli_utils::{async_trait::async_trait, result, Result, Run};

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

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Show(Show),
        Installable(Installable),
        Install(Install),
        Uninstall(Uninstall),
        Run(Run_),
    }

    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::Show(action) => action.run().await,
                Action::Installable(action) => action.run().await,
                Action::Install(action) => action.run().await,
                Action::Uninstall(action) => action.run().await,
                Action::Run(action) => action.run().await,
            }
        }
    }

    /// Show information on a binary
    ///
    /// Searches for the binary on your path and in Stencila's "binaries"
    /// folder for versions that are installed. Use the `semver` argument
    /// to show the latest version that meets the semantic version requirement.
    ///
    /// This command should find any binary that is on your PATH
    /// (i.e. including those not in the `stencila binaries installable` list).
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Show {
        /// The name of the binary e.g. pandoc
        pub name: String,

        /// The semantic version requirement for the binary e.g. >2
        ///
        /// If this is supplied and the binary does not meet the semver
        /// requirement nothing will be shown
        pub semver: Option<String>,
    }

    #[async_trait]
    impl Run for Show {
        async fn run(&self) -> Result {
            // Try to get registered binary (because has potential aliases and extracting versions) but fall
            // back to unregistered for others
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(&self.name, &[], &[]));
            let binary = BINARIES.get(&self.name).unwrap_or(&unregistered);
            if self.semver.is_some() {
                if let Ok(installation) = binary.installed(self.semver.clone()) {
                    result::value(installation)
                } else {
                    tracing::info!(
                        "No matching binary found. Perhaps use `stencila binaries install`?"
                    );
                    result::nothing()
                }
            } else {
                let installations = binary.installations();
                result::value(installations)
            }
        }
    }

    /// List binaries (and their versions) that can be installed using Stencila
    ///
    /// The returned list is a list of the binaries/versions that Stencila knows how to install.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Installable {}

    #[async_trait]
    impl Run for Installable {
        async fn run(&self) -> Result {
            let list: Vec<serde_json::Value> = BINARIES
                .values()
                .map(|binary| {
                    let spec = binary.spec();
                    serde_json::json!({
                        "name": spec.name,
                        "versions": spec.installable
                    })
                })
                .collect();
            result::value(list)
        }
    }

    /// Install a binary
    ///
    /// Installs the latest version of the binary, that also meets any
    /// semantic version requirement supplied, into the Stencila "binaries"
    /// folder.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Install {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The semantic version requirement (the most recent matching version will be installed)
        pub semver: Option<String>,

        /// The operating system to install for (defaults to the current)
        #[structopt(short, long, possible_values = &OS_VALUES )]
        pub os: Option<String>,

        /// The architecture to install for (defaults to the current)
        #[structopt(short, long, possible_values = &ARCH_VALUES)]
        pub arch: Option<String>,
    }

    const OS_VALUES: [&str; 3] = ["macos", "windows", "linux"];
    const ARCH_VALUES: [&str; 3] = ["x86", "x86_64", "arm"];

    #[async_trait]
    impl Run for Install {
        async fn run(&self) -> Result {
            match BINARIES.get(&self.name) {
                Some(binary) => {
                    binary
                        .install(self.semver.clone(), self.os.clone(), self.arch.clone())
                        .await?;
                    tracing::info!("📦 Installed {}", self.name);
                }
                None => {
                    tracing::error!("Stencila is unable to install `{}`", self.name);
                }
            }
            result::nothing()
        }
    }

    /// Uninstall a binary
    ///
    /// Removes the binary (optionally, just a specific version) from the Stencila
    /// "binaries" folder. No other installations of the binary on the system will
    /// will be removed.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Uninstall {
        /// The name of the binary (must be a registered binary name)
        pub name: String,

        /// The specific version of the binary to uninstall
        ///
        /// If this is not provided, all versions will be removed.
        pub version: Option<String>,
    }
    #[async_trait]
    impl Run for Uninstall {
        async fn run(&self) -> Result {
            // Fallback to unregistered since that is sufficient for uninstall
            let unregistered: Box<dyn BinaryTrait> = Box::new(Binary::new(&self.name, &[], &[]));
            let binary = BINARIES.get(&self.name).unwrap_or(&unregistered);
            binary.uninstall(self.version.clone()).await?;

            tracing::info!("🗑 Uninstalled {}", self.name);
            result::nothing()
        }
    }

    /// Run a command using a binary
    ///
    /// Pass arguments and options to the binary after the `--` flag.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Run_ {
        /// The name of the binary e.g. node
        pub name: String,

        /// The semantic version requirement e.g. 16
        pub semver: Option<String>,

        /// The arguments and options to pass to the binary
        #[structopt(raw(true))]
        pub args: Vec<String>,
    }

    #[async_trait]
    impl Run for Run_ {
        async fn run(&self) -> Result {
            let installation = require(
                &self.name,
                &self.semver.clone().unwrap_or_else(|| "*".to_string()),
            )
            .await?;
            let output = installation.run(&self.args).await?;

            use std::io::Write;
            std::io::stdout().write_all(output.stdout.as_ref())?;
            std::io::stderr().write_all(output.stderr.as_ref())?;

            result::nothing()
        }
    }
}

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

    // End to end CLI test that install, show and uninstall
    // the latest version of each binary. Intended as a coarse
    // tests of installation of each binary. These tests are
    // tagged with #[ignore] because they are slow, so in development
    // you don't want to run them, and because if they are run in
    // parallel with other tests that use `require()` they can cause deadlocks
    // and other on-disk conflicts.

    // Run this test at the start of CI tests using
    //   cargo test binaries::tests::install -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn install() -> Result<()> {
        use super::commands::{Install, Installable, Show};
        use cli_utils::Run;
        use test_utils::assert_json_eq;

        Installable {}.run().await?;

        for name in BINARIES.keys() {
            eprintln!("Testing {}", name);

            Install {
                name: name.clone(),
                semver: None,
                os: None,
                arch: None,
            }
            .run()
            .await?;

            let display = Show {
                name: name.clone(),
                semver: None,
            }
            .run()
            .await?;

            let value = if let Some(value) = display.value {
                value
            } else {
                bail!("Expected value")
            };
            assert_json_eq!(value.get("name"), Some(name.clone()));
            assert!(!value
                .get("installs")
                .expect("To have installs")
                .as_array()
                .expect("To be array")
                .is_empty());
        }

        Ok(())
    }

    // Run this test at the end of CI tests using
    //   cargo test binaries::tests::uninstall -- --ignored --nocapture
    #[cfg(feature = "cli")]
    #[tokio::test]
    #[ignore]
    async fn uninstall() -> Result<()> {
        use super::commands::Uninstall;
        use cli_utils::Run;

        for name in BINARIES.keys() {
            Uninstall {
                name: name.clone(),
                version: None,
            }
            .run()
            .await?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

use parsers::ParseError;

/// Defines the units to be used for scaling paint servers, per the [svg specification].
///
/// [svg spec]: https://www.w3.org/TR/SVG/pservers.html
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PaintServerUnits {
    UserSpaceOnUse,
    ObjectBoundingBox
}

impl FromStr for PaintServerUnits {
    type Err = ParseError;

    fn from_str (s: &str) -> Result<PaintServerUnits, ParseError> {
        match s {
            "userSpaceOnUse"    => Ok (PaintServerUnits::UserSpaceOnUse),
            "objectBoundingBox" => Ok (PaintServerUnits::ObjectBoundingBox),
            _                   => Err (ParseError::new ("expected 'userSpaceOnUse' or 'objectBoundingBox'"))
        }
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error () {
        assert! (PaintServerUnits::from_str ("").is_err ());
        assert! (PaintServerUnits::from_str ("foo").is_err ());
    }

    #[test]
    fn parses_paint_server_units () {
        assert_eq! (PaintServerUnits::from_str ("userSpaceOnUse"), Ok (PaintServerUnits::UserSpaceOnUse));
        assert_eq! (PaintServerUnits::from_str ("objectBoundingBox"), Ok (PaintServerUnits::ObjectBoundingBox));
    }
}
<commit_msg>Implement Default for PaintServerUnits, to yield ObjectBoundingBox<commit_after>use std::str::FromStr;

use parsers::ParseError;

/// Defines the units to be used for scaling paint servers, per the [svg specification].
///
/// [svg spec]: https://www.w3.org/TR/SVG/pservers.html
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PaintServerUnits {
    UserSpaceOnUse,
    ObjectBoundingBox
}

impl FromStr for PaintServerUnits {
    type Err = ParseError;

    fn from_str (s: &str) -> Result<PaintServerUnits, ParseError> {
        match s {
            "userSpaceOnUse"    => Ok (PaintServerUnits::UserSpaceOnUse),
            "objectBoundingBox" => Ok (PaintServerUnits::ObjectBoundingBox),
            _                   => Err (ParseError::new ("expected 'userSpaceOnUse' or 'objectBoundingBox'"))
        }
    }
}

impl Default for PaintServerUnits {
    fn default () -> PaintServerUnits {
        PaintServerUnits::ObjectBoundingBox
    }
}

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

    #[test]
    fn parsing_invalid_strings_yields_error () {
        assert! (PaintServerUnits::from_str ("").is_err ());
        assert! (PaintServerUnits::from_str ("foo").is_err ());
    }

    #[test]
    fn parses_paint_server_units () {
        assert_eq! (PaintServerUnits::from_str ("userSpaceOnUse"), Ok (PaintServerUnits::UserSpaceOnUse));
        assert_eq! (PaintServerUnits::from_str ("objectBoundingBox"), Ok (PaintServerUnits::ObjectBoundingBox));
    }
}
<|endoftext|>