<commit_before>//! How to use value hints and generate shell completions.
//!
//! Usage with zsh:
//! ```sh
//! cargo run --example value_hints_derive -- --generate=zsh > /usr/local/share/zsh/site-functions/_value_hints_derive
//! compinit
//! ./target/debug/examples/value_hints_derive --<TAB>
//! ```
//! fish:
//! ```sh
//! cargo run --example value_hints_derive -- --generate=fish > value_hints_derive.fish
//! . ./value_hints_derive.fish
//! ./target/debug/examples/value_hints_derive --<TAB>
//! ```
use clap::{Command, CommandFactory, Parser, ValueHint};
use clap_complete::{generate, Generator, Shell};
use std::ffi::OsString;
use std::io;
use std::path::PathBuf;

#[derive(Parser, Debug, PartialEq)]
#[command(name = "completion-derive")]
struct Opt {
    /// If provided, outputs the completion file for given shell
    #[arg(long = "generate", value_enum)]
    generator: Option<Shell>,
    // Showcasing all possible ValueHints:
    #[arg(long, value_hint = ValueHint::Unknown)]
    unknown: Option<String>,
    #[arg(long, value_hint = ValueHint::Other)]
    other: Option<String>,
    #[arg(short, long, value_hint = ValueHint::AnyPath)]
    path: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::FilePath)]
    file: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::DirPath)]
    dir: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::ExecutablePath)]
    exe: Option<PathBuf>,
    #[arg(long, value_hint = ValueHint::CommandName)]
    cmd_name: Option<OsString>,
    #[arg(short, long, value_hint = ValueHint::CommandString)]
    cmd: Option<String>,
    // Command::trailing_var_ar is required to use ValueHint::CommandWithArguments
    #[arg(trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
    command_with_args: Vec<String>,
    #[arg(short, long, value_hint = ValueHint::Username)]
    user: Option<String>,
    #[arg(long, value_hint = ValueHint::Hostname)]
    host: Option<String>,
    #[arg(long, value_hint = ValueHint::Url)]
    url: Option<String>,
    #[arg(long, value_hint = ValueHint::EmailAddress)]
    email: Option<String>,
}

fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}

fn main() {
    let opt = Opt::parse();

    if let Some(generator) = opt.generator {
        let mut cmd = Opt::command();
        eprintln!("Generating completion file for {:?}...", generator);
        print_completions(generator, &mut cmd);
    } else {
        println!("{:#?}", opt);
    }
}
<commit_msg>fix(complete): Make the two examples consistent<commit_after>//! How to use value hints and generate shell completions.
//!
//! Usage with zsh:
//! ```sh
//! cargo run --example value_hints_derive -- --generate=zsh > /usr/local/share/zsh/site-functions/_value_hints_derive
//! compinit
//! ./target/debug/examples/value_hints_derive --<TAB>
//! ```
//! fish:
//! ```sh
//! cargo run --example value_hints_derive -- --generate=fish > value_hints_derive.fish
//! . ./value_hints_derive.fish
//! ./target/debug/examples/value_hints_derive --<TAB>
//! ```
use clap::{Command, CommandFactory, Parser, ValueHint};
use clap_complete::{generate, Generator, Shell};
use std::ffi::OsString;
use std::io;
use std::path::PathBuf;

#[derive(Parser, Debug, PartialEq)]
#[command(name = "completion-derive")]
struct Opt {
    // If provided, outputs the completion file for given shell
    #[arg(long = "generate", value_enum)]
    generator: Option<Shell>,
    // Showcasing all possible ValueHints:
    #[arg(long, value_hint = ValueHint::Unknown)]
    unknown: Option<String>,
    #[arg(long, value_hint = ValueHint::Other)]
    other: Option<String>,
    #[arg(short, long, value_hint = ValueHint::AnyPath)]
    path: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::FilePath)]
    file: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::DirPath)]
    dir: Option<PathBuf>,
    #[arg(short, long, value_hint = ValueHint::ExecutablePath)]
    exe: Option<PathBuf>,
    #[arg(long, value_hint = ValueHint::CommandName)]
    cmd_name: Option<OsString>,
    #[arg(short, long, value_hint = ValueHint::CommandString)]
    cmd: Option<String>,
    // Command::trailing_var_ar is required to use ValueHint::CommandWithArguments
    #[arg(trailing_var_arg = true, value_hint = ValueHint::CommandWithArguments)]
    command_with_args: Vec<String>,
    #[arg(short, long, value_hint = ValueHint::Username)]
    user: Option<String>,
    #[arg(long, value_hint = ValueHint::Hostname)]
    host: Option<String>,
    #[arg(long, value_hint = ValueHint::Url)]
    url: Option<String>,
    #[arg(long, value_hint = ValueHint::EmailAddress)]
    email: Option<String>,
}

fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}

fn main() {
    let opt = Opt::parse();

    if let Some(generator) = opt.generator {
        let mut cmd = Opt::command();
        eprintln!("Generating completion file for {:?}...", generator);
        print_completions(generator, &mut cmd);
    } else {
        println!("{:#?}", opt);
    }
}
<|endoftext|>


<commit_before>#[link(name="dirname", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use extra::getopts::groups;

static VERSION: &'static str = "1.0.0";

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("z", "zero", "separate output with NUL rather than newline"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("dirname " + VERSION + " - strip last component from file name");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION] NAME...", program);
        println("");
        print(groups::usage("Output each NAME with its last non-slash component and trailing slashes\
removed; if NAME contains no  /'s,  output  '.'  (meaning  the  current\
directory).", opts));
        return;
    }

    if matches.opt_present("version") {
        return println("dirname version: " + VERSION);
    }

    let separator = match matches.opt_present("zero") {
        true => "\0",
        false => "\n"
    };

    if !matches.free.is_empty() {
        for path in matches.free.iter() {
            let p = std::path::Path::init(path.clone());
            print(std::str::from_utf8_slice(p.dirname()));
            print(separator);
        }
    } else {
        println!("{0:s}: missing operand", program);
        println!("Try '{0:s} --help' for more information.", program);
    }
}
<commit_msg>Remove unnecessary escaping<commit_after>#[link(name="dirname", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use extra::getopts::groups;

static VERSION: &'static str = "1.0.0";

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("z", "zero", "separate output with NUL rather than newline"),
        groups::optflag("", "help", "display this help and exit"),
        groups::optflag("", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("dirname " + VERSION + " - strip last component from file name");
        println("");
        println("Usage:");
        println!("  {0:s} [OPTION] NAME...", program);
        println("");
        print(groups::usage("Output each NAME with its last non-slash component and trailing slashes
removed; if NAME contains no  /'s,  output  '.'  (meaning  the  current
directory).", opts));
        return;
    }

    if matches.opt_present("version") {
        return println("dirname version: " + VERSION);
    }

    let separator = match matches.opt_present("zero") {
        true => "\0",
        false => "\n"
    };

    if !matches.free.is_empty() {
        for path in matches.free.iter() {
            let p = std::path::Path::init(path.clone());
            print(std::str::from_utf8_slice(p.dirname()));
            print(separator);
        }
    } else {
        println!("{0:s}: missing operand", program);
        println!("Try '{0:s} --help' for more information.", program);
    }
}
<|endoftext|>


