<commit_before>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, BytePos};
use utils::span_lint;

/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
/// outside ticks in documentation.
///
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
/// camel-case probably indicates some code which should be included between
/// ticks. `_` can also be used for empasis in markdown, this lint tries to
/// consider that.
///
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
/// for is limited, and there are still false positives.
///
/// **Examples:**
/// ```rust
/// /// Do something with the foo_bar parameter. See also that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. }
/// ```
declare_lint! {
    pub DOC_MARKDOWN,
    Warn,
    "presence of `_`, `::` or camel-case outside backticks in documentation"
}

#[derive(Clone)]
pub struct Doc {
    valid_idents: Vec<String>,
}

impl Doc {
    pub fn new(valid_idents: Vec<String>) -> Self {
        Doc { valid_idents: valid_idents }
    }
}

impl LintPass for Doc {
    fn get_lints(&self) -> LintArray {
        lint_array![DOC_MARKDOWN]
    }
}

impl EarlyLintPass for Doc {
    fn check_crate(&mut self, cx: &EarlyContext, krate: &ast::Crate) {
        check_attrs(cx, &self.valid_idents, &krate.attrs);
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
        check_attrs(cx, &self.valid_idents, &item.attrs);
    }
}

/// Cleanup documentation decoration (`///` and such).
///
/// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
/// the span but this function is inspired from the later.
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(String, Span)> {
    // one-line comments lose their prefix
    const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
    for prefix in ONELINERS {
        if comment.starts_with(*prefix) {
            return vec![(comment[prefix.len()..].to_owned(),
                         Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })];
        }
    }

    if comment.starts_with("/*") {
        return comment[3..comment.len() - 2]
            .lines()
            .map(|line| {
                let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
                debug_assert_eq!(offset as u32 as usize, offset);

                (line.to_owned(), Span { lo: span.lo + BytePos(offset as u32), ..span })
            })
            .collect();
    }

    panic!("not a doc-comment: {}", comment);
}

pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [ast::Attribute]) {
    let mut docs = vec![];

    for attr in attrs {
        if attr.is_sugared_doc {
            if let Some(ref doc) = attr.value_str() {
                let doc = doc.to_string();
                docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
            }
        }
    }

    if !docs.is_empty() {
        let _ = check_doc(cx, valid_idents, &docs);
    }
}

#[allow(while_let_loop)] // #362
fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]) -> Result<(), ()> {
    // In markdown, `_` can be used to emphasize something, or, is a raw `_` depending on context.
    // There really is no markdown specification that would disambiguate this properly. This is
    // what GitHub and Rustdoc do:
    //
    // foo_bar test_quz    → foo_bar test_quz
    // foo_bar_baz         → foo_bar_baz (note that the “official” spec says this should be emphasized)
    // _foo bar_ test_quz_ → foo bar test_quz_
    // \_foo bar\_         → _foo bar_
    // (_baz_)             → (baz)
    // foo _ bar _ baz     → foo _ bar _ baz

    /// Character that can appear in a path
    fn is_path_char(c: char) -> bool {
        match c {
            t if t.is_alphanumeric() => true,
            ':' | '_' => true,
            _ => false,
        }
    }

    #[derive(Clone, Debug)]
    /// This type is used to iterate through the documentation characters, keeping the span at the
    /// same time.
    struct Parser<'a> {
        /// First byte of the current potential match
        current_word_begin: usize,
        /// List of lines and their associated span
        docs: &'a [(String, Span)],
        /// Index of the current line we are parsing
        line: usize,
        /// Whether we are in a link
        link: bool,
        /// Whether we are at the beginning of a line
        new_line: bool,
        /// Whether we were to the end of a line last time `next` was called
        reset: bool,
        /// The position of the current character within the current line
        pos: usize,
    }

    impl<'a> Parser<'a> {
        fn advance_begin(&mut self) {
            self.current_word_begin = self.pos;
        }

        fn line(&self) -> (&'a str, Span) {
            let (ref doc, span) = self.docs[self.line];
            (doc, span)
        }

        fn peek(&self) -> Option<char> {
            self.line().0[self.pos..].chars().next()
        }

        #[allow(while_let_on_iterator)] // borrowck complains about for
        fn jump_to(&mut self, n: char) -> Result<bool, ()> {
            while let Some((new_line, c)) = self.next() {
                if c == n {
                    self.advance_begin();
                    return Ok(new_line);
                }
            }

            Err(())
        }

        fn next_line(&mut self) {
            self.pos = 0;
            self.current_word_begin = 0;
            self.line += 1;
            self.new_line = true;
        }

        fn put_back(&mut self, c: char) {
            self.pos -= c.len_utf8();
        }

        #[allow(cast_possible_truncation)]
        fn word(&self) -> (&'a str, Span) {
            let begin = self.current_word_begin;
            let end = self.pos;

            debug_assert_eq!(end as u32 as usize, end);
            debug_assert_eq!(begin as u32 as usize, begin);

            let (doc, mut span) = self.line();
            span.hi = span.lo + BytePos(end as u32);
            span.lo = span.lo + BytePos(begin as u32);

            (&doc[begin..end], span)
        }
    }

    impl<'a> Iterator for Parser<'a> {
        type Item = (bool, char);

        fn next(&mut self) -> Option<(bool, char)> {
            while self.line < self.docs.len() {
                if self.reset {
                    self.line += 1;
                    self.reset = false;
                    self.pos = 0;
                    self.current_word_begin = 0;
                }

                let mut chars = self.line().0[self.pos..].chars();
                let c = chars.next();

                if let Some(c) = c {
                    self.pos += c.len_utf8();
                    let new_line = self.new_line;
                    self.new_line = c == '\n' || (self.new_line && c.is_whitespace());
                    return Some((new_line, c));
                } else if self.line == self.docs.len() - 1 {
                    return None;
                } else {
                    self.new_line = true;
                    self.reset = true;
                    self.pos += 1;
                    return Some((true, '\n'));
                }
            }

            None
        }
    }

    let mut parser = Parser {
        current_word_begin: 0,
        docs: docs,
        line: 0,
        link: false,
        new_line: true,
        reset: false,
        pos: 0,
    };

    /// Check for fanced code block.
    macro_rules! check_block {
        ($parser:expr, $c:tt, $new_line:expr) => {{
            check_block!($parser, $c, $c, $new_line)
        }};

        ($parser:expr, $c:pat, $c_expr:expr, $new_line:expr) => {{
            fn check_block(parser: &mut Parser, new_line: bool) -> Result<bool, ()> {
                if new_line {
                    let mut lookup_parser = parser.clone();
                    if let (Some((false, $c)), Some((false, $c))) = (lookup_parser.next(), lookup_parser.next()) {
                        *parser = lookup_parser;
                        // 3 or more ` or ~ open a code block to be closed with the same number of ` or ~
                        let mut open_count = 3;
                        while let Some((false, $c)) = parser.next() {
                            open_count += 1;
                        }

                        loop {
                            loop {
                                if try!(parser.jump_to($c_expr)) {
                                    break;
                                }
                            }

                            lookup_parser = parser.clone();
                            let a = lookup_parser.next();
                            let b = lookup_parser.next();
                            if let (Some((false, $c)), Some((false, $c))) = (a, b) {
                                let mut close_count = 3;
                                while let Some((false, $c)) = lookup_parser.next() {
                                    close_count += 1;
                                }

                                if close_count == open_count {
                                    *parser = lookup_parser;
                                    return Ok(true);
                                }
                            }
                        }
                    }
                }

                Ok(false)
            }

            check_block(&mut $parser, $new_line)
        }};
    }

    loop {
        match parser.next() {
            Some((new_line, c)) => {
                match c {
                    '#' if new_line => {
                        // don’t warn on titles
                        parser.next_line();
                    },
                    '`' => {
                        if try!(check_block!(parser, '`', new_line)) {
                            continue;
                        }

                        // not a code block, just inline code
                        try!(parser.jump_to('`'));
                    },
                    '~' => {
                        if try!(check_block!(parser, '~', new_line)) {
                            continue;
                        }

                        // ~ does not introduce inline code, but two of them introduce
                        // strikethrough. Too bad for the consistency but we don't care about
                        // strikethrough.
                    },
                    '[' => {
                        // Check for a reference definition `[foo]:` at the beginning of a line
                        let mut link = true;

                        if new_line {
                            let mut lookup_parser = parser.clone();
                            if lookup_parser.any(|(_, c)| c == ']') {
                                if let Some((_, ':')) = lookup_parser.next() {
                                    lookup_parser.next_line();
                                    parser = lookup_parser;
                                    link = false;
                                }
                            }
                        }

                        parser.advance_begin();
                        parser.link = link;
                    },
                    ']' if parser.link => {
                        parser.link = false;

                        match parser.peek() {
                            Some('(') => {
                                try!(parser.jump_to(')'));
                            },
                            Some('[') => {
                                try!(parser.jump_to(']'));
                            },
                            Some(_) => continue,
                            None => return Err(()),
                        }
                    },
                    c if !is_path_char(c) => {
                        parser.advance_begin();
                    },
                    _ => {
                        if let Some((_, c)) = parser.find(|&(_, c)| !is_path_char(c)) {
                            parser.put_back(c);
                        }

                        let (word, span) = parser.word();
                        check_word(cx, valid_idents, word, span);
                        parser.advance_begin();
                    },
                }

            },
            None => break,
        }
    }

    Ok(())
}