<commit_before>// vim: tw=80
/// Generate Expectation and Expectations types with specific signatures
///
/// # Arguments
///
/// * `expectation`:    Name of the generated Expectation type
/// * `module`:         Name of the private module to create
/// * `o`:              Output type.  Must be static
/// * `methty`:         Comma-delimited sequence of arguments types for the
///                     method being mocked.
/// * `matchcall`:      Comma-delimited sequence of expressions that produce
///                     values of type `matchty` from values of type `methty`.
/// * `args`:           comma-delimited sequence of argument names for each
///                     argument.  Ideally this wouldn't need to be specified,
///                     but it is due to Rust's macro hygiene rules.
/// * `predargs`:       Comma-delimited sequence of identifiers of the same
///                     length as `args`, but distinct.
/// * `matchty`:        comma-delimited sequence of types for each match
///                     argument.  Must all be `'static`.
///
/// # Examples
///
/// ```
/// // Mock a method like foo(&self, x: u32, y: &i16) -> u32
/// omnimock!{(u32, &i16), u32, i, [&i.0, i.1], [i0, i1], [u32, i16]}
#[macro_export]
macro_rules! omnimock {(
        $expectation:ident,
        $module:ident,
        $o:ty,
        [ $( $methty:ty ),* ],
        [ $( $matchcall:expr ),* ],
        [ $( $args:ident ),* ],
        [ $( $predargs:ident ),* ],
        [ $( $matchty:ty ),* ]) =>
    {
        mod $module {
            use super::*;

        enum Rfunc {
            Default,
            // Indicates that a `return_once` expectation has already returned
            Expired,
            Mut(Box<dyn FnMut($( $methty, )*) -> $o + Send>),
            // Should be Box<dyn FnOnce> once that feature is stabilized
            // https://github.com/rust-lang/rust/issues/28796
            Once(Box<dyn FnMut($( $methty, )*) -> $o + Send>),
        }

        impl  Rfunc {
            fn call_mut(&mut self, $( $args: $methty, )* ) -> $o {
                match self {
                    Rfunc::Default => {
                        unimplemented!()
                        //Self::return_default()
                    },
                    Rfunc::Expired => {
                        panic!("Called a method twice that was expected only once")
                    },
                    Rfunc::Mut(f) => {
                        f( $( $args, )* )
                    },
                    Rfunc::Once(_) => {
                        let fo = ::std::mem::replace(self, Rfunc::Expired);
                        if let Rfunc::Once(mut f) = fo {
                            f( $( $args, )* )
                        } else {
                            unreachable!()
                        }
                    },
                }
            }
        }

        impl std::default::Default for Rfunc {
            fn default() -> Self {
                Rfunc::Default
            }
        }

        enum Matcher {
            Func(Box<Fn($( &$matchty, )* ) -> bool>),
            Pred( $( Box<::mockall::Predicate<$matchty>>, )* )
        }

        impl Matcher {
            fn eval(&self, $( $args: &$matchty, )*) -> bool {
                match self {
                    Matcher::Func(f) => f($( $args, )*),
                    Matcher::Pred($( $predargs, )*) =>
                        [$( $predargs.eval($args), )*]
                        .into_iter()
                        .all(|x| *x),
                }
            }

            fn verify(&self, $( $args: &$matchty, )* ) {
                use predicates_tree::CaseTreeExt;
                match self {
                    Matcher::Func(f) => assert!(f($( $args, )*),
                        "Expectation didn't match arguments"),
                    Matcher::Pred($( $predargs, )*) => {
                        $(if let Some(c) = $predargs.find_case(false, $args) {
                            panic!("Expectation didn't match arguments:\n{}",
                                   c.tree());
                        })*
                    }
                }
            }
        }

        impl Default for Matcher {
            #[allow(unused_variables)]
            fn default() -> Self {
                Matcher::Func(Box::new(|$( $args, )*| true))
            }
        }

        #[derive(Default)]
        struct Common {
            matcher: ::std::sync::Mutex<Matcher>,
            seq_handle: Option<::mockall::SeqHandle>,
            times: ::mockall::Times
        }

        impl Common {
            fn call(&self, $( $args: &$matchty, )* ) {
                self.matcher.lock().unwrap().verify($( $args, )*);
                self.times.call();
                self.verify_sequence();
                if self.times.is_satisfied() {
                    self.satisfy_sequence()
                }
            }

            fn in_sequence(&mut self, seq: &mut ::mockall::Sequence)
                -> &mut Self
            {
                assert!(self.times.is_exact(),
                    "Only Expectations with an exact call count have sequences");
                self.seq_handle = Some(seq.next());
                self
            }

            /// Forbid this expectation from ever being called
            fn never(&mut self) {
                self.times.never();
            }

            fn satisfy_sequence(&self) {
                if let Some(handle) = &self.seq_handle {
                    handle.satisfy()
                }
            }

            /// Require this expectation to be called exactly `n` times.
            fn times(&mut self, n: usize) {
                self.times.n(n);
            }

            /// Allow this expectation to be called any number of times
            fn times_any(&mut self) {
                self.times.any();
            }

            /// Allow this expectation to be called any number of times within a given
            /// range
            fn times_range(&mut self, range: ::std::ops::Range<usize>) {
                self.times.range(range);
            }

            fn verify_sequence(&self) {
                if let Some(handle) = &self.seq_handle {
                    handle.verify()
                }
            }

            #[allow(non_camel_case_types)]  // Repurpose $predargs for generics
            fn with<$( $predargs: ::mockall::Predicate<$matchty> + 'static,)*>
                (&mut self, $( $args: $predargs,)*)
            {
                use ::std::ops::DerefMut;
                let mut guard = self.matcher.lock().unwrap();
                let m = Matcher::Pred($( Box::new($args), )*);
                ::std::mem::replace(guard.deref_mut(), m);
            }

            fn withf<F>(&mut self, f: F)
                where F: Fn($( &$matchty, )* ) -> bool + Send + 'static
            {
                use ::std::ops::DerefMut;
                let mut guard = self.matcher.lock().unwrap();
                let m = Matcher::Func(Box::new(f));
                ::std::mem::replace(guard.deref_mut(), m);
            }
        }

        #[derive(Default)]
        pub struct Expectation {
            common: Common,
            rfunc: ::std::sync::Mutex<Rfunc>,
        }

        impl Expectation {
            pub fn call(&self, $( $args: $methty, )* ) -> $o {
                self.common.call($( $matchcall, )*);
                self.rfunc.lock().unwrap().call_mut($( $args, )*)
            }

            pub fn in_sequence(&mut self, seq: &mut ::mockall::Sequence)
                -> &mut Self
            {
                self.common.in_sequence(seq);
                self
            }

            /// Forbid this expectation from ever being called
            pub fn never(&mut self) -> &mut Self {
                self.common.never();
                self
            }

            /// Supply an `FnOnce` closure that will provide the return value
            /// for this Expectation.  This is useful for return types that
            /// aren't `Clone`.  It will be an error to call this Expectation
            /// multiple times.
            pub fn return_once<F>(&mut self, f: F) -> &mut Self
                where F: FnOnce($( $methty, )*) -> $o + Send + 'static
            {
                use ::std::ops::DerefMut;
                let mut fopt = Some(f);
                let fmut = move |$( $args, )*| {
                    if let Some(f) = fopt.take() {
                        f($( $args, )*)
                    } else {
                        panic!("Called a method twice that was expected only once")
                    }
                };
                {
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(),
                                        Rfunc::Once(Box::new(fmut)));
                }
                self
            }

            pub fn returning<F>(&mut self, f: F) -> &mut Self
                where F: FnMut($( $methty, )*) -> $o + Send + 'static
            {
                {
                    use ::std::ops::DerefMut;
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(), Rfunc::Mut(Box::new(f)));
                }
                self
            }

            /// Require this expectation to be called exactly `n` times.
            pub fn times(&mut self, n: usize) -> &mut Self {
                self.common.times(n);
                self
            }

            /// Allow this expectation to be called any number of times
            ///
            /// This behavior is the default, but the method is provided in case the
            /// default behavior changes.
            pub fn times_any(&mut self) -> &mut Self {
                self.common.times_any();
                self
            }

            /// Allow this expectation to be called any number of times within a
            /// given range
            pub fn times_range(&mut self, range: ::std::ops::Range<usize>)
                -> &mut Self
            {
                self.common.times_range(range);
                self
            }

            #[allow(non_camel_case_types)]  // Repurpose $predargs for generics
            pub fn with<$( $predargs: ::mockall::Predicate<$matchty> + 'static,)*>
                (&mut self, $( $args: $predargs,)*) -> &mut Self
            {
                self.common.with($( $args, )*);
                self
            }

            pub fn withf<F>(&mut self, f: F) -> &mut Self
                where F: Fn($( &$matchty, )* ) -> bool + Send + 'static
            {
                self.common.withf(f);
                self
            }
        }

        }
        use $module::Expectation as $expectation;
    }
}
<commit_msg>omnimock: add return_once_st and returning_st<commit_after>// vim: tw=80
/// Generate Expectation and Expectations types with specific signatures
///
/// # Arguments
///
/// * `expectation`:    Name of the generated Expectation type
/// * `module`:         Name of the private module to create
/// * `o`:              Output type.  Must be static
/// * `methty`:         Comma-delimited sequence of arguments types for the
///                     method being mocked.
/// * `matchcall`:      Comma-delimited sequence of expressions that produce
///                     values of type `matchty` from values of type `methty`.
/// * `args`:           comma-delimited sequence of argument names for each
///                     argument.  Ideally this wouldn't need to be specified,
///                     but it is due to Rust's macro hygiene rules.
/// * `predargs`:       Comma-delimited sequence of identifiers of the same
///                     length as `args`, but distinct.
/// * `matchty`:        comma-delimited sequence of types for each match
///                     argument.  Must all be `'static`.
///
/// # Examples
///
/// ```
/// // Mock a method like foo(&self, x: u32, y: &i16) -> u32
/// omnimock!{(u32, &i16), u32, i, [&i.0, i.1], [i0, i1], [u32, i16]}
#[macro_export]
macro_rules! omnimock {(
        $expectation:ident,
        $module:ident,
        $o:ty,
        [ $( $methty:ty ),* ],
        [ $( $matchcall:expr ),* ],
        [ $( $args:ident ),* ],
        [ $( $predargs:ident ),* ],
        [ $( $matchty:ty ),* ]) =>
    {
        mod $module {
            use super::*;

        enum Rfunc {
            Default,
            // Indicates that a `return_once` expectation has already returned
            Expired,
            Mut(Box<dyn FnMut($( $methty, )*) -> $o + Send>),
            // Should be Box<dyn FnOnce> once that feature is stabilized
            // https://github.com/rust-lang/rust/issues/28796
            Once(Box<dyn FnMut($( $methty, )*) -> $o + Send>),
        }

        impl  Rfunc {
            fn call_mut(&mut self, $( $args: $methty, )* ) -> $o {
                match self {
                    Rfunc::Default => {
                        unimplemented!()
                        //Self::return_default()
                    },
                    Rfunc::Expired => {
                        panic!("Called a method twice that was expected only once")
                    },
                    Rfunc::Mut(f) => {
                        f( $( $args, )* )
                    },
                    Rfunc::Once(_) => {
                        let fo = ::std::mem::replace(self, Rfunc::Expired);
                        if let Rfunc::Once(mut f) = fo {
                            f( $( $args, )* )
                        } else {
                            unreachable!()
                        }
                    },
                }
            }
        }

        impl std::default::Default for Rfunc {
            fn default() -> Self {
                Rfunc::Default
            }
        }

        enum Matcher {
            Func(Box<Fn($( &$matchty, )* ) -> bool>),
            Pred( $( Box<::mockall::Predicate<$matchty>>, )* )
        }

        impl Matcher {
            fn eval(&self, $( $args: &$matchty, )*) -> bool {
                match self {
                    Matcher::Func(f) => f($( $args, )*),
                    Matcher::Pred($( $predargs, )*) =>
                        [$( $predargs.eval($args), )*]
                        .into_iter()
                        .all(|x| *x),
                }
            }

            fn verify(&self, $( $args: &$matchty, )* ) {
                use predicates_tree::CaseTreeExt;
                match self {
                    Matcher::Func(f) => assert!(f($( $args, )*),
                        "Expectation didn't match arguments"),
                    Matcher::Pred($( $predargs, )*) => {
                        $(if let Some(c) = $predargs.find_case(false, $args) {
                            panic!("Expectation didn't match arguments:\n{}",
                                   c.tree());
                        })*
                    }
                }
            }
        }

        impl Default for Matcher {
            #[allow(unused_variables)]
            fn default() -> Self {
                Matcher::Func(Box::new(|$( $args, )*| true))
            }
        }

        #[derive(Default)]
        struct Common {
            matcher: ::std::sync::Mutex<Matcher>,
            seq_handle: Option<::mockall::SeqHandle>,
            times: ::mockall::Times
        }

        impl Common {
            fn call(&self, $( $args: &$matchty, )* ) {
                self.matcher.lock().unwrap().verify($( $args, )*);
                self.times.call();
                self.verify_sequence();
                if self.times.is_satisfied() {
                    self.satisfy_sequence()
                }
            }

            fn in_sequence(&mut self, seq: &mut ::mockall::Sequence)
                -> &mut Self
            {
                assert!(self.times.is_exact(),
                    "Only Expectations with an exact call count have sequences");
                self.seq_handle = Some(seq.next());
                self
            }

            /// Forbid this expectation from ever being called
            fn never(&mut self) {
                self.times.never();
            }

            fn satisfy_sequence(&self) {
                if let Some(handle) = &self.seq_handle {
                    handle.satisfy()
                }
            }

            /// Require this expectation to be called exactly `n` times.
            fn times(&mut self, n: usize) {
                self.times.n(n);
            }

            /// Allow this expectation to be called any number of times
            fn times_any(&mut self) {
                self.times.any();
            }

            /// Allow this expectation to be called any number of times within a given
            /// range
            fn times_range(&mut self, range: ::std::ops::Range<usize>) {
                self.times.range(range);
            }

            fn verify_sequence(&self) {
                if let Some(handle) = &self.seq_handle {
                    handle.verify()
                }
            }

            #[allow(non_camel_case_types)]  // Repurpose $predargs for generics
            fn with<$( $predargs: ::mockall::Predicate<$matchty> + 'static,)*>
                (&mut self, $( $args: $predargs,)*)
            {
                use ::std::ops::DerefMut;
                let mut guard = self.matcher.lock().unwrap();
                let m = Matcher::Pred($( Box::new($args), )*);
                ::std::mem::replace(guard.deref_mut(), m);
            }

            fn withf<F>(&mut self, f: F)
                where F: Fn($( &$matchty, )* ) -> bool + Send + 'static
            {
                use ::std::ops::DerefMut;
                let mut guard = self.matcher.lock().unwrap();
                let m = Matcher::Func(Box::new(f));
                ::std::mem::replace(guard.deref_mut(), m);
            }
        }

        #[derive(Default)]
        pub struct Expectation {
            common: Common,
            rfunc: ::std::sync::Mutex<Rfunc>,
        }

        impl Expectation {
            pub fn call(&self, $( $args: $methty, )* ) -> $o {
                self.common.call($( $matchcall, )*);
                self.rfunc.lock().unwrap().call_mut($( $args, )*)
            }

            pub fn in_sequence(&mut self, seq: &mut ::mockall::Sequence)
                -> &mut Self
            {
                self.common.in_sequence(seq);
                self
            }

            /// Forbid this expectation from ever being called
            pub fn never(&mut self) -> &mut Self {
                self.common.never();
                self
            }

            /// Supply an `FnOnce` closure that will provide the return value
            /// for this Expectation.  This is useful for return types that
            /// aren't `Clone`.  It will be an error to call this Expectation
            /// multiple times.
            pub fn return_once<F>(&mut self, f: F) -> &mut Self
                where F: FnOnce($( $methty, )*) -> $o + Send + 'static
            {
                use ::std::ops::DerefMut;
                let mut fopt = Some(f);
                let fmut = move |$( $args: $methty, )*| {
                    if let Some(f) = fopt.take() {
                        f($( $args, )*)
                    } else {
                        panic!("Called a method twice that was expected only once")
                    }
                };
                {
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(),
                                        Rfunc::Once(Box::new(fmut)));
                }
                self
            }

            /// Single-threaded version of [`return_once`](#method.return_once).
            /// This is useful for return types that are neither `Send` nor
            /// `Clone`.
            ///
            /// It is a runtime error to call the mock method from a different
            /// thread than the one that originally called this method.  It is
            /// also a runtime error to call the method more than once.
            pub fn return_once_st<F>(&mut self, f: F) -> &mut Self
                where F: FnOnce($( $methty, )*) -> $o + 'static
            {
                use ::std::ops::DerefMut;
                let mut fragile = Some(::fragile::Fragile::new(f));
                let fmut = Box::new(move |$( $args: $methty, )*| {
                    match fragile.take() {
                        Some(frag) => (frag.into_inner())($( $args, )*),
                        None => panic!(
                            "Called a method twice that was expected only once")
                    }
                });
                {
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(), Rfunc::Once(fmut));
                }
                self
            }

            pub fn returning<F>(&mut self, f: F) -> &mut Self
                where F: FnMut($( $methty, )*) -> $o + Send + 'static
            {
                {
                    use ::std::ops::DerefMut;
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(), Rfunc::Mut(Box::new(f)));
                }
                self
            }

            /// Single-threaded version of [`returning`](#method.returning).
            /// Can be used when the argument or return type isn't `Send`.
            ///
            /// It is a runtime error to call the mock method from a different
            /// thread than the one that originally called this method.
            pub fn returning_st<F>(&mut self, f: F) -> &mut Self
                where F: FnMut($( $methty, )*) -> $o + 'static
            {
                use ::std::ops::DerefMut;
                let mut fragile = ::fragile::Fragile::new(f);
                let fmut = move |$( $args: $methty, )*| {
                    (fragile.get_mut())($( $args, )*)
                };
                {
                    let mut guard = self.rfunc.lock().unwrap();
                    ::std::mem::replace(guard.deref_mut(),
                                        Rfunc::Mut(Box::new(fmut)));
                }
                self
            }

            /// Require this expectation to be called exactly `n` times.
            pub fn times(&mut self, n: usize) -> &mut Self {
                self.common.times(n);
                self
            }

            /// Allow this expectation to be called any number of times
            ///
            /// This behavior is the default, but the method is provided in case the
            /// default behavior changes.
            pub fn times_any(&mut self) -> &mut Self {
                self.common.times_any();
                self
            }

            /// Allow this expectation to be called any number of times within a
            /// given range
            pub fn times_range(&mut self, range: ::std::ops::Range<usize>)
                -> &mut Self
            {
                self.common.times_range(range);
                self
            }

            #[allow(non_camel_case_types)]  // Repurpose $predargs for generics
            pub fn with<$( $predargs: ::mockall::Predicate<$matchty> + 'static,)*>
                (&mut self, $( $args: $predargs,)*) -> &mut Self
            {
                self.common.with($( $args, )*);
                self
            }

            pub fn withf<F>(&mut self, f: F) -> &mut Self
                where F: Fn($( &$matchty, )* ) -> bool + Send + 'static
            {
                self.common.withf(f);
                self
            }
        }

        }
        use $module::Expectation as $expectation;
    }
}
<|endoftext|>