fn check_word(cx: &EarlyContext, valid_idents: &[String], word: &str, span: Span) {
    /// Checks if a string a camel-case, ie. contains at least two uppercase letter (`Clippy` is
    /// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded (`IDs` is ok).
    fn is_camel_case(s: &str) -> bool {
        if s.starts_with(|c: char| c.is_digit(10)) {
            return false;
        }

        let s = if s.ends_with('s') {
            &s[..s.len() - 1]
        } else {
            s
        };

        s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
        s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
    }

    fn has_underscore(s: &str) -> bool {
        s != "_" && !s.contains("\\_") && s.contains('_')
    }

    // Trim punctuation as in `some comment (see foo::bar).`
    //                                                   ^^
    // Or even as in `_foo bar_` which is emphasized.
    let word = word.trim_matches(|c: char| !c.is_alphanumeric());

    if valid_idents.iter().any(|i| i == word) {
        return;
    }

    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
        span_lint(cx,
                  DOC_MARKDOWN,
                  span,
                  &format!("you should put `{}` between ticks in the documentation", word));
    }
}
<commit_msg>fix another never_loop<commit_after>use rustc::lint::*;
use syntax::ast;
use syntax::codemap::{Span, BytePos};
use utils::span_lint;

/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
/// outside ticks in documentation.
///
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
/// camel-case probably indicates some code which should be included between
/// ticks. `_` can also be used for empasis in markdown, this lint tries to
/// consider that.
///
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
/// for is limited, and there are still false positives.
///
/// **Examples:**
/// ```rust
/// /// Do something with the foo_bar parameter. See also that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. }
/// ```
declare_lint! {
    pub DOC_MARKDOWN,
    Warn,
    "presence of `_`, `::` or camel-case outside backticks in documentation"
}

#[derive(Clone)]
pub struct Doc {
    valid_idents: Vec<String>,
}

impl Doc {
    pub fn new(valid_idents: Vec<String>) -> Self {
        Doc { valid_idents: valid_idents }
    }
}

impl LintPass for Doc {
    fn get_lints(&self) -> LintArray {
        lint_array![DOC_MARKDOWN]
    }
}

impl EarlyLintPass for Doc {
    fn check_crate(&mut self, cx: &EarlyContext, krate: &ast::Crate) {
        check_attrs(cx, &self.valid_idents, &krate.attrs);
    }

    fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
        check_attrs(cx, &self.valid_idents, &item.attrs);
    }
}

/// Cleanup documentation decoration (`///` and such).
///
/// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
/// the span but this function is inspired from the later.
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(String, Span)> {
    // one-line comments lose their prefix
    const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
    for prefix in ONELINERS {
        if comment.starts_with(*prefix) {
            return vec![(comment[prefix.len()..].to_owned(),
                         Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })];
        }
    }

    if comment.starts_with("/*") {
        return comment[3..comment.len() - 2]
            .lines()
            .map(|line| {
                let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
                debug_assert_eq!(offset as u32 as usize, offset);

                (line.to_owned(), Span { lo: span.lo + BytePos(offset as u32), ..span })
            })
            .collect();
    }

    panic!("not a doc-comment: {}", comment);
}

pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [ast::Attribute]) {
    let mut docs = vec![];

    for attr in attrs {
        if attr.is_sugared_doc {
            if let Some(ref doc) = attr.value_str() {
                let doc = doc.to_string();
                docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
            }
        }
    }

    if !docs.is_empty() {
        let _ = check_doc(cx, valid_idents, &docs);
    }
}

#[allow(while_let_loop)] // #362
fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]) -> Result<(), ()> {
    // In markdown, `_` can be used to emphasize something, or, is a raw `_` depending on context.
    // There really is no markdown specification that would disambiguate this properly. This is
    // what GitHub and Rustdoc do:
    //
    // foo_bar test_quz    → foo_bar test_quz
    // foo_bar_baz         → foo_bar_baz (note that the “official” spec says this should be emphasized)
    // _foo bar_ test_quz_ → foo bar test_quz_
    // \_foo bar\_         → _foo bar_
    // (_baz_)             → (baz)
    // foo _ bar _ baz     → foo _ bar _ baz

    /// Character that can appear in a path
    fn is_path_char(c: char) -> bool {
        match c {
            t if t.is_alphanumeric() => true,
            ':' | '_' => true,
            _ => false,
        }
    }

    #[derive(Clone, Debug)]
    /// This type is used to iterate through the documentation characters, keeping the span at the
    /// same time.
    struct Parser<'a> {
        /// First byte of the current potential match
        current_word_begin: usize,
        /// List of lines and their associated span
        docs: &'a [(String, Span)],
        /// Index of the current line we are parsing
        line: usize,
        /// Whether we are in a link
        link: bool,
        /// Whether we are at the beginning of a line
        new_line: bool,
        /// Whether we were to the end of a line last time `next` was called
        reset: bool,
        /// The position of the current character within the current line
        pos: usize,
    }

    impl<'a> Parser<'a> {
        fn advance_begin(&mut self) {
            self.current_word_begin = self.pos;
        }

        fn line(&self) -> (&'a str, Span) {
            let (ref doc, span) = self.docs[self.line];
            (doc, span)
        }

        fn peek(&self) -> Option<char> {
            self.line().0[self.pos..].chars().next()
        }

        #[allow(while_let_on_iterator)] // borrowck complains about for
        fn jump_to(&mut self, n: char) -> Result<bool, ()> {
            while let Some((new_line, c)) = self.next() {
                if c == n {
                    self.advance_begin();
                    return Ok(new_line);
                }
            }

            Err(())
        }

        fn next_line(&mut self) {
            self.pos = 0;
            self.current_word_begin = 0;
            self.line += 1;
            self.new_line = true;
        }

        fn put_back(&mut self, c: char) {
            self.pos -= c.len_utf8();
        }

        #[allow(cast_possible_truncation)]
        fn word(&self) -> (&'a str, Span) {
            let begin = self.current_word_begin;
            let end = self.pos;

            debug_assert_eq!(end as u32 as usize, end);
            debug_assert_eq!(begin as u32 as usize, begin);

            let (doc, mut span) = self.line();
            span.hi = span.lo + BytePos(end as u32);
            span.lo = span.lo + BytePos(begin as u32);

            (&doc[begin..end], span)
        }
    }

    impl<'a> Iterator for Parser<'a> {
        type Item = (bool, char);

        fn next(&mut self) -> Option<(bool, char)> {
            if self.line < self.docs.len() {
                if self.reset {
                    self.line += 1;
                    self.reset = false;
                    self.pos = 0;
                    self.current_word_begin = 0;
                }

                let mut chars = self.line().0[self.pos..].chars();
                let c = chars.next();

                if let Some(c) = c {
                    self.pos += c.len_utf8();
                    let new_line = self.new_line;
                    self.new_line = c == '\n' || (self.new_line && c.is_whitespace());
                    Some((new_line, c))
                } else if self.line == self.docs.len() - 1 {
                    None
                } else {
                    self.new_line = true;
                    self.reset = true;
                    self.pos += 1;
                    Some((true, '\n'))
                }
            } else {
                None
            }
        }
    }

    let mut parser = Parser {
        current_word_begin: 0,
        docs: docs,
        line: 0,
        link: false,
        new_line: true,
        reset: false,
        pos: 0,
    };

    /// Check for fanced code block.
    macro_rules! check_block {
        ($parser:expr, $c:tt, $new_line:expr) => {{
            check_block!($parser, $c, $c, $new_line)
        }};

        ($parser:expr, $c:pat, $c_expr:expr, $new_line:expr) => {{
            fn check_block(parser: &mut Parser, new_line: bool) -> Result<bool, ()> {
                if new_line {
                    let mut lookup_parser = parser.clone();
                    if let (Some((false, $c)), Some((false, $c))) = (lookup_parser.next(), lookup_parser.next()) {
                        *parser = lookup_parser;
                        // 3 or more ` or ~ open a code block to be closed with the same number of ` or ~
                        let mut open_count = 3;
                        while let Some((false, $c)) = parser.next() {
                            open_count += 1;
                        }

                        loop {
                            loop {
                                if try!(parser.jump_to($c_expr)) {
                                    break;
                                }
                            }

                            lookup_parser = parser.clone();
                            let a = lookup_parser.next();
                            let b = lookup_parser.next();
                            if let (Some((false, $c)), Some((false, $c))) = (a, b) {
                                let mut close_count = 3;
                                while let Some((false, $c)) = lookup_parser.next() {
                                    close_count += 1;
                                }

                                if close_count == open_count {
                                    *parser = lookup_parser;
                                    return Ok(true);
                                }
                            }
                        }
                    }
                }

                Ok(false)
            }

            check_block(&mut $parser, $new_line)
        }};
    }

    loop {
        match parser.next() {
            Some((new_line, c)) => {
                match c {
                    '#' if new_line => {
                        // don’t warn on titles
                        parser.next_line();
                    },
                    '`' => {
                        if try!(check_block!(parser, '`', new_line)) {
                            continue;
                        }

                        // not a code block, just inline code
                        try!(parser.jump_to('`'));
                    },
                    '~' => {
                        if try!(check_block!(parser, '~', new_line)) {
                            continue;
                        }

                        // ~ does not introduce inline code, but two of them introduce
                        // strikethrough. Too bad for the consistency but we don't care about
                        // strikethrough.
                    },
                    '[' => {
                        // Check for a reference definition `[foo]:` at the beginning of a line
                        let mut link = true;

                        if new_line {
                            let mut lookup_parser = parser.clone();
                            if lookup_parser.any(|(_, c)| c == ']') {
                                if let Some((_, ':')) = lookup_parser.next() {
                                    lookup_parser.next_line();
                                    parser = lookup_parser;
                                    link = false;
                                }
                            }
                        }

                        parser.advance_begin();
                        parser.link = link;
                    },
                    ']' if parser.link => {
                        parser.link = false;

                        match parser.peek() {
                            Some('(') => {
                                try!(parser.jump_to(')'));
                            },
                            Some('[') => {
                                try!(parser.jump_to(']'));
                            },
                            Some(_) => continue,
                            None => return Err(()),
                        }
                    },
                    c if !is_path_char(c) => {
                        parser.advance_begin();
                    },
                    _ => {
                        if let Some((_, c)) = parser.find(|&(_, c)| !is_path_char(c)) {
                            parser.put_back(c);
                        }

                        let (word, span) = parser.word();
                        check_word(cx, valid_idents, word, span);
                        parser.advance_begin();
                    },
                }

            },
            None => break,
        }
    }

    Ok(())
}

fn check_word(cx: &EarlyContext, valid_idents: &[String], word: &str, span: Span) {
    /// Checks if a string a camel-case, ie. contains at least two uppercase letter (`Clippy` is
    /// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded (`IDs` is ok).
    fn is_camel_case(s: &str) -> bool {
        if s.starts_with(|c: char| c.is_digit(10)) {
            return false;
        }

        let s = if s.ends_with('s') {
            &s[..s.len() - 1]
        } else {
            s
        };

        s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
        s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
    }

    fn has_underscore(s: &str) -> bool {
        s != "_" && !s.contains("\\_") && s.contains('_')
    }

    // Trim punctuation as in `some comment (see foo::bar).`
    //                                                   ^^
    // Or even as in `_foo bar_` which is emphasized.
    let word = word.trim_matches(|c: char| !c.is_alphanumeric());

    if valid_idents.iter().any(|i| i == word) {
        return;
    }

    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
        span_lint(cx,
                  DOC_MARKDOWN,
                  span,
                  &format!("you should put `{}` between ticks in the documentation", word));
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::all, rust_2018_idioms)]

use chrono::prelude::Utc;
use hmac::{Hmac, Mac};
use reqwest::{
    blocking::{Body, Client, Response},
    header,
};
use sha1::Sha1;
use std::time::Duration;

pub use reqwest::Error;

#[derive(Clone, Debug)]
pub struct Bucket {
    name: String,
    region: Option<String>,
    access_key: String,
    secret_key: String,
    proto: String,
}

impl Bucket {
    pub fn new(
        name: String,
        region: Option<String>,
        access_key: String,
        secret_key: String,
        proto: &str,
    ) -> Bucket {
        Bucket {
            name,
            region,
            access_key,
            secret_key,
            proto: proto.to_string(),
        }
    }