<commit_before>//! Cryptocurrency API.

use serde_json;
use router::Router;
use iron::prelude::*;
use bodyparser;

use exonum::api::{Api, ApiError};
use exonum::node::TransactionSend;
use exonum::crypto::{PublicKey, Hash};
use exonum::storage::{MapProof, ListProof};
use exonum::blockchain::{self, Blockchain, BlockProof, Transaction};
use exonum::helpers::Height;

use std::fmt;

use {CRYPTOCURRENCY_SERVICE_ID, CurrencySchema};
use transactions::WalletTransactions;
use wallet::Wallet;

/// The structure returned by the REST API.
#[derive(Debug, Serialize, Deserialize)]
pub struct TransactionResponse {
    /// Hash of the transaction.
    pub tx_hash: Hash,
}

/// Proof of existence for specific wallet.
#[derive(Debug, Serialize)]
pub struct WalletProof {
    /// Proof to the whole database table.
    to_table: MapProof<Hash>,
    /// Proof to the specific wallet in this table.
    to_wallet: MapProof<Wallet>,
}

/// Wallet information.
#[derive(Debug, Serialize)]
pub struct WalletInfo {
    block_proof: BlockProof,
    wallet_proof: WalletProof,
    wallet_history: Option<ListProof<Hash>>,
}