    pub fn put<R: std::io::Read + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        content: R,
        content_length: u64,
        content_type: &str,
        extra_headers: header::HeaderMap,
    ) -> Result<Response, Error> {
        let path = path.strip_prefix('/').unwrap_or(path);
        let date = Utc::now().to_rfc2822();
        let auth = self.auth("PUT", &date, path, "", content_type);
        let url = self.url(path);

        client
            .put(&url)
            .header(header::AUTHORIZATION, auth)
            .header(header::CONTENT_TYPE, content_type)
            .header(header::DATE, date)
            .header(header::USER_AGENT, "crates.io (https://crates.io)")
            .headers(extra_headers)
            .body(Body::sized(content, content_length))
            .timeout(Duration::from_secs(60))
            .send()?
            .error_for_status()
            .map_err(Into::into)
    }

    pub fn delete(&self, client: &Client, path: &str) -> Result<Response, Error> {
        let path = path.strip_prefix('/').unwrap_or(path);
        let date = Utc::now().to_rfc2822();
        let auth = self.auth("DELETE", &date, path, "", "");
        let url = self.url(path);

        client
            .delete(&url)
            .header(header::DATE, date)
            .header(header::AUTHORIZATION, auth)
            .send()?
            .error_for_status()
            .map_err(Into::into)
    }

    pub fn host(&self) -> String {
        format!(
            "{}.s3{}.amazonaws.com",
            self.name,
            match self.region {
                Some(ref r) if !r.is_empty() => format!("-{r}"),
                Some(_) => String::new(),
                None => String::new(),
            }
        )
    }

    fn auth(&self, verb: &str, date: &str, path: &str, md5: &str, content_type: &str) -> String {
        let string = format!(
            "{verb}\n{md5}\n{ty}\n{date}\n{headers}/{name}/{path}",
            ty = content_type,
            headers = "",
            name = self.name,
        );
        let signature = {
            let key = self.secret_key.as_bytes();
            let mut h = Hmac::<Sha1>::new_from_slice(key).expect("HMAC can take key of any size");
            h.update(string.as_bytes());
            let res = h.finalize().into_bytes();
            base64::encode(&res)
        };
        format!("AWS {}:{}", self.access_key, signature)
    }

    fn url(&self, path: &str) -> String {
        format!("{}://{}/{}", self.proto, self.host(), path)
    }
}
<commit_msg>s3: Fix `base64::encode()` call (#5411)<commit_after>#![warn(clippy::all, rust_2018_idioms)]

use chrono::prelude::Utc;
use hmac::{Hmac, Mac};
use reqwest::{
    blocking::{Body, Client, Response},
    header,
};
use sha1::Sha1;
use std::time::Duration;

pub use reqwest::Error;

#[derive(Clone, Debug)]
pub struct Bucket {
    name: String,
    region: Option<String>,
    access_key: String,
    secret_key: String,
    proto: String,
}

impl Bucket {
    pub fn new(
        name: String,
        region: Option<String>,
        access_key: String,
        secret_key: String,
        proto: &str,
    ) -> Bucket {
        Bucket {
            name,
            region,
            access_key,
            secret_key,
            proto: proto.to_string(),
        }
    }

    pub fn put<R: std::io::Read + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        content: R,
        content_length: u64,
        content_type: &str,
        extra_headers: header::HeaderMap,
    ) -> Result<Response, Error> {
        let path = path.strip_prefix('/').unwrap_or(path);
        let date = Utc::now().to_rfc2822();
        let auth = self.auth("PUT", &date, path, "", content_type);
        let url = self.url(path);

        client
            .put(&url)
            .header(header::AUTHORIZATION, auth)
            .header(header::CONTENT_TYPE, content_type)
            .header(header::DATE, date)
            .header(header::USER_AGENT, "crates.io (https://crates.io)")
            .headers(extra_headers)
            .body(Body::sized(content, content_length))
            .timeout(Duration::from_secs(60))
            .send()?
            .error_for_status()
            .map_err(Into::into)
    }

    pub fn delete(&self, client: &Client, path: &str) -> Result<Response, Error> {
        let path = path.strip_prefix('/').unwrap_or(path);
        let date = Utc::now().to_rfc2822();
        let auth = self.auth("DELETE", &date, path, "", "");
        let url = self.url(path);

        client
            .delete(&url)
            .header(header::DATE, date)
            .header(header::AUTHORIZATION, auth)
            .send()?
            .error_for_status()
            .map_err(Into::into)
    }

    pub fn host(&self) -> String {
        format!(
            "{}.s3{}.amazonaws.com",
            self.name,
            match self.region {
                Some(ref r) if !r.is_empty() => format!("-{r}"),
                Some(_) => String::new(),
                None => String::new(),
            }
        )
    }

    fn auth(&self, verb: &str, date: &str, path: &str, md5: &str, content_type: &str) -> String {
        let string = format!(
            "{verb}\n{md5}\n{ty}\n{date}\n{headers}/{name}/{path}",
            ty = content_type,
            headers = "",
            name = self.name,
        );
        let signature = {
            let key = self.secret_key.as_bytes();
            let mut h = Hmac::<Sha1>::new_from_slice(key).expect("HMAC can take key of any size");
            h.update(string.as_bytes());
            let res = h.finalize().into_bytes();
            base64::encode(res)
        };
        format!("AWS {}:{}", self.access_key, signature)
    }

    fn url(&self, path: &str) -> String {
        format!("{}://{}/{}", self.proto, self.host(), path)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    smoke_test_environment::SmokeTestEnvironment,
    test_utils::{
        compare_balances, load_backend_storage, load_libra_root_storage,
        setup_swarm_and_client_proxy,
    },
};
use libra_config::config::NodeConfig;
use libra_global_constants::{
    CONSENSUS_KEY, OPERATOR_ACCOUNT, OPERATOR_KEY, VALIDATOR_NETWORK_KEY,
};
use libra_management::storage::to_x25519;
use libra_operational_tool::test_helper::OperationalTool;
use libra_secure_json_rpc::VMStatusView;
use libra_secure_storage::{CryptoStorage, KVStorage, Storage};
use libra_types::{
    account_address::AccountAddress,
    account_config::{testnet_dd_account_address, treasury_compliance_account_address},
    chain_id::ChainId,
    transaction::authenticator::AuthenticationKey,
};
use std::convert::{TryFrom, TryInto};

#[test]
fn test_consensus_key_rotation() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the consensus key
    let (txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the config has been updated correctly with the new consensus key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_consensus_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .consensus_public_key;
    assert_eq!(new_consensus_key, config_consensus_key);

    // Verify that the validator set info contains the new consensus key
    let info_consensus_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .consensus_public_key
        .clone();
    assert_eq!(new_consensus_key, info_consensus_key);

    // Rotate the consensus key in storage manually and perform another rotation using the op_tool.
    // Here, we expected the op_tool to see that the consensus key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let mut storage: Storage = (&backend).try_into().unwrap();
    let rotated_consensus_key = storage.rotate_key(CONSENSUS_KEY).unwrap();
    let (_txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    assert_eq!(rotated_consensus_key, new_consensus_key);
}

#[test]
fn test_e2e_reconfiguration() {
    let (swarm, mut client_proxy_1) = setup_swarm_and_client_proxy(3, 1);
    let mut client_proxy_0 = swarm.get_validator_client(0, None);

    // Mint some coins and verify the balances have been updated
    client_proxy_1.create_next_account(false).unwrap();
    client_proxy_0.set_accounts(client_proxy_1.copy_all_accounts());
    client_proxy_1
        .mint_coins(&["mintb", "0", "10", "Coin1"], true)
        .unwrap();
    client_proxy_1
        .wait_for_transaction(treasury_compliance_account_address(), 1)
        .unwrap();
    assert!(compare_balances(
        vec![(10.0, "Coin1".to_string())],
        client_proxy_1.get_balances(&["b", "0"]).unwrap(),
    ));

    // Wait for the mint transaction to be processed by node 0
    client_proxy_0
        .wait_for_transaction(testnet_dd_account_address(), 1)
        .unwrap();
    assert!(compare_balances(
        vec![(10.0, "Coin1".to_string())],
        client_proxy_0.get_balances(&["b", "0"]).unwrap(),
    ));

    // Connect the operator tool to the second node's JSON RPC API
    let op_tool = swarm.get_op_tool(1);

    // Load libra root's on disk storage
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();
    let libra_root = load_libra_root_storage(&node_config);

    // Remove node 0
    let peer_id = swarm.get_validator(0).unwrap().validator_peer_id().unwrap();
    let context = op_tool.remove_validator(peer_id, &libra_root).unwrap();
    client_proxy_1
        .wait_for_transaction(context.address, context.sequence_number + 1)
        .unwrap();

    // Mint another 10 coins after removing node 0
    client_proxy_1
        .mint_coins(&["mintb", "0", "10", "Coin1"], true)
        .unwrap();
    assert!(compare_balances(
        vec![(20.0, "Coin1".to_string())],
        client_proxy_1.get_balances(&["b", "0"]).unwrap(),
    ));
    assert!(compare_balances(
        vec![(10.0, "Coin1".to_string())],
        client_proxy_0.get_balances(&["b", "0"]).unwrap(),
    ));

    // Add the node back
    let context = op_tool.add_validator(peer_id, &libra_root).unwrap();
    client_proxy_0
        .wait_for_transaction(context.address, context.sequence_number)
        .unwrap();

    // Wait for the node to catch up
    client_proxy_0
        .wait_for_transaction(testnet_dd_account_address(), 2)
        .unwrap();
    assert!(compare_balances(
        vec![(20.0, "Coin1".to_string())],
        client_proxy_0.get_balances(&["b", "0"]).unwrap(),
    ));
}

#[test]
fn test_network_key_rotation() {
    let num_nodes = 5;
    let mut swarm = SmokeTestEnvironment::new(num_nodes);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the validator network key
    let (txn_ctx, new_network_key) = op_tool.rotate_validator_network_key(&backend).unwrap();
    wait_for_transaction_on_all_nodes(
        &swarm,
        num_nodes,
        txn_ctx.address,
        txn_ctx.sequence_number + 1,
    );

    // Verify that config has been loaded correctly with new key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_network_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, config_network_key);

    // Verify that the validator set info contains the new network key
    let info_network_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, info_network_key);

    // Restart validator
    // At this point, the `add_node` call ensures connectivity to all nodes
    swarm.validator_swarm.kill_node(0);
    swarm.validator_swarm.add_node(0).unwrap();
}

#[test]
fn test_network_key_rotation_recovery() {
    let num_nodes = 5;
    let mut swarm = SmokeTestEnvironment::new(num_nodes);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the network key in storage manually and perform a key rotation using the op_tool.
    // Here, we expected the op_tool to see that the network key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let mut storage: Storage = (&backend).try_into().unwrap();
    let rotated_network_key = storage.rotate_key(VALIDATOR_NETWORK_KEY).unwrap();
    let (txn_ctx, new_network_key) = op_tool.rotate_validator_network_key(&backend).unwrap();
    assert_eq!(new_network_key, to_x25519(rotated_network_key).unwrap());

    // Ensure all nodes have received the transaction
    wait_for_transaction_on_all_nodes(
        &swarm,
        num_nodes,
        txn_ctx.address,
        txn_ctx.sequence_number + 1,
    );

    // Verify that config has been loaded correctly with new key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_network_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, config_network_key);

    // Verify that the validator set info contains the new network key
    let info_network_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, info_network_key);

    // Restart validator
    // At this point, the `add_node` call ensures connectivity to all nodes
    swarm.validator_swarm.kill_node(0);
    swarm.validator_swarm.add_node(0).unwrap();
}

#[test]
fn test_operator_key_rotation() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = OperationalTool::new(
        format!("http://127.0.0.1:{}", node_config.rpc.address.port()),
        ChainId::test(),
    );

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    let (txn_ctx, _) = op_tool.rotate_operator_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Rotate the consensus key to verify the operator key has been updated
    let (txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the config has been updated correctly with the new consensus key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_consensus_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .consensus_public_key;
    assert_eq!(new_consensus_key, config_consensus_key);
}

#[test]
fn test_operator_key_rotation_recovery() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = OperationalTool::new(
        format!("http://127.0.0.1:{}", node_config.rpc.address.port()),
        ChainId::test(),
    );

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the operator key
    let (txn_ctx, new_operator_key) = op_tool.rotate_operator_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Verify that the operator key was updated on-chain
    let mut storage: Storage = (&backend).try_into().unwrap();
    let operator_account = storage
        .get::<AccountAddress>(OPERATOR_ACCOUNT)
        .unwrap()
        .value;
    let account_resource = op_tool.account_resource(operator_account).unwrap();
    let on_chain_operator_key = hex::decode(account_resource.authentication_key).unwrap();
    assert_eq!(
        AuthenticationKey::ed25519(&new_operator_key),
        AuthenticationKey::try_from(on_chain_operator_key).unwrap()
    );

    // Rotate the operator key in storage manually and perform another rotation using the op tool.
    // Here, we expected the op_tool to see that the operator key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let rotated_operator_key = storage.rotate_key(OPERATOR_KEY).unwrap();
    let (txn_ctx, new_operator_key) = op_tool.rotate_operator_key(&backend).unwrap();
    assert_eq!(rotated_operator_key, new_operator_key);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Verify that the operator key was updated on-chain
    let account_resource = op_tool.account_resource(operator_account).unwrap();
    let on_chain_operator_key = hex::decode(account_resource.authentication_key).unwrap();
    assert_eq!(
        AuthenticationKey::ed25519(&new_operator_key),
        AuthenticationKey::try_from(on_chain_operator_key).unwrap()
    );
}