/// TODO: Add documentation.
#[derive(Clone)]
pub struct CryptocurrencyApi<T: TransactionSend + Clone> {
    /// Exonum blockchain.
    pub blockchain: Blockchain,
    /// Channel for transactions.
    pub channel: T,
}

impl<T> CryptocurrencyApi<T>
where
    T: TransactionSend + Clone + 'static,
{
    fn wallet_info(&self, pub_key: &PublicKey) -> Result<WalletInfo, ApiError> {
        let view = self.blockchain.snapshot();
        let general_schema = blockchain::Schema::new(&view);
        let mut view = self.blockchain.fork();
        let currency_schema = CurrencySchema::new(&mut view);

        let max_height = general_schema.block_hashes_by_height().len() - 1;

        let block_proof = general_schema
            .block_and_precommits(Height(max_height))
            .unwrap();

        let to_table: MapProof<Hash> =
            general_schema.get_proof_to_service_table(CRYPTOCURRENCY_SERVICE_ID, 0);

        let to_wallet: MapProof<Wallet> = currency_schema.wallets().get_proof(pub_key);

        let wallet_proof = WalletProof {
            to_table,
            to_wallet,
        };

        let wallet = currency_schema.wallet(pub_key);

        let wallet_history = wallet.map(|_| {
            let history = currency_schema.wallet_history(pub_key);
            history.get_range_proof(0, history.len())
        });

        Ok(WalletInfo {
            block_proof,
            wallet_proof,
            wallet_history,
        })
    }

    fn wire_post_transaction(self, router: &mut Router) {
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<WalletTransactions>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self.channel.send(transaction).map_err(ApiError::from)?;
                    let json = TransactionResponse { tx_hash };
                    self.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::BadRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::BadRequest(e.to_string()))?,
            }
        };
        router.post("/v1/wallets/transaction", transaction, "post_transaction");
    }

    fn wire_wallet_info(self, router: &mut Router) {
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let pub_key: PublicKey = self.url_fragment(req, "pubkey")?;
            let info = self.wallet_info(&pub_key)?;
            self.ok_response(&serde_json::to_value(&info).unwrap())
        };
        router.get("/v1/wallets/info/:pubkey", wallet_info, "wallet_info");
    }

    fn wire_wallet(self, router: &mut Router) {
        let wallet = move |req: &mut Request| -> IronResult<Response> {
            let pub_key: PublicKey = self.url_fragment(req, "pubkey")?;
            let view = self.blockchain.snapshot();
            let schema = CurrencySchema::new(view);
            if let Some(wallet) = schema.wallet(&pub_key) {
                self.ok_response(&serde_json::to_value(&wallet).unwrap())
            } else {
                self.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };
        router.get("/v1/wallets/:pubkey", wallet, "wallet");
    }
}

impl<T: TransactionSend + Clone> fmt::Debug for CryptocurrencyApi<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CryptocurrencyApi {{}}")
    }
}

impl<T> Api for CryptocurrencyApi<T>
where
    T: 'static + TransactionSend + Clone,
{
    fn wire(&self, router: &mut Router) {
        self.clone().wire_post_transaction(router);
        self.clone().wire_wallet_info(router);
        self.clone().wire_wallet(router);
    }
}
<commit_msg>Put back list of transactions<commit_after>//! Cryptocurrency API.

use serde_json;
use router::Router;
use iron::prelude::*;
use bodyparser;

use exonum::api::{Api, ApiError};
use exonum::node::TransactionSend;
use exonum::crypto::{PublicKey, Hash};
use exonum::storage::{MapProof, ListProof};
use exonum::blockchain::{self, Blockchain, BlockProof, Transaction, TransactionSet};
use exonum::helpers::Height;

use std::fmt;

use {CRYPTOCURRENCY_SERVICE_ID, CurrencySchema};
use transactions::WalletTransactions;
use wallet::Wallet;

/// The structure returned by the REST API.
#[derive(Debug, Serialize, Deserialize)]
pub struct TransactionResponse {
    /// Hash of the transaction.
    pub tx_hash: Hash,
}

/// Proof of existence for specific wallet.
#[derive(Debug, Serialize)]
pub struct WalletProof {
    /// Proof to the whole database table.
    to_table: MapProof<Hash>,
    /// Proof to the specific wallet in this table.
    to_wallet: MapProof<Wallet>,
}

/// Wallet history.
#[derive(Debug, Serialize)]
pub struct WalletHistory {
    proof: ListProof<Hash>,
    transactions: Vec<WalletTransactions>,
}

/// Wallet information.
#[derive(Debug, Serialize)]
pub struct WalletInfo {
    block_proof: BlockProof,
    wallet_proof: WalletProof,
    wallet_history: Option<WalletHistory>,
}

/// TODO: Add documentation.
#[derive(Clone)]
pub struct CryptocurrencyApi<T: TransactionSend + Clone> {
    /// Exonum blockchain.
    pub blockchain: Blockchain,
    /// Channel for transactions.
    pub channel: T,
}

impl<T> CryptocurrencyApi<T>
where
    T: TransactionSend + Clone + 'static,
{
    fn wallet_info(&self, pub_key: &PublicKey) -> Result<WalletInfo, ApiError> {
        let view = self.blockchain.snapshot();
        let general_schema = blockchain::Schema::new(&view);
        let mut view = self.blockchain.fork();
        let currency_schema = CurrencySchema::new(&mut view);

        let max_height = general_schema.block_hashes_by_height().len() - 1;

        let block_proof = general_schema
            .block_and_precommits(Height(max_height))
            .unwrap();

        let to_table: MapProof<Hash> =
            general_schema.get_proof_to_service_table(CRYPTOCURRENCY_SERVICE_ID, 0);

        let to_wallet: MapProof<Wallet> = currency_schema.wallets().get_proof(pub_key);

        let wallet_proof = WalletProof {
            to_table,
            to_wallet,
        };

        let wallet = currency_schema.wallet(pub_key);

        let wallet_history = wallet.map(|_| {
            let history = currency_schema.wallet_history(pub_key);
            let proof = history.get_range_proof(0, history.len());

            let transactions: Vec<WalletTransactions> = history
                .iter()
                .map(|record| {
                    general_schema
                        .transactions()
                        .get(&record)
                        .unwrap()
                })
                .map(|raw| WalletTransactions::tx_from_raw(raw).unwrap())
                .collect::<Vec<_>>();

            WalletHistory {
                proof,
                transactions,
            }
        });

        Ok(WalletInfo {
            block_proof,
            wallet_proof,
            wallet_history,
        })
    }

    fn wire_post_transaction(self, router: &mut Router) {
        let transaction = move |req: &mut Request| -> IronResult<Response> {
            match req.get::<bodyparser::Struct<WalletTransactions>>() {
                Ok(Some(transaction)) => {
                    let transaction: Box<Transaction> = transaction.into();
                    let tx_hash = transaction.hash();
                    self.channel.send(transaction).map_err(ApiError::from)?;
                    let json = TransactionResponse { tx_hash };
                    self.ok_response(&serde_json::to_value(&json).unwrap())
                }
                Ok(None) => Err(ApiError::BadRequest("Empty request body".into()))?,
                Err(e) => Err(ApiError::BadRequest(e.to_string()))?,
            }
        };
        router.post("/v1/wallets/transaction", transaction, "post_transaction");
    }

    fn wire_wallet_info(self, router: &mut Router) {
        let wallet_info = move |req: &mut Request| -> IronResult<Response> {
            let pub_key: PublicKey = self.url_fragment(req, "pubkey")?;
            let info = self.wallet_info(&pub_key)?;
            self.ok_response(&serde_json::to_value(&info).unwrap())
        };
        router.get("/v1/wallets/info/:pubkey", wallet_info, "wallet_info");
    }

    fn wire_wallet(self, router: &mut Router) {
        let wallet = move |req: &mut Request| -> IronResult<Response> {
            let pub_key: PublicKey = self.url_fragment(req, "pubkey")?;
            let view = self.blockchain.snapshot();
            let schema = CurrencySchema::new(view);
            if let Some(wallet) = schema.wallet(&pub_key) {
                self.ok_response(&serde_json::to_value(&wallet).unwrap())
            } else {
                self.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
            }
        };
        router.get("/v1/wallets/:pubkey", wallet, "wallet");
    }
}