fn wait_for_transaction_on_all_nodes(
    swarm: &SmokeTestEnvironment,
    num_nodes: usize,
    account: AccountAddress,
    sequence_number: u64,
) {
    for i in 0..num_nodes {
        let mut client = swarm.get_validator_client(i, None);
        client
            .wait_for_transaction(account, sequence_number)
            .unwrap();
    }
}
<commit_msg>[Smoke Test] Remove e2e_reconfiguration test<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{smoke_test_environment::SmokeTestEnvironment, test_utils::load_backend_storage};
use libra_config::config::NodeConfig;
use libra_global_constants::{
    CONSENSUS_KEY, OPERATOR_ACCOUNT, OPERATOR_KEY, VALIDATOR_NETWORK_KEY,
};
use libra_management::storage::to_x25519;
use libra_operational_tool::test_helper::OperationalTool;
use libra_secure_json_rpc::VMStatusView;
use libra_secure_storage::{CryptoStorage, KVStorage, Storage};
use libra_types::{
    account_address::AccountAddress, chain_id::ChainId,
    transaction::authenticator::AuthenticationKey,
};
use std::convert::{TryFrom, TryInto};

#[test]
fn test_consensus_key_rotation() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the consensus key
    let (txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the config has been updated correctly with the new consensus key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_consensus_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .consensus_public_key;
    assert_eq!(new_consensus_key, config_consensus_key);

    // Verify that the validator set info contains the new consensus key
    let info_consensus_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .consensus_public_key
        .clone();
    assert_eq!(new_consensus_key, info_consensus_key);

    // Rotate the consensus key in storage manually and perform another rotation using the op_tool.
    // Here, we expected the op_tool to see that the consensus key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let mut storage: Storage = (&backend).try_into().unwrap();
    let rotated_consensus_key = storage.rotate_key(CONSENSUS_KEY).unwrap();
    let (_txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    assert_eq!(rotated_consensus_key, new_consensus_key);
}

#[test]
fn test_network_key_rotation() {
    let num_nodes = 5;
    let mut swarm = SmokeTestEnvironment::new(num_nodes);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the validator network key
    let (txn_ctx, new_network_key) = op_tool.rotate_validator_network_key(&backend).unwrap();
    wait_for_transaction_on_all_nodes(
        &swarm,
        num_nodes,
        txn_ctx.address,
        txn_ctx.sequence_number + 1,
    );

    // Verify that config has been loaded correctly with new key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_network_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, config_network_key);

    // Verify that the validator set info contains the new network key
    let info_network_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, info_network_key);

    // Restart validator
    // At this point, the `add_node` call ensures connectivity to all nodes
    swarm.validator_swarm.kill_node(0);
    swarm.validator_swarm.add_node(0).unwrap();
}

#[test]
fn test_network_key_rotation_recovery() {
    let num_nodes = 5;
    let mut swarm = SmokeTestEnvironment::new(num_nodes);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = swarm.get_op_tool(0);

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the network key in storage manually and perform a key rotation using the op_tool.
    // Here, we expected the op_tool to see that the network key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let mut storage: Storage = (&backend).try_into().unwrap();
    let rotated_network_key = storage.rotate_key(VALIDATOR_NETWORK_KEY).unwrap();
    let (txn_ctx, new_network_key) = op_tool.rotate_validator_network_key(&backend).unwrap();
    assert_eq!(new_network_key, to_x25519(rotated_network_key).unwrap());

    // Ensure all nodes have received the transaction
    wait_for_transaction_on_all_nodes(
        &swarm,
        num_nodes,
        txn_ctx.address,
        txn_ctx.sequence_number + 1,
    );

    // Verify that config has been loaded correctly with new key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_network_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, config_network_key);

    // Verify that the validator set info contains the new network key
    let info_network_key = op_tool.validator_set(validator_account, &backend).unwrap()[0]
        .validator_network_address
        .find_noise_proto()
        .unwrap();
    assert_eq!(new_network_key, info_network_key);

    // Restart validator
    // At this point, the `add_node` call ensures connectivity to all nodes
    swarm.validator_swarm.kill_node(0);
    swarm.validator_swarm.add_node(0).unwrap();
}

#[test]
fn test_operator_key_rotation() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = OperationalTool::new(
        format!("http://127.0.0.1:{}", node_config.rpc.address.port()),
        ChainId::test(),
    );

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    let (txn_ctx, _) = op_tool.rotate_operator_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Rotate the consensus key to verify the operator key has been updated
    let (txn_ctx, new_consensus_key) = op_tool.rotate_consensus_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the config has been updated correctly with the new consensus key
    let validator_account = node_config.validator_network.as_ref().unwrap().peer_id();
    let config_consensus_key = op_tool
        .validator_config(validator_account, &backend)
        .unwrap()
        .consensus_public_key;
    assert_eq!(new_consensus_key, config_consensus_key);
}

#[test]
fn test_operator_key_rotation_recovery() {
    let mut swarm = SmokeTestEnvironment::new(5);
    swarm.validator_swarm.launch();

    // Load a node config
    let node_config =
        NodeConfig::load(swarm.validator_swarm.config.config_files.first().unwrap()).unwrap();

    // Connect the operator tool to the first node's JSON RPC API
    let op_tool = OperationalTool::new(
        format!("http://127.0.0.1:{}", node_config.rpc.address.port()),
        ChainId::test(),
    );

    // Load validator's on disk storage
    let backend = load_backend_storage(&node_config);

    // Rotate the operator key
    let (txn_ctx, new_operator_key) = op_tool.rotate_operator_key(&backend).unwrap();
    let mut client = swarm.get_validator_client(0, None);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Verify that the operator key was updated on-chain
    let mut storage: Storage = (&backend).try_into().unwrap();
    let operator_account = storage
        .get::<AccountAddress>(OPERATOR_ACCOUNT)
        .unwrap()
        .value;
    let account_resource = op_tool.account_resource(operator_account).unwrap();
    let on_chain_operator_key = hex::decode(account_resource.authentication_key).unwrap();
    assert_eq!(
        AuthenticationKey::ed25519(&new_operator_key),
        AuthenticationKey::try_from(on_chain_operator_key).unwrap()
    );

    // Rotate the operator key in storage manually and perform another rotation using the op tool.
    // Here, we expected the op_tool to see that the operator key in storage doesn't match the one
    // on-chain, and thus it should simply forward a transaction to the blockchain.
    let rotated_operator_key = storage.rotate_key(OPERATOR_KEY).unwrap();
    let (txn_ctx, new_operator_key) = op_tool.rotate_operator_key(&backend).unwrap();
    assert_eq!(rotated_operator_key, new_operator_key);
    client
        .wait_for_transaction(txn_ctx.address, txn_ctx.sequence_number + 1)
        .unwrap();

    // Verify that the transaction was executed correctly
    let result = op_tool
        .validate_transaction(txn_ctx.address, txn_ctx.sequence_number)
        .unwrap();
    let vm_status = result.unwrap();
    assert_eq!(VMStatusView::Executed, vm_status);

    // Verify that the operator key was updated on-chain
    let account_resource = op_tool.account_resource(operator_account).unwrap();
    let on_chain_operator_key = hex::decode(account_resource.authentication_key).unwrap();
    assert_eq!(
        AuthenticationKey::ed25519(&new_operator_key),
        AuthenticationKey::try_from(on_chain_operator_key).unwrap()
    );
}

fn wait_for_transaction_on_all_nodes(
    swarm: &SmokeTestEnvironment,
    num_nodes: usize,
    account: AccountAddress,
    sequence_number: u64,
) {
    for i in 0..num_nodes {
        let mut client = swarm.get_validator_client(i, None);
        client
            .wait_for_transaction(account, sequence_number)
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use std::ops::Not;

use crate::types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum ReplyMarkup {
    InlineKeyboardMarkup(InlineKeyboardMarkup),
    ReplyKeyboardMarkup(ReplyKeyboardMarkup),
    ReplyKeyboardRemove(ReplyKeyboardRemove),
    ForceReply(ForceReply),
}

impl From<InlineKeyboardMarkup> for ReplyMarkup {
    fn from(value: InlineKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value)
    }
}

impl From<Vec<Vec<InlineKeyboardButton>>> for ReplyMarkup {
    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value.into())
    }
}