impl<T: TransactionSend + Clone> fmt::Debug for CryptocurrencyApi<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "CryptocurrencyApi {{}}")
    }
}

impl<T> Api for CryptocurrencyApi<T>
where
    T: 'static + TransactionSend + Clone,
{
    fn wire(&self, router: &mut Router) {
        self.clone().wire_post_transaction(router);
        self.clone().wire_wallet_info(router);
        self.clone().wire_wallet(router);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use super::{super::DirectEvaluatorInput, ApiEvaluatorError, API_CATEGORY};
use crate::{
    configuration::EvaluatorArgs,
    evaluator::{EvaluationResult, Evaluator},
    evaluators::EvaluatorType,
};
use anyhow::Result;
use aptos_rest_client::{aptos_api_types::TransactionData, Client as AptosRestClient};
use clap::Parser;
use poem_openapi::Object as PoemObject;
use serde::{Deserialize, Serialize};
use std::cmp::{max, min};

const TRANSACTIONS_ENDPOINT: &str = "/transactions/by_version";

#[derive(Clone, Debug, Deserialize, Parser, PoemObject, Serialize)]
pub struct TransactionAvailabilityEvaluatorArgs {}

#[derive(Debug)]
pub struct TransactionAvailabilityEvaluator {
    #[allow(dead_code)]
    args: TransactionAvailabilityEvaluatorArgs,
}

impl TransactionAvailabilityEvaluator {
    pub fn new(args: TransactionAvailabilityEvaluatorArgs) -> Self {
        Self { args }
    }

    /// Fetch a transaction by version and return it.
    async fn get_transaction_by_version(
        client: &AptosRestClient,
        version: u64,
    ) -> Result<TransactionData, ApiEvaluatorError> {
        Ok(client
            .get_transaction_by_version_bcs(version)
            .await
            .map_err(|e| {
                ApiEvaluatorError::EndpointError(
                    TRANSACTIONS_ENDPOINT.to_string(),
                    anyhow::Error::from(e).context(format!(
                        "The node API failed to return the requested transaction at version: {}",
                        version
                    )),
                )
            })?
            .into_inner())
    }

    /// Helper to get the accumulator root hash from an on chain transaction
    /// as returned by the API.
    fn unwrap_accumulator_root_hash(
        transaction_data: &TransactionData,
    ) -> Result<&aptos_crypto::HashValue, ApiEvaluatorError> {
        match transaction_data {
            TransactionData::OnChain(on_chain) => Ok(&on_chain.accumulator_root_hash),
            wildcard => Err(ApiEvaluatorError::EndpointError(
                TRANSACTIONS_ENDPOINT.to_string(),
                anyhow::anyhow!(
                    "The API unexpectedly returned a transaction that was not an on-chain transaction: {:?}",
                    wildcard
                ),
            ))
        }
    }
}

#[async_trait::async_trait]
impl Evaluator for TransactionAvailabilityEvaluator {
    type Input = DirectEvaluatorInput;
    type Error = ApiEvaluatorError;

    /// Assert that the target node can produce the same transaction that the
    /// baseline produced after a delay. We confirm that the transactions are
    /// same by looking at the version.
    async fn evaluate(&self, input: &Self::Input) -> Result<Vec<EvaluationResult>, Self::Error> {
        let oldest_baseline_version = input.baseline_index_response.oldest_ledger_version.0;
        let oldest_target_version = input.target_index_response.oldest_ledger_version.0;
        let latest_baseline_version = input.baseline_index_response.ledger_version.0;
        let latest_target_version = input.target_index_response.ledger_version.0;

        // Get the oldest ledger version between the two nodes.
        let oldest_shared_version = max(oldest_baseline_version, oldest_target_version);

        // Get the least up to date latest ledger version between the two nodes.
        let latest_shared_version = min(latest_baseline_version, latest_target_version);

        // Ensure that there is a window between the oldest shared version and
        // latest shared version. If there is not, it will not be possible to
        // pull a transaction that both nodes have.
        if oldest_shared_version > latest_shared_version {
            return Ok(vec![self.build_evaluation_result(
                "Unable to pull transaction from both nodes".to_string(),
                0,
                format!(
                    "We were unable to find a ledger version window between \
                        the baseline and target nodes. The oldest and latest \
                        ledger versions on the baseline node are {} and {}. \
                        The oldest and latest ledger versions on the target \
                        node are {} and {}. This means your API cannot return \
                        a transaction that the baseline has for us to verify. \
                        Likely this means your node is too out of sync with \
                        the network, but it could also indicate an \
                        over-aggressive pruner.",
                    oldest_baseline_version,
                    latest_baseline_version,
                    oldest_target_version,
                    latest_target_version,
                ),
            )]);
        }

        // We've asserted that both nodes are sufficiently up to date relative
        // to each other, we should be able to pull the same transaction from
        // both nodes.

        let baseline_client = input
            .baseline_node_information
            .node_address
            .get_api_client(std::time::Duration::from_secs(4));

        let latest_baseline_transaction =
            Self::get_transaction_by_version(&baseline_client, latest_shared_version).await?;
        let latest_baseline_accumulator_root_hash =
            Self::unwrap_accumulator_root_hash(&latest_baseline_transaction)?;

        let target_client = AptosRestClient::new(input.target_node_address.get_api_url());
        let evaluation =
            match Self::get_transaction_by_version(&target_client, latest_shared_version).await {
                Ok(latest_target_transaction) => {
                    match Self::unwrap_accumulator_root_hash(&latest_target_transaction) {
                        Ok(latest_target_accumulator_root_hash) => {
                            if latest_baseline_accumulator_root_hash
                                == latest_target_accumulator_root_hash
                            {
                                self.build_evaluation_result(
                                    "Target node produced valid recent transaction".to_string(),
                                    100,
                                    format!(
                                        "We were able to pull the same transaction (version: {}) \
                                    from both your node and the baseline node. Great! This \
                                    implies that your node is keeping up with other nodes \
                                    in the network and returning valid transaction data.",
                                        latest_shared_version,
                                    ),
                                )
                            } else {
                                self.build_evaluation_result(
                                    "Target node produced recent transaction, but it was invalid"
                                        .to_string(),
                                    0,
                                    format!(
                                        "We were able to pull the same transaction (version: {}) \
                                    from both your node and the baseline node. However, the \
                                    transaction was invalid compared to the baseline as the \
                                    accumulator root hash of the transaction ({}) was different \
                                    compared to the baseline ({}).",
                                        latest_shared_version,
                                        latest_target_accumulator_root_hash,
                                        latest_baseline_accumulator_root_hash,
                                    ),
                                )
                            }
                        }
                        Err(error) => self.build_evaluation_result(
                            "Target node produced recent transaction, but it was missing metadata"
                                .to_string(),
                            10,
                            format!(
                                "We were able to pull the same transaction (version: {}) \
                            from both your node and the baseline node. However, the \
                            the transaction was missing metadata such as the version, \
                            accumulator root hash, etc. Error: {}",
                                latest_shared_version, error,
                            ),
                        ),
                    }
                }
                Err(error) => self.build_evaluation_result(
                    "Target node failed to produce transaction".to_string(),
                    25,
                    format!(
                        "The target node claims it has transactions between versions {} and {}, \
                    but it was unable to return the transaction with version {}. This implies \
                    something is wrong with your node's API. Error: {}",
                        oldest_target_version, latest_target_version, latest_shared_version, error,
                    ),
                ),
            };

        Ok(vec![evaluation])
    }

    fn get_category_name() -> String {
        API_CATEGORY.to_string()
    }

    fn get_evaluator_name() -> String {
        "transaction_availability".to_string()
    }

    fn from_evaluator_args(evaluator_args: &EvaluatorArgs) -> Result<Self> {
        Ok(Self::new(
            evaluator_args.transaction_availability_args.clone(),
        ))
    }

    fn evaluator_type_from_evaluator_args(evaluator_args: &EvaluatorArgs) -> Result<EvaluatorType> {
        Ok(EvaluatorType::Api(Box::new(Self::from_evaluator_args(
            evaluator_args,
        )?)))
    }
}
<commit_msg>[NHC] Improve error message in the transaction availability evaluator<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use super::{super::DirectEvaluatorInput, ApiEvaluatorError, API_CATEGORY};
use crate::{
    configuration::EvaluatorArgs,
    evaluator::{EvaluationResult, Evaluator},
    evaluators::EvaluatorType,
};
use anyhow::Result;
use aptos_rest_client::{aptos_api_types::TransactionData, Client as AptosRestClient};
use clap::Parser;
use poem_openapi::Object as PoemObject;
use serde::{Deserialize, Serialize};
use std::cmp::{max, min};

const TRANSACTIONS_ENDPOINT: &str = "/transactions/by_version";

#[derive(Clone, Debug, Deserialize, Parser, PoemObject, Serialize)]
pub struct TransactionAvailabilityEvaluatorArgs {}

#[derive(Debug)]
pub struct TransactionAvailabilityEvaluator {
    #[allow(dead_code)]
    args: TransactionAvailabilityEvaluatorArgs,
}

impl TransactionAvailabilityEvaluator {
    pub fn new(args: TransactionAvailabilityEvaluatorArgs) -> Self {
        Self { args }
    }

    /// Fetch a transaction by version and return it.
    async fn get_transaction_by_version(
        client: &AptosRestClient,
        version: u64,
        node_name: &str,
    ) -> Result<TransactionData, ApiEvaluatorError> {
        Ok(client
            .get_transaction_by_version_bcs(version)
            .await
            .map_err(|e| {
                ApiEvaluatorError::EndpointError(
                    TRANSACTIONS_ENDPOINT.to_string(),
                    anyhow::Error::from(e).context(format!(
                        "The {} node API failed to return the requested transaction at version: {}",
                        node_name, version
                    )),
                )
            })?
            .into_inner())
    }

    /// Helper to get the accumulator root hash from an on chain transaction
    /// as returned by the API.
    fn unwrap_accumulator_root_hash(
        transaction_data: &TransactionData,
    ) -> Result<&aptos_crypto::HashValue, ApiEvaluatorError> {
        match transaction_data {
            TransactionData::OnChain(on_chain) => Ok(&on_chain.accumulator_root_hash),
            wildcard => Err(ApiEvaluatorError::EndpointError(
                TRANSACTIONS_ENDPOINT.to_string(),
                anyhow::anyhow!(
                    "The API unexpectedly returned a transaction that was not an on-chain transaction: {:?}",
                    wildcard
                ),
            ))
        }
    }
}

#[async_trait::async_trait]
impl Evaluator for TransactionAvailabilityEvaluator {
    type Input = DirectEvaluatorInput;
    type Error = ApiEvaluatorError;

    /// Assert that the target node can produce the same transaction that the
    /// baseline produced after a delay. We confirm that the transactions are
    /// same by looking at the version.
    async fn evaluate(&self, input: &Self::Input) -> Result<Vec<EvaluationResult>, Self::Error> {
        let oldest_baseline_version = input.baseline_index_response.oldest_ledger_version.0;
        let oldest_target_version = input.target_index_response.oldest_ledger_version.0;
        let latest_baseline_version = input.baseline_index_response.ledger_version.0;
        let latest_target_version = input.target_index_response.ledger_version.0;

        // Get the oldest ledger version between the two nodes.
        let oldest_shared_version = max(oldest_baseline_version, oldest_target_version);

        // Get the least up to date latest ledger version between the two nodes.
        let latest_shared_version = min(latest_baseline_version, latest_target_version);

        // Ensure that there is a window between the oldest shared version and
        // latest shared version. If there is not, it will not be possible to
        // pull a transaction that both nodes have.
        if oldest_shared_version > latest_shared_version {
            return Ok(vec![self.build_evaluation_result(
                "Unable to pull transaction from both nodes".to_string(),
                0,
                format!(
                    "We were unable to find a ledger version window between \
                        the baseline and target nodes. The oldest and latest \
                        ledger versions on the baseline node are {} and {}. \
                        The oldest and latest ledger versions on the target \
                        node are {} and {}. This means your API cannot return \
                        a transaction that the baseline has for us to verify. \
                        Likely this means your node is too out of sync with \
                        the network, but it could also indicate an \
                        over-aggressive pruner.",
                    oldest_baseline_version,
                    latest_baseline_version,
                    oldest_target_version,
                    latest_target_version,
                ),
            )]);
        }

        // We've asserted that both nodes are sufficiently up to date relative
        // to each other, we should be able to pull the same transaction from
        // both nodes.

        let baseline_client = input
            .baseline_node_information
            .node_address
            .get_api_client(std::time::Duration::from_secs(4));

        let latest_baseline_transaction =
            Self::get_transaction_by_version(&baseline_client, latest_shared_version, "baseline")
                .await?;

        let latest_baseline_accumulator_root_hash =
            Self::unwrap_accumulator_root_hash(&latest_baseline_transaction)?;

        let target_client = AptosRestClient::new(input.target_node_address.get_api_url());
        let evaluation =
            match Self::get_transaction_by_version(&target_client, latest_shared_version, "target")
                .await
            {
                Ok(latest_target_transaction) => {
                    match Self::unwrap_accumulator_root_hash(&latest_target_transaction) {
                        Ok(latest_target_accumulator_root_hash) => {
                            if latest_baseline_accumulator_root_hash
                                == latest_target_accumulator_root_hash
                            {
                                self.build_evaluation_result(
                                    "Target node produced valid recent transaction".to_string(),
                                    100,
                                    format!(
                                        "We were able to pull the same transaction (version: {}) \
                                    from both your node and the baseline node. Great! This \
                                    implies that your node is keeping up with other nodes \
                                    in the network and returning valid transaction data.",
                                        latest_shared_version,
                                    ),
                                )
                            } else {
                                self.build_evaluation_result(
                                    "Target node produced recent transaction, but it was invalid"
                                        .to_string(),
                                    0,
                                    format!(
                                        "We were able to pull the same transaction (version: {}) \
                                    from both your node and the baseline node. However, the \
                                    transaction was invalid compared to the baseline as the \
                                    accumulator root hash of the transaction ({}) was different \
                                    compared to the baseline ({}).",
                                        latest_shared_version,
                                        latest_target_accumulator_root_hash,
                                        latest_baseline_accumulator_root_hash,
                                    ),
                                )
                            }
                        }
                        Err(error) => self.build_evaluation_result(
                            "Target node produced recent transaction, but it was missing metadata"
                                .to_string(),
                            10,
                            format!(
                                "We were able to pull the same transaction (version: {}) \
                            from both your node and the baseline node. However, the \
                            the transaction was missing metadata such as the version, \
                            accumulator root hash, etc. Error: {}",
                                latest_shared_version, error,
                            ),
                        ),
                    }
                }
                Err(error) => self.build_evaluation_result(
                    "Target node failed to produce transaction".to_string(),
                    25,
                    format!(
                        "The target node claims it has transactions between versions {} and {}, \
                    but it was unable to return the transaction with version {}. This implies \
                    something is wrong with your node's API. Error: {}",
                        oldest_target_version, latest_target_version, latest_shared_version, error,
                    ),
                ),
            };

        Ok(vec![evaluation])
    }

    fn get_category_name() -> String {
        API_CATEGORY.to_string()
    }

    fn get_evaluator_name() -> String {
        "transaction_availability".to_string()
    }

    fn from_evaluator_args(evaluator_args: &EvaluatorArgs) -> Result<Self> {
        Ok(Self::new(
            evaluator_args.transaction_availability_args.clone(),
        ))
    }

    fn evaluator_type_from_evaluator_args(evaluator_args: &EvaluatorArgs) -> Result<EvaluatorType> {
        Ok(EvaluatorType::Api(Box::new(Self::from_evaluator_args(
            evaluator_args,
        )?)))
    }
}
<|endoftext|>


<commit_before>macro_rules! expand_align {
    () => {
        s! {
            #[allow(missing_debug_implementations)]
            #[repr(align(4))]
            pub struct pthread_mutex_t {
                size: [u8; ::__SIZEOF_PTHREAD_MUTEX_T],
            }

            #[repr(align(4))]
            pub struct pthread_rwlock_t {
                size: [u8; ::__SIZEOF_PTHREAD_RWLOCK_T],
            }

            #[repr(align(4))]
            pub struct pthread_mutexattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_MUTEXATTR_T],
            }

            #[repr(align(4))]
            pub struct pthread_rwlockattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_RWLOCKATTR_T],
            }

            #[repr(align(4))]
            pub struct pthread_condattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_CONDATTR_T],
            }
        }

        s_no_extra_traits! {
            #[cfg_attr(target_pointer_width = "32",
                       repr(align(4)))]
            #[cfg_attr(target_pointer_width = "64",
                       repr(align(8)))]
            pub struct pthread_cond_t {
                size: [u8; ::__SIZEOF_PTHREAD_COND_T],
            }

            #[repr(align(8))]
            pub struct max_align_t {
                priv_: f64; 2]
            }

        }

        cfg_if! {
            if #[cfg(feature = "extra_traits")] {
                impl PartialEq for pthread_cond_t {
                    fn eq(&self, other: &pthread_cond_t) -> bool {
                        self.size
                            .iter()
                            .zip(other.size.iter())
                            .all(|(a,b)| a == b)
                    }
                }
                impl Eq for pthread_cond_t {}
                impl ::fmt::Debug for pthread_cond_t {
                    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                        f.debug_struct("pthread_cond_t")
                            // FIXME: .field("size", &self.size)
                            .finish()
                    }
                }
                impl ::hash::Hash for pthread_cond_t {
                    fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                        self.size.hash(state);
                    }
                }
            }
        }
    };
}
<commit_msg>Fix typo<commit_after>macro_rules! expand_align {
    () => {
        s! {
            #[allow(missing_debug_implementations)]
            #[repr(align(4))]
            pub struct pthread_mutex_t {
                size: [u8; ::__SIZEOF_PTHREAD_MUTEX_T],
            }

            #[repr(align(4))]
            pub struct pthread_rwlock_t {
                size: [u8; ::__SIZEOF_PTHREAD_RWLOCK_T],
            }

            #[repr(align(4))]
            pub struct pthread_mutexattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_MUTEXATTR_T],
            }

            #[repr(align(4))]
            pub struct pthread_rwlockattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_RWLOCKATTR_T],
            }

            #[repr(align(4))]
            pub struct pthread_condattr_t {
                size: [u8; ::__SIZEOF_PTHREAD_CONDATTR_T],
            }
        }

        s_no_extra_traits! {
            #[cfg_attr(target_pointer_width = "32",
                       repr(align(4)))]
            #[cfg_attr(target_pointer_width = "64",
                       repr(align(8)))]
            pub struct pthread_cond_t {
                size: [u8; ::__SIZEOF_PTHREAD_COND_T],
            }

            #[repr(align(8))]
            pub struct max_align_t {
                priv_: [f64; 2]
            }

        }

        cfg_if! {
            if #[cfg(feature = "extra_traits")] {
                impl PartialEq for pthread_cond_t {
                    fn eq(&self, other: &pthread_cond_t) -> bool {
                        self.size
                            .iter()
                            .zip(other.size.iter())
                            .all(|(a,b)| a == b)
                    }
                }
                impl Eq for pthread_cond_t {}
                impl ::fmt::Debug for pthread_cond_t {
                    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                        f.debug_struct("pthread_cond_t")
                            // FIXME: .field("size", &self.size)
                            .finish()
                    }
                }
                impl ::hash::Hash for pthread_cond_t {
                    fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                        self.size.hash(state);
                    }
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>use num_bigint::BigInt;
use num_traits::ToPrimitive;
use std::{fmt, str::FromStr};