impl From<ReplyKeyboardMarkup> for ReplyMarkup {
    fn from(value: ReplyKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardRemove> for ReplyMarkup {
    fn from(value: ReplyKeyboardRemove) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardRemove(value)
    }
}

impl From<ForceReply> for ReplyMarkup {
    fn from(value: ForceReply) -> ReplyMarkup {
        ReplyMarkup::ForceReply(value)
    }
}

/// This object represents a custom keyboard with reply options.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardMarkup {
    keyboard: Vec<Vec<KeyboardButton>>,
    #[serde(skip_serializing_if = "Not::not")]
    resize_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    one_time_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ReplyKeyboardMarkup {
    pub fn new() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: false,
            one_time_keyboard: false,
            selective: false,
        }
    }

    fn init(rows: Vec<Vec<KeyboardButton>>) -> Self {
        let mut keyboard = Self::new();
        keyboard.keyboard = rows;
        keyboard
    }

    /// Requests clients to resize the keyboard vertically for
    /// optimal fit (e.g., make the keyboard smaller if there
    /// are just two rows of buttons). Defaults to false, in which case
    /// the custom keyboard is always of the same height as the app's standard keyboard.
    pub fn resize_keyboard(&mut self) -> &mut Self {
        self.resize_keyboard = true;
        self
    }

    /// Requests clients to hide the keyboard as soon as it's been used.
    /// The keyboard will still be available, but clients will automatically
    /// display the usual letter-keyboard in the chat – the user can
    /// press a special button in the input field to see the custom
    /// keyboard again. Defaults to false.
    pub fn one_time_keyboard(&mut self) -> &mut Self {
        self.one_time_keyboard = true;
        self
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }

    pub fn add_row(&mut self, row: Vec<KeyboardButton>) -> &mut Vec<KeyboardButton> {
        self.keyboard.push(row);
        self.keyboard.last_mut().unwrap()
    }

    pub fn add_empty_row(&mut self) -> &mut Vec<KeyboardButton> {
        self.add_row(Default::default())
    }
}

impl From<Vec<Vec<KeyboardButton>>> for ReplyKeyboardMarkup {
    fn from(value: Vec<Vec<KeyboardButton>>) -> Self {
        Self::init(value)
    }
}

/// This object represents one button of the reply keyboard.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct KeyboardButton {
    text: String,
    #[serde(skip_serializing_if = "Not::not")]
    request_contact: bool,
    #[serde(skip_serializing_if = "Not::not")]
    request_location: bool,
}

impl KeyboardButton {
    pub fn new<S: AsRef<str>>(text: S) -> Self {
        Self {
            text: text.as_ref().to_string(),
            request_contact: false,
            request_location: false,
        }
    }

    /// The user's phone number will be sent as a contact when the
    /// button is pressed. Available in private chats only
    pub fn request_contact(&mut self) -> &mut Self {
        self.request_location = false;
        self.request_contact = true;
        self
    }

    /// The user's current location will be sent when the
    /// button is pressed. Available in private chats only
    pub fn request_location(&mut self) -> &mut Self {
        self.request_contact = false;
        self.request_location = true;
        self
    }
}

impl<'a> From<&'a str> for KeyboardButton {
    fn from(value: &'a str) -> KeyboardButton {
        KeyboardButton::new(value)
    }
}

impl From<String> for KeyboardButton {
    fn from(value: String) -> KeyboardButton {
        KeyboardButton::new(value)
    }
}

/// Upon receiving a message with this object, Telegram clients will remove
/// the current custom keyboard and display the default letter-keyboard.
/// By default, custom keyboards are displayed until a new keyboard is sent
/// by a bot. An exception is made for one-time keyboards that are hidden
/// immediately after the user presses a button (see ReplyKeyboardMarkup).
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardRemove {
    remove_keyboard: True,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ReplyKeyboardRemove {
    pub fn new() -> Self {
        Self {
            remove_keyboard: True,
            selective: false,
        }
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }
}

/// This object represents an inline keyboard that appears right next to the message it belongs to.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardMarkup {
    inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
}

impl InlineKeyboardMarkup {
    pub fn new() -> Self {
        Self {
            inline_keyboard: Default::default(),
        }
    }

    fn init(inline_keyboard: Vec<Vec<InlineKeyboardButton>>) -> Self {
        Self { inline_keyboard }
    }

    pub fn add_row(&mut self, row: Vec<InlineKeyboardButton>) -> &mut Vec<InlineKeyboardButton> {
        self.inline_keyboard.push(row);
        self.inline_keyboard.last_mut().unwrap()
    }

    pub fn add_empty_row(&mut self) -> &mut Vec<InlineKeyboardButton> {
        self.add_row(Default::default())
    }
}

impl From<Vec<Vec<InlineKeyboardButton>>> for InlineKeyboardMarkup {
    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> Self {
        Self::init(value)
    }
}

/// This object represents one button of an inline keyboard.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardButton {
    text: String,
    #[serde(flatten)]
    kind: InlineKeyboardButtonKind,
}

impl InlineKeyboardButton {
    /// Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes
    pub fn callback<T: AsRef<str>, C: AsRef<str>>(text: T, callback: C) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::CallbackData(callback.as_ref().to_string()),
        }
    }

    pub fn url<T: AsRef<str>, U: AsRef<str>>(text: T, url: U) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::Url(url.as_ref().to_string()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub enum InlineKeyboardButtonKind {
    #[serde(rename = "url")]
    Url(String), // TODO(knsd): Url?
    #[serde(rename = "callback_data")]
    CallbackData(String), // TODO(knsd) Validate size?
                          //  SwitchInlineQuery(String),
                          //  SwitchInlineQueryCurrentChat(String),
                          //  CallbackGame(CallbackGame),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
struct InlineKeyboardButtonRaw<'a> {
    text: &'a str,
    #[serde(skip_serializing_if = "Option::is_none")]
    url: Option<&'a str>, // TODO(knsd): Url?
    #[serde(skip_serializing_if = "Option::is_none")]
    callback_data: Option<&'a str>, //TODO(knsd) Validate size?
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query: Option<&'a str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query_current_chat: Option<&'a str>,
    //    callback_game: Option<CallbackGame>,
}

/// Upon receiving a message with this object, Telegram clients will
/// display a reply interface to the user (act as if the user has
/// selected the bot‘s message and tapped ’Reply'). This can be
/// extremely useful if you want to create user-friendly step-by-step
/// interfaces without having to sacrifice privacy mod
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ForceReply {
    force_reply: True,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ForceReply {
    pub fn new() -> Self {
        Self {
            force_reply: True,
            selective: false,
        }
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }
}
<commit_msg>Add InlineKeyboardButton variants (SwitchInlineQuery, SwitchInlineQueryCurrentChat)<commit_after>use std::ops::Not;

use crate::types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
#[serde(untagged)]
pub enum ReplyMarkup {
    InlineKeyboardMarkup(InlineKeyboardMarkup),
    ReplyKeyboardMarkup(ReplyKeyboardMarkup),
    ReplyKeyboardRemove(ReplyKeyboardRemove),
    ForceReply(ForceReply),
}

impl From<InlineKeyboardMarkup> for ReplyMarkup {
    fn from(value: InlineKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value)
    }
}

impl From<Vec<Vec<InlineKeyboardButton>>> for ReplyMarkup {
    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value.into())
    }
}

impl From<ReplyKeyboardMarkup> for ReplyMarkup {
    fn from(value: ReplyKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardRemove> for ReplyMarkup {
    fn from(value: ReplyKeyboardRemove) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardRemove(value)
    }
}

impl From<ForceReply> for ReplyMarkup {
    fn from(value: ForceReply) -> ReplyMarkup {
        ReplyMarkup::ForceReply(value)
    }
}

/// This object represents a custom keyboard with reply options.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardMarkup {
    keyboard: Vec<Vec<KeyboardButton>>,
    #[serde(skip_serializing_if = "Not::not")]
    resize_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    one_time_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ReplyKeyboardMarkup {
    pub fn new() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: false,
            one_time_keyboard: false,
            selective: false,
        }
    }

    fn init(rows: Vec<Vec<KeyboardButton>>) -> Self {
        let mut keyboard = Self::new();
        keyboard.keyboard = rows;
        keyboard
    }

    /// Requests clients to resize the keyboard vertically for
    /// optimal fit (e.g., make the keyboard smaller if there
    /// are just two rows of buttons). Defaults to false, in which case
    /// the custom keyboard is always of the same height as the app's standard keyboard.
    pub fn resize_keyboard(&mut self) -> &mut Self {
        self.resize_keyboard = true;
        self
    }

    /// Requests clients to hide the keyboard as soon as it's been used.
    /// The keyboard will still be available, but clients will automatically
    /// display the usual letter-keyboard in the chat – the user can
    /// press a special button in the input field to see the custom
    /// keyboard again. Defaults to false.
    pub fn one_time_keyboard(&mut self) -> &mut Self {
        self.one_time_keyboard = true;
        self
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }

    pub fn add_row(&mut self, row: Vec<KeyboardButton>) -> &mut Vec<KeyboardButton> {
        self.keyboard.push(row);
        self.keyboard.last_mut().unwrap()
    }

    pub fn add_empty_row(&mut self) -> &mut Vec<KeyboardButton> {
        self.add_row(Default::default())
    }
}