mod precision;
use self::precision::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 26;
pub const MAX_PRECISION: u8 = 5;

#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]
pub struct Asset {
    pub amount: i64,
}

impl Asset {
    #[inline]
    pub const fn new(amount: i64) -> Asset {
        Asset { amount }
    }

    #[inline]
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_add(other.amount)?,
        })
    }

    #[inline]
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_sub(other.amount)?,
        })
    }

    pub fn checked_mul(self, other: Self) -> Option<Self> {
        const MUL_PRECISION: u8 = MAX_PRECISION * 2;
        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, MUL_PRECISION, MAX_PRECISION)?;
        if final_mul > i128::from(::std::i64::MAX) {
            return None;
        }
        Some(Asset {
            amount: final_mul as i64,
        })
    }

    pub fn checked_div(self, other: Self) -> Option<Self> {
        if other.amount == 0 {
            return None;
        }
        const DIV_PRECISION: u8 = MAX_PRECISION * 2;
        let a = set_decimals_i64(self.amount, MAX_PRECISION, DIV_PRECISION)?;
        Some(Asset {
            amount: a.checked_div(other.amount)?,
        })
    }

    pub fn checked_pow(self, num: u16) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, MAX_PRECISION)?,
            });
        }

        let decimals = u16::from(MAX_PRECISION).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = num;
            loop {
                if exp & 1 == 1 {
                    res = &res * &base;
                }
                exp >>= 1;
                if exp == 0 {
                    break;
                };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(MAX_PRECISION));
        Some(Asset {
            amount: res.to_i64()?,
        })
    }
}

impl fmt::Debug for Asset {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Asset(\"{}\")", self.to_string())
    }
}

impl ToString for Asset {
    #[allow(clippy::comparison_chain)]
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        {
            let len = s.len();
            // See std::core::cmp for the most optimal comparisons
            if len < MAX_PRECISION as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = MAX_PRECISION as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == MAX_PRECISION as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (MAX_PRECISION as usize), '.');
            }
        }
        s.push_str(" GRAEL");
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError {
                kind: AssetErrorKind::StrTooLarge,
            });
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        match split.next() {
            Some(x) => {
                match x.find('.') {
                    Some(pos) => {
                        // Check decimal precision is correct
                        {
                            let decimals = {
                                let len = x.len() - 1;
                                if pos > 0 {
                                    (len - pos) as u8
                                } else {
                                    len as u8
                                }
                            };

                            if decimals != MAX_PRECISION {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidFormat,
                                });
                            }
                        }

                        // Actually parse the amount
                        amount = match x.replace('.', "").parse() {
                            Ok(x) => x,
                            Err(_) => {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidAmount,
                                });
                            }
                        }
                    }
                    None => {
                        return Err(AssetError {
                            kind: AssetErrorKind::InvalidFormat,
                        });
                    }
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        match split.next() {
            Some(x) => {
                if x != "GRAEL" {
                    return Err(AssetError {
                        kind: AssetErrorKind::InvalidAssetType,
                    });
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        Ok(Asset { amount })
    }
}

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

    #[test]
    fn parse_valid_input() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.amount.to_string(), amount);
        };

        c(get_asset("1.00000 GRAEL"), "100000");
        c(get_asset("-1.00000 GRAEL"), "-100000");
        c(get_asset(".10000 GRAEL"), "10000");
        c(get_asset("-.10000 GRAEL"), "-10000");
        c(get_asset("0.10000 GRAEL"), "10000");
        c(get_asset("0.00000 GRAEL"), "0");
        c(get_asset("-0.00000 GRAEL"), "0");
    }

    #[test]
    fn asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GRAEL"), "1.00001 GRAEL");
        c(get_asset("0.00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset("0.00010 GRAEL"), "0.00010 GRAEL");
        c(get_asset("-0.00001 GRAEL"), "-0.00001 GRAEL");
        c(get_asset(".00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset(".10000 GRAEL"), "0.10000 GRAEL");
        c(get_asset("1.00000 GRAEL"), "1.00000 GRAEL");
    }

    #[test]
    fn fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1b10.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("a100.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("100.0000a GRAEL", AssetErrorKind::InvalidAmount);

        c("1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1. GRAEL", AssetErrorKind::InvalidFormat);
        c(".1 GRAEL", AssetErrorKind::InvalidFormat);
        c("-.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("0.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-0.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-1.0 GRAEL", AssetErrorKind::InvalidFormat);

        c("123456789012345678901 GRAEL", AssetErrorKind::StrTooLarge);
        c("1.000000 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0000", AssetErrorKind::InvalidFormat);

        c("1.00000 GRAEL a", AssetErrorKind::InvalidAssetType);
        c("1.00000 grael", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn perform_arithmetic() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.45600 GRAEL");
        c(
            a.checked_add(get_asset("2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset("-2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset(".00001 GRAEL")).unwrap(),
            "123.45601 GRAEL",
        );
        c(
            a.checked_sub(get_asset("2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_sub(get_asset("-2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_mul(get_asset("100000.11111 GRAEL")).unwrap(),
            "12345613.71719 GRAEL",
        );
        c(
            a.checked_mul(get_asset("-100000.11111 GRAEL")).unwrap(),
            "-12345613.71719 GRAEL",
        );
        c(
            a.checked_div(get_asset("23.00000 GRAEL")).unwrap(),
            "5.36765 GRAEL",
        );
        c(
            a.checked_div(get_asset("-23.00000 GRAEL")).unwrap(),
            "-5.36765 GRAEL",
        );
        c(a.checked_pow(2).unwrap(), "15241.38393 GRAEL");
        c(a.checked_pow(3).unwrap(), "1881640.29520 GRAEL");
        c(a, "123.45600 GRAEL");

        c(
            get_asset("1.00020 GRAEL").checked_pow(1000).unwrap(),
            "1.22137 GRAEL",
        );
        c(
            get_asset("10.00000 GRAEL")
                .checked_div(get_asset("2.00000 GRAEL"))
                .unwrap(),
            "5.00000 GRAEL",
        );
        c(
            get_asset("5.00000 GRAEL")
                .checked_div(get_asset("10.00000 GRAEL"))
                .unwrap(),
            "0.50000 GRAEL",
        );

        assert!(a.checked_div(get_asset("0.00000 GRAEL")).is_none());
    }

    #[test]
    fn invalid_arithmetic() {
        let a = get_asset("10.00000 GRAEL");
        let b = get_asset("92233720368547.75807 GRAEL");

        assert_eq!(a.checked_add(b), None);
        assert_eq!(a.checked_mul(Asset::new(-1)).unwrap().checked_sub(b), None);
        assert_eq!(a.checked_div(Asset::new(0)), None);
        assert_eq!(a.checked_mul(b), None);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<commit_msg>Make the asset symbol a global constant<commit_after>use num_bigint::BigInt;
use num_traits::ToPrimitive;
use std::{fmt, str::FromStr};

mod precision;
use self::precision::*;

pub mod error;
pub use self::error::*;

pub const ASSET_SYMBOL: &str = "GRAEL";

pub const MAX_STR_LEN: usize = 26;
pub const MAX_PRECISION: u8 = 5;

#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]
pub struct Asset {
    pub amount: i64,
}

impl Asset {
    #[inline]
    pub const fn new(amount: i64) -> Asset {
        Asset { amount }
    }

    #[inline]
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_add(other.amount)?,
        })
    }

    #[inline]
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_sub(other.amount)?,
        })
    }

    pub fn checked_mul(self, other: Self) -> Option<Self> {
        const MUL_PRECISION: u8 = MAX_PRECISION * 2;
        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, MUL_PRECISION, MAX_PRECISION)?;
        if final_mul > i128::from(::std::i64::MAX) {
            return None;
        }
        Some(Asset {
            amount: final_mul as i64,
        })
    }

    pub fn checked_div(self, other: Self) -> Option<Self> {
        if other.amount == 0 {
            return None;
        }
        const DIV_PRECISION: u8 = MAX_PRECISION * 2;
        let a = set_decimals_i64(self.amount, MAX_PRECISION, DIV_PRECISION)?;
        Some(Asset {
            amount: a.checked_div(other.amount)?,
        })
    }

    pub fn checked_pow(self, num: u16) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, MAX_PRECISION)?,
            });
        }

        let decimals = u16::from(MAX_PRECISION).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = num;
            loop {
                if exp & 1 == 1 {
                    res = &res * &base;
                }
                exp >>= 1;
                if exp == 0 {
                    break;
                };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(MAX_PRECISION));
        Some(Asset {
            amount: res.to_i64()?,
        })
    }
}