impl From<Vec<Vec<KeyboardButton>>> for ReplyKeyboardMarkup {
    fn from(value: Vec<Vec<KeyboardButton>>) -> Self {
        Self::init(value)
    }
}

/// This object represents one button of the reply keyboard.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct KeyboardButton {
    text: String,
    #[serde(skip_serializing_if = "Not::not")]
    request_contact: bool,
    #[serde(skip_serializing_if = "Not::not")]
    request_location: bool,
}

impl KeyboardButton {
    pub fn new<S: AsRef<str>>(text: S) -> Self {
        Self {
            text: text.as_ref().to_string(),
            request_contact: false,
            request_location: false,
        }
    }

    /// The user's phone number will be sent as a contact when the
    /// button is pressed. Available in private chats only
    pub fn request_contact(&mut self) -> &mut Self {
        self.request_location = false;
        self.request_contact = true;
        self
    }

    /// The user's current location will be sent when the
    /// button is pressed. Available in private chats only
    pub fn request_location(&mut self) -> &mut Self {
        self.request_contact = false;
        self.request_location = true;
        self
    }
}

impl<'a> From<&'a str> for KeyboardButton {
    fn from(value: &'a str) -> KeyboardButton {
        KeyboardButton::new(value)
    }
}

impl From<String> for KeyboardButton {
    fn from(value: String) -> KeyboardButton {
        KeyboardButton::new(value)
    }
}

/// Upon receiving a message with this object, Telegram clients will remove
/// the current custom keyboard and display the default letter-keyboard.
/// By default, custom keyboards are displayed until a new keyboard is sent
/// by a bot. An exception is made for one-time keyboards that are hidden
/// immediately after the user presses a button (see ReplyKeyboardMarkup).
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardRemove {
    remove_keyboard: True,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ReplyKeyboardRemove {
    pub fn new() -> Self {
        Self {
            remove_keyboard: True,
            selective: false,
        }
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }
}

/// This object represents an inline keyboard that appears right next to the message it belongs to.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardMarkup {
    inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
}

impl InlineKeyboardMarkup {
    pub fn new() -> Self {
        Self {
            inline_keyboard: Default::default(),
        }
    }

    fn init(inline_keyboard: Vec<Vec<InlineKeyboardButton>>) -> Self {
        Self { inline_keyboard }
    }

    pub fn add_row(&mut self, row: Vec<InlineKeyboardButton>) -> &mut Vec<InlineKeyboardButton> {
        self.inline_keyboard.push(row);
        self.inline_keyboard.last_mut().unwrap()
    }

    pub fn add_empty_row(&mut self) -> &mut Vec<InlineKeyboardButton> {
        self.add_row(Default::default())
    }
}

impl From<Vec<Vec<InlineKeyboardButton>>> for InlineKeyboardMarkup {
    fn from(value: Vec<Vec<InlineKeyboardButton>>) -> Self {
        Self::init(value)
    }
}

/// This object represents one button of an inline keyboard.
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardButton {
    text: String,
    #[serde(flatten)]
    kind: InlineKeyboardButtonKind,
}