impl fmt::Debug for Asset {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Asset(\"{}\")", self.to_string())
    }
}

impl ToString for Asset {
    #[allow(clippy::comparison_chain)]
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        {
            let len = s.len();
            // See std::core::cmp for the most optimal comparisons
            if len < MAX_PRECISION as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = MAX_PRECISION as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == MAX_PRECISION as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (MAX_PRECISION as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(ASSET_SYMBOL);
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError {
                kind: AssetErrorKind::StrTooLarge,
            });
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        match split.next() {
            Some(x) => {
                match x.find('.') {
                    Some(pos) => {
                        // Check decimal precision is correct
                        {
                            let decimals = {
                                let len = x.len() - 1;
                                if pos > 0 {
                                    (len - pos) as u8
                                } else {
                                    len as u8
                                }
                            };

                            if decimals != MAX_PRECISION {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidFormat,
                                });
                            }
                        }

                        // Actually parse the amount
                        amount = match x.replace('.', "").parse() {
                            Ok(x) => x,
                            Err(_) => {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidAmount,
                                });
                            }
                        }
                    }
                    None => {
                        return Err(AssetError {
                            kind: AssetErrorKind::InvalidFormat,
                        });
                    }
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        match split.next() {
            Some(x) => {
                if x != ASSET_SYMBOL {
                    return Err(AssetError {
                        kind: AssetErrorKind::InvalidAssetType,
                    });
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        Ok(Asset { amount })
    }
}

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

    #[test]
    fn parse_valid_input() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.amount.to_string(), amount);
        };

        c(get_asset("1.00000 GRAEL"), "100000");
        c(get_asset("-1.00000 GRAEL"), "-100000");
        c(get_asset(".10000 GRAEL"), "10000");
        c(get_asset("-.10000 GRAEL"), "-10000");
        c(get_asset("0.10000 GRAEL"), "10000");
        c(get_asset("0.00000 GRAEL"), "0");
        c(get_asset("-0.00000 GRAEL"), "0");
    }

    #[test]
    fn asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GRAEL"), "1.00001 GRAEL");
        c(get_asset("0.00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset("0.00010 GRAEL"), "0.00010 GRAEL");
        c(get_asset("-0.00001 GRAEL"), "-0.00001 GRAEL");
        c(get_asset(".00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset(".10000 GRAEL"), "0.10000 GRAEL");
        c(get_asset("1.00000 GRAEL"), "1.00000 GRAEL");
    }

    #[test]
    fn fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1b10.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("a100.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("100.0000a GRAEL", AssetErrorKind::InvalidAmount);

        c("1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1. GRAEL", AssetErrorKind::InvalidFormat);
        c(".1 GRAEL", AssetErrorKind::InvalidFormat);
        c("-.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("0.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-0.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-1.0 GRAEL", AssetErrorKind::InvalidFormat);

        c("123456789012345678901 GRAEL", AssetErrorKind::StrTooLarge);
        c("1.000000 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0000", AssetErrorKind::InvalidFormat);

        c("1.00000 GRAEL a", AssetErrorKind::InvalidAssetType);
        c("1.00000 grael", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn perform_arithmetic() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.45600 GRAEL");
        c(
            a.checked_add(get_asset("2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset("-2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset(".00001 GRAEL")).unwrap(),
            "123.45601 GRAEL",
        );
        c(
            a.checked_sub(get_asset("2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_sub(get_asset("-2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_mul(get_asset("100000.11111 GRAEL")).unwrap(),
            "12345613.71719 GRAEL",
        );
        c(
            a.checked_mul(get_asset("-100000.11111 GRAEL")).unwrap(),
            "-12345613.71719 GRAEL",
        );
        c(
            a.checked_div(get_asset("23.00000 GRAEL")).unwrap(),
            "5.36765 GRAEL",
        );
        c(
            a.checked_div(get_asset("-23.00000 GRAEL")).unwrap(),
            "-5.36765 GRAEL",
        );
        c(a.checked_pow(2).unwrap(), "15241.38393 GRAEL");
        c(a.checked_pow(3).unwrap(), "1881640.29520 GRAEL");
        c(a, "123.45600 GRAEL");

        c(
            get_asset("1.00020 GRAEL").checked_pow(1000).unwrap(),
            "1.22137 GRAEL",
        );
        c(
            get_asset("10.00000 GRAEL")
                .checked_div(get_asset("2.00000 GRAEL"))
                .unwrap(),
            "5.00000 GRAEL",
        );
        c(
            get_asset("5.00000 GRAEL")
                .checked_div(get_asset("10.00000 GRAEL"))
                .unwrap(),
            "0.50000 GRAEL",
        );

        assert!(a.checked_div(get_asset("0.00000 GRAEL")).is_none());
    }

    #[test]
    fn invalid_arithmetic() {
        let a = get_asset("10.00000 GRAEL");
        let b = get_asset("92233720368547.75807 GRAEL");

        assert_eq!(a.checked_add(b), None);
        assert_eq!(a.checked_mul(Asset::new(-1)).unwrap().checked_sub(b), None);
        assert_eq!(a.checked_div(Asset::new(0)), None);
        assert_eq!(a.checked_mul(b), None);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<|endoftext|>


<commit_before>use std::fmt;

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// DOCS
// generic?
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<B = Vec<u8>> {
    bytes: B,
}

// See https://github.com/matrix-org/matrix-doc/issues/3211
const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true);

impl<B: AsRef<[u8]>> Base64<B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes }
    }

    /// Get the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, BASE64_CONFIG)
    }
}

impl Base64 {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self { bytes: Vec::new() }
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, BASE64_CONFIG).map(Self::new)
    }
}

impl<B: AsRef<[u8]>> fmt::Debug for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<B: AsRef<[u8]>> fmt::Display for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de> Deserialize<'de> for Base64 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<B: AsRef<[u8]>> Serialize for Base64<B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<commit_msg>serde: Add Base64::into_inner<commit_after>use std::fmt;

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// DOCS
// generic?
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<B = Vec<u8>> {
    bytes: B,
}

// See https://github.com/matrix-org/matrix-doc/issues/3211
const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true);

impl<B: AsRef<[u8]>> Base64<B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes }
    }

    /// Get a reference to the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, BASE64_CONFIG)
    }
}

impl<B> Base64<B> {
    /// Get the raw bytes held by this `Base64` instance.
    pub fn into_inner(self) -> B {
        self.bytes
    }
}

impl Base64 {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self { bytes: Vec::new() }
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, BASE64_CONFIG).map(Self::new)
    }
}

impl<B: AsRef<[u8]>> fmt::Debug for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<B: AsRef<[u8]>> fmt::Display for Base64<B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de> Deserialize<'de> for Base64 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<B: AsRef<[u8]>> Serialize for Base64<B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<|endoftext|>