impl InlineKeyboardButton {
    /// Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes
    pub fn callback<T: AsRef<str>, C: AsRef<str>>(text: T, callback: C) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::CallbackData(callback.as_ref().to_string()),
        }
    }

    /// HTTP or tg:// url to be opened when button is pressed
    pub fn url<T: AsRef<str>, U: AsRef<str>>(text: T, url: U) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::Url(url.as_ref().to_string()),
        }
    }

    /// Pressing the button will prompt the user to select one of their chats, open that chat and
    /// insert the bot‘s username and the specified inline query in the input field. Can be empty,
    /// in which case just the bot’s username will be inserted.
    pub fn switch_inline_query<T: AsRef<str>, Q: AsRef<str>>(text: T, query: Q) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::SwitchInlineQuery(query.as_ref().to_string()),
        }
    }

    /// Pressing the button will insert the bot‘s username and the specified inline query in the
    /// current chat's input field. Can be empty, in which case just the bot’s username will be
    /// inserted.
    pub fn switch_inline_query_current_chat<T: AsRef<str>, Q: AsRef<str>>(
        text: T,
        query: Q,
    ) -> Self {
        Self {
            text: text.as_ref().to_string(),
            kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
                query.as_ref().to_string(),
            ),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub enum InlineKeyboardButtonKind {
    #[serde(rename = "url")]
    Url(String), // TODO(knsd): Url?
    #[serde(rename = "callback_data")]
    CallbackData(String), // TODO(knsd) Validate size?
    #[serde(rename = "switch_inline_query")]
    SwitchInlineQuery(String),
    #[serde(rename = "switch_inline_query_current_chat")]
    SwitchInlineQueryCurrentChat(String),
    // #[serde(rename = "callback_game")]
    //  CallbackGame(CallbackGame),
    // #[serde(rename = "pay")]
    //  Pay,
    // #[serde(rename = "login_url")]
    //  LoginUrl(LoginUrl),
}

/// Upon receiving a message with this object, Telegram clients will
/// display a reply interface to the user (act as if the user has
/// selected the bot‘s message and tapped ’Reply'). This can be
/// extremely useful if you want to create user-friendly step-by-step
/// interfaces without having to sacrifice privacy mod
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ForceReply {
    force_reply: True,
    #[serde(skip_serializing_if = "Not::not")]
    selective: bool,
}

impl ForceReply {
    pub fn new() -> Self {
        Self {
            force_reply: True,
            selective: false,
        }
    }

    /// Use this method if you want to force reply from specific users only.
    /// Targets: 1) users that are @mentioned in the text of
    /// the Message object; 2) if the bot's message is a reply (has reply_to_message_id),
    /// sender of the original message.
    pub fn selective(&mut self) -> &mut Self {
        self.selective = true;
        self
    }
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use static_assertions::const_assert;

use crate::formatter::fast::tests::fmt_factory::create_resolver;
use crate::formatter::fast::MAX_FMT_INSTR_LEN;
use crate::formatter::tests::sym_res::symbol_resolver_test_fast;
use crate::iced_constants::IcedConstants;
use crate::{Decoder, DecoderOptions, FastFormatter, Instruction, SymbolResolver, SymbolResult};

#[test]
fn symres() {
	symbol_resolver_test_fast("Fast", "SymbolResolverTests", |symbol_resolver| create_resolver(symbol_resolver));
}

struct LongSymbolResolver {
	symbol_result: String,
	instruction_operand: u32,
}
impl SymbolResolver for LongSymbolResolver {
	fn symbol(
		&mut self, _instruction: &Instruction, _operand: u32, instruction_operand: Option<u32>, address: u64, _address_size: u32,
	) -> Option<SymbolResult<'_>> {
		if instruction_operand == Some(self.instruction_operand) {
			Some(SymbolResult::with_str(address, &self.symbol_result))
		} else {
			None
		}
	}
}

#[test]
fn test_long_symbols() {
	const MAX_SYMBOL_LEN: usize = 1024;
	const_assert!(MAX_SYMBOL_LEN > MAX_FMT_INSTR_LEN);

	#[rustfmt::skip]
	let test_cases: [(u32, &'static [u8]); IcedConstants::MAX_OP_COUNT] = [
		// op0: push 0FFFFFFFFA412A55Ah
		(0, b"\x68\x5A\xA5\x12\xA4"),
		// op1: mov rax,3412A55A37261504h
		(1, b"\x48\xB8\x04\x15\x26\x37\x5A\xA5\x12\x34"),
		// op2: imul ecx,esi,3412A55Ah
		(2, b"\x69\xCE\x5A\xA5\x12\x34"),
		// op3: vgf2p8affineinvqb zmm2{k3},zmm6,[rax+40h],0A5h
		(3, b"\x62\xF3\xCD\x4B\xCF\x50\x01\xA5"),
		// op4: vpermil2ps xmm2,xmm6,[rax],xmm4,1
		(4, b"\xC4\xE3\x49\x48\x10\x41"),
	];
	for &(op, bytes) in &test_cases {
		let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
		let instr = decoder.decode();
		for symbol_len in 0..=MAX_SYMBOL_LEN {
			// Don't re-use it, always create a new one
			let mut output = String::new();
			let resolver = LongSymbolResolver { symbol_result: core::iter::repeat('a').take(symbol_len).collect(), instruction_operand: op };
			let mut formatter = FastFormatter::with_options(Some(Box::new(resolver)));
			formatter.format(&instr, &mut output);
			assert!(output.len() >= symbol_len);
		}
	}
}
<commit_msg>Update fast fmt long symbol tests<commit_after>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

use static_assertions::const_assert;

use crate::formatter::fast::tests::fmt_factory::create_resolver;
use crate::formatter::fast::MAX_FMT_INSTR_LEN;
use crate::formatter::tests::sym_res::symbol_resolver_test_fast;
use crate::iced_constants::IcedConstants;
use crate::{Decoder, DecoderOptions, FastFormatter, Instruction, SymbolResolver, SymbolResult};

#[test]
fn symres() {
	symbol_resolver_test_fast("Fast", "SymbolResolverTests", |symbol_resolver| create_resolver(symbol_resolver));
}

struct LongSymbolResolver {
	symbol_result: String,
	instruction_operand: u32,
}
impl SymbolResolver for LongSymbolResolver {
	fn symbol(
		&mut self, _instruction: &Instruction, _operand: u32, instruction_operand: Option<u32>, address: u64, _address_size: u32,
	) -> Option<SymbolResult<'_>> {
		if instruction_operand == Some(self.instruction_operand) {
			Some(SymbolResult::with_str(address, &self.symbol_result))
		} else {
			None
		}
	}
}

#[test]
fn test_long_symbols() {
	const MAX_SYMBOL_LEN: usize = 1024;
	const_assert!(MAX_SYMBOL_LEN > MAX_FMT_INSTR_LEN);

	#[rustfmt::skip]
	let test_cases: [(u32, &'static [u8]); IcedConstants::MAX_OP_COUNT] = [
		// mov [rax+12345678h],rcx
		(0, b"\x48\x89\x88\x78\x56\x34\x12"),
		// op1: mov rcx,[rax+12345678h]
		(1, b"\x48\x8B\x88\x78\x56\x34\x12"),
		// op2: imul ecx,[rsi+12345678h],9ABCDEF1h
		(2, b"\x69\x8E\x78\x56\x34\x12\xF1\xDE\xBC\x9A"),
		// op3: vpermil2ps xmm2,xmm6,xmm4,[rax+12345678h],1
		(3, b"\xC4\xE3\xC9\x48\x90\x78\x56\x34\x12\x41"),
		// op4: vpermil2ps xmm2,xmm6,[rax],xmm4,1
		(4, b"\xC4\xE3\x49\x48\x10\x41"),
	];
	for &(op, bytes) in &test_cases {
		let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
		let instr = decoder.decode();
		for symbol_len in 0..=MAX_SYMBOL_LEN {
			// Don't re-use it, always create a new one
			let mut output = String::new();
			let resolver = LongSymbolResolver { symbol_result: core::iter::repeat('a').take(symbol_len).collect(), instruction_operand: op };
			let mut formatter = FastFormatter::with_options(Some(Box::new(resolver)));
			formatter.format(&instr, &mut output);
			assert!(output.len() >= symbol_len);
		}
	}
}
<|endoftext|>


<commit_before>use std::cmp::max;

#[derive(Debug)]
enum Aminoacid {
    A,
    G,
    C,
    T,
    Undef,
}

static SCORES: [[i32; 4]; 4] = [[10, -1, -3, -4],
                                [-1, 7, -5, -3],
                                [-3, -5, 9, 0],
                                [-4, -3, 0, 8]];

fn get_score(a: char, b: char) -> i32 {
    let a_pos = match a {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    let b_pos = match b {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    SCORES[b_pos as usize][a_pos as usize]
}

fn print_matrix(sec_1: &str, sec_2: &str, mtrx: Vec<Vec<i32>>) {
    for row in mtrx.into_iter() {
        // println!("{:?}", row);
        for elem in row.into_iter() {
            print!("{}\t", elem);
        }
        println!("");
    }
}

fn align_secuence(sec_1: &str, sec_2: &str) {
    let len_1 = sec_1.len() + 1;
    let len_2 = sec_2.len() + 1;

    /********* Initialize matrix **********/
    let mut matrix: Vec<Vec<i32>> = vec![vec![0; len_1]; len_2];

    let mut init_val = -5;
    for i in 1..len_1 {
        matrix[0][i] = init_val;
        init_val -= 5;
    }

    init_val = -5;
    for i in 1..len_2 {
        matrix[i][0] = init_val;
        init_val -= 5;
    }
    /**************************************/

    let mut diag_val;
    let mut left_val;
    let mut uppr_val;

    let mut i = 1;
    for c2 in sec_2.chars() {
        let mut j = 1;
        for c1 in sec_1.chars() {
            let score = get_score(c2, c1);
            diag_val = score + matrix[i - 1][j - 1];
            left_val = -5 + matrix[i][j - 1];
            uppr_val = -5 + matrix[i - 1][j];
            matrix[i][j] = max(diag_val, max(left_val, uppr_val));
            // println!("[{}][{}]: {}\td: {}\tl: {}\tu: {}\t->\tval: {}",
            //          i,
            //          j,
            //          score,
            //          diag_val,
            //          left_val,
            //          uppr_val,
            //          matrix[i][j]);
            j += 1;
        }
        i += 1;
    }

    print_matrix(sec_1, sec_2, matrix);
}

fn main() {
    align_secuence("ACCGTCTT", "CGTCTT");
    // align_secuence("ACC", "CGT");
    // println!("Hello, world!");
}
<commit_msg>Trying to print the alignment<commit_after>use std::cmp::max;

#[derive(Debug)]
enum Aminoacid {
    A,
    G,
    C,
    T,
    Undef,
}

macro_rules! char_at {
    ($str: ident, $index: ident) => {
        &$str[$index..$index+1]
    }
}

static SCORES: [[i32; 4]; 4] = [[10, -1, -3, -4],
                                [-1, 7, -5, -3],
                                [-3, -5, 9, 0],
                                [-4, -3, 0, 8]];

fn get_score(a: char, b: char) -> i32 {
    let a_pos = match a {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    let b_pos = match b {
        'A' => Aminoacid::A,
        'G' => Aminoacid::G,
        'C' => Aminoacid::C,
        'T' => Aminoacid::T,
        _ => Aminoacid::Undef,
    };

    SCORES[b_pos as usize][a_pos as usize]
}

fn print_matrix(sec_1: &str, sec_2: &str, mtrx: Vec<Vec<i32>>) {
    print!("\t\t");
    for chr in sec_1.chars() {
        print!(" {}\t", chr);
    }
    print!("\n\t");

    for elem in mtrx[0].iter() {
        print!("{}\t", elem);
    }
    println!("");

    let mut i: usize = 1;
    for chr in sec_2.chars() {
        print!("{}\t", chr);
        for elem in mtrx[i].iter() {
            print!("{}\t", elem);
        }
        println!("");
        i += 1;
    }
}

fn align_secuence(sec_1: &str, sec_2: &str) {
    let len_1 = sec_1.len() + 1;
    let len_2 = sec_2.len() + 1;

    /********* Initialize matrix **********/
    let mut matrix: Vec<Vec<i32>> = vec![vec![0; len_1]; len_2];

    let mut init_val = -5;
    for i in 1..len_1 {
        matrix[0][i] = init_val;
        init_val -= 5;
    }

    init_val = -5;
    for i in 1..len_2 {
        matrix[i][0] = init_val;
        init_val -= 5;
    }
    /**************************************/

    let mut diag_val;
    let mut left_val;
    let mut uppr_val;

    let mut i = 1;
    for c2 in sec_2.chars() {
        let mut j = 1;
        for c1 in sec_1.chars() {
            let score = get_score(c2, c1);
            diag_val = score + matrix[i - 1][j - 1];
            left_val = -5 + matrix[i][j - 1];
            uppr_val = -5 + matrix[i - 1][j];
            matrix[i][j] = max(diag_val, max(left_val, uppr_val));
            j += 1;
        }
        i += 1;
    }

    let mut i = sec_1.len();
    let mut j = sec_2.len();
    let mut alignment = String::new();

    while i > 0 || j > 0 {
        diag_val = matrix[j - 1][i - 1];
        left_val = matrix[j - 1][i];
        uppr_val = matrix[j][i - 1];

        println!("{}\t{}\t{}", diag_val, left_val, uppr_val);

        if diag_val >= left_val && diag_val >= uppr_val {
                j -= 1;
                i-= 1;
                alignment += char_at!(sec_2, j);
        } else if left_val > uppr_val {
                j -= 1;
                alignment += "_";
            } else {
                i -= 1;
                alignment += "_";
            }
    }

    println!("\nAlignment: {}", alignment.chars().rev().collect::<String>());
    println!("\nMatrix:");
    print_matrix(sec_1, sec_2, matrix);
}

fn main() {
    align_secuence("ACCGTCTT", "CGTCTT");
    // align_secuence("ACTTAG", "ACTTC");
    // println!("Hello, world!");
}
<|endoftext|>