<commit_before>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>[[:alpha:]]+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>[[:alpha:]]+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args()
        .nth(1)
        .expect("expect root Markdown directory as CLI argument");
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = current_month
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap().display()
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<commit_msg>update date-check format on github issue (#1416)<commit_after>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>[[:alpha:]]+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>[[:alpha:]]+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args()
        .nth(1)
        .expect("expect root Markdown directory as CLI argument");
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = today_chrono.format("%B %Y"),
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap().display()
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<|endoftext|>


<commit_before><commit_msg>Add attribute completion tests<commit_after><|endoftext|>


<commit_before>use std::collections::HashMap;

use serde_json::{json, Value};

use crate::{model::interactions::ApplicationCommandOptionType, utils};

/// A builder for creating a new [`ApplicationCommandInteractionDataOption`].
///
/// [`kind`], [`name`], and [`description`] are required fields.
///
/// [`ApplicationCommandInteractionDataOption`]: crate::model::interactions::ApplicationCommandInteractionDataOption
/// [`kind`]: Self::kind
/// [`name`]: Self::name
/// [`description`]: Self::description
#[derive(Clone, Debug, Default)]
pub struct CreateInteractionOption(pub HashMap<&'static str, Value>);

impl CreateInteractionOption {
    /// Set the ApplicationCommandOptionType for the InteractionOption.
    pub fn kind(&mut self, kind: ApplicationCommandOptionType) -> &mut Self {
        self.0.insert("type", Value::Number(serde_json::Number::from(kind as u8)));
        self
    }

    /// Sets the name of the option.
    ///
    /// **Note**: The option name must be between 1 and 32 characters.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));
        self
    }

    /// Sets the description for the option.
    ///
    /// **Note**: The description must be between 1 and 100 characters.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::String(description.to_string()));
        self
    }

    /// The first required option for the user to complete
    ///
    /// **Note**: Only one option can be `default`.
    pub fn default_option(&mut self, default: bool) -> &mut Self {
        self.0.insert("default", Value::Bool(default));
        self
    }

    /// Sets if this option is required or optional.
    ///
    /// **Note**: This defaults to `false`.
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.0.insert("required", Value::Bool(required));
        self
    }

    /// Interaction commands can optionally have a limited
    /// number of integer or string choices.
    ///
    /// **Note**: There can be no more than 10 choices set.
    pub fn add_int_choice<D: ToString>(&mut self, name: D, value: i32) -> &mut Self {
        let choice = json!({
            "name": name.to_string(),
            "value" : value
        });
        self.add_choice(choice)
    }

    pub fn add_string_choice<D: ToString, E: ToString>(&mut self, name: D, value: E) -> &mut Self {
        let choice = json!({
            "name": name.to_string(),
            "value": value.to_string()
        });
        self.add_choice(choice)
    }

    fn add_choice(&mut self, value: Value) -> &mut Self {
        let choices = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(choices_arr) = choices.as_array_mut() {
            choices_arr.push(value);
        };
        self
    }

    /// If the option is a [`SubCommand`] or [`SubCommandGroup`] nested options are its parameters.
    ///
    /// [`SubCommand`]: crate::model::interactions::ApplicationCommandOptionType::SubCommand
    /// [`SubCommandGroup`]: crate::model::interactions::ApplicationCommandOptionType::SubCommandGroup
    pub fn create_sub_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInteractionOption) -> &mut CreateInteractionOption,
    {
        let mut data = CreateInteractionOption::default();
        f(&mut data);
        self.add_sub_option(data)
    }

    pub fn add_sub_option(&mut self, sub_option: CreateInteractionOption) -> &mut Self {
        let new_option = utils::hashmap_to_json_map(sub_option.0);
        let options = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(opt_arr) = options.as_array_mut() {
            opt_arr.push(Value::Object(new_option));
        };
        self
    }
}

/// A builder for creating a new [`ApplicationCommand`].
///
/// [`name`] and [`description`] are required fields.
///
/// [`ApplicationCommand`]: crate::model::interactions::ApplicationCommand
/// [`name`]: Self::name
/// [`description`]: Self::description
#[derive(Clone, Debug, Default)]
pub struct CreateInteraction(pub HashMap<&'static str, Value>);

impl CreateInteraction {
    /// Specify the name of the Interaction.
    ///
    /// **Note**: Must be between 1 and 32 characters long,
    /// and cannot start with a space.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));
        self
    }

    /// Specify the description of the Interaction.
    ///
    /// **Note**: Must be between 1 and 100 characters long.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::String(description.to_string()));
        self
    }

    /// Create an interaction option for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn create_interaction_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInteractionOption) -> &mut CreateInteractionOption,
    {
        let mut data = CreateInteractionOption::default();
        f(&mut data);
        self.add_interaction_option(data)
    }

    /// Add an interaction option for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn add_interaction_option(&mut self, option: CreateInteractionOption) -> &mut Self {
        let new_option = utils::hashmap_to_json_map(option.0);
        let options = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(opt_arr) = options.as_array_mut() {
            opt_arr.push(Value::Object(new_option));
        };
        // should this be changed to have None be a panic?
        self
    }

    /// Sets all the interaction options for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn set_interaction_options(&mut self, options: Vec<CreateInteractionOption>) -> &mut Self {
        let new_options = options
            .into_iter()
            .map(|f| Value::Object(utils::hashmap_to_json_map(f.0)))
            .collect::<Vec<Value>>();
        self.0.insert("options", Value::Array(new_options));
        self
    }
}
<commit_msg>Fix a typo in the create interaction builder (#1196)<commit_after>use std::collections::HashMap;

use serde_json::{json, Value};

use crate::{model::interactions::ApplicationCommandOptionType, utils};

/// A builder for creating a new [`ApplicationCommandInteractionDataOption`].
///
/// [`kind`], [`name`], and [`description`] are required fields.
///
/// [`ApplicationCommandInteractionDataOption`]: crate::model::interactions::ApplicationCommandInteractionDataOption
/// [`kind`]: Self::kind
/// [`name`]: Self::name
/// [`description`]: Self::description
#[derive(Clone, Debug, Default)]
pub struct CreateInteractionOption(pub HashMap<&'static str, Value>);

impl CreateInteractionOption {
    /// Set the ApplicationCommandOptionType for the InteractionOption.
    pub fn kind(&mut self, kind: ApplicationCommandOptionType) -> &mut Self {
        self.0.insert("type", Value::Number(serde_json::Number::from(kind as u8)));
        self
    }

    /// Sets the name of the option.
    ///
    /// **Note**: The option name must be between 1 and 32 characters.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));
        self
    }

    /// Sets the description for the option.
    ///
    /// **Note**: The description must be between 1 and 100 characters.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::String(description.to_string()));
        self
    }

    /// The first required option for the user to complete
    ///
    /// **Note**: Only one option can be `default`.
    pub fn default_option(&mut self, default: bool) -> &mut Self {
        self.0.insert("default", Value::Bool(default));
        self
    }

    /// Sets if this option is required or optional.
    ///
    /// **Note**: This defaults to `false`.
    pub fn required(&mut self, required: bool) -> &mut Self {
        self.0.insert("required", Value::Bool(required));
        self
    }

    /// Interaction commands can optionally have a limited
    /// number of integer or string choices.
    ///
    /// **Note**: There can be no more than 10 choices set.
    pub fn add_int_choice<D: ToString>(&mut self, name: D, value: i32) -> &mut Self {
        let choice = json!({
            "name": name.to_string(),
            "value" : value
        });
        self.add_choice(choice)
    }

    pub fn add_string_choice<D: ToString, E: ToString>(&mut self, name: D, value: E) -> &mut Self {
        let choice = json!({
            "name": name.to_string(),
            "value": value.to_string()
        });
        self.add_choice(choice)
    }

    fn add_choice(&mut self, value: Value) -> &mut Self {
        let choices = self.0.entry("choices").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(choices_arr) = choices.as_array_mut() {
            choices_arr.push(value);
        };
        self
    }

    /// If the option is a [`SubCommand`] or [`SubCommandGroup`] nested options are its parameters.
    ///
    /// [`SubCommand`]: crate::model::interactions::ApplicationCommandOptionType::SubCommand
    /// [`SubCommandGroup`]: crate::model::interactions::ApplicationCommandOptionType::SubCommandGroup
    pub fn create_sub_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInteractionOption) -> &mut CreateInteractionOption,
    {
        let mut data = CreateInteractionOption::default();
        f(&mut data);
        self.add_sub_option(data)
    }

    pub fn add_sub_option(&mut self, sub_option: CreateInteractionOption) -> &mut Self {
        let new_option = utils::hashmap_to_json_map(sub_option.0);
        let options = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(opt_arr) = options.as_array_mut() {
            opt_arr.push(Value::Object(new_option));
        };
        self
    }
}

/// A builder for creating a new [`ApplicationCommand`].
///
/// [`name`] and [`description`] are required fields.
///
/// [`ApplicationCommand`]: crate::model::interactions::ApplicationCommand
/// [`name`]: Self::name
/// [`description`]: Self::description
#[derive(Clone, Debug, Default)]
pub struct CreateInteraction(pub HashMap<&'static str, Value>);

impl CreateInteraction {
    /// Specify the name of the Interaction.
    ///
    /// **Note**: Must be between 1 and 32 characters long,
    /// and cannot start with a space.
    pub fn name<D: ToString>(&mut self, name: D) -> &mut Self {
        self.0.insert("name", Value::String(name.to_string()));
        self
    }

    /// Specify the description of the Interaction.
    ///
    /// **Note**: Must be between 1 and 100 characters long.
    pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
        self.0.insert("description", Value::String(description.to_string()));
        self
    }

    /// Create an interaction option for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn create_interaction_option<F>(&mut self, f: F) -> &mut Self
    where
        F: FnOnce(&mut CreateInteractionOption) -> &mut CreateInteractionOption,
    {
        let mut data = CreateInteractionOption::default();
        f(&mut data);
        self.add_interaction_option(data)
    }

    /// Add an interaction option for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn add_interaction_option(&mut self, option: CreateInteractionOption) -> &mut Self {
        let new_option = utils::hashmap_to_json_map(option.0);
        let options = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
        if let Some(opt_arr) = options.as_array_mut() {
            opt_arr.push(Value::Object(new_option));
        };
        // should this be changed to have None be a panic?
        self
    }

    /// Sets all the interaction options for the interaction.
    ///
    /// **Note**: Interactions can only have up to 10 options.
    pub fn set_interaction_options(&mut self, options: Vec<CreateInteractionOption>) -> &mut Self {
        let new_options = options
            .into_iter()
            .map(|f| Value::Object(utils::hashmap_to_json_map(f.0)))
            .collect::<Vec<Value>>();
        self.0.insert("options", Value::Array(new_options));
        self
    }
}
<|endoftext|>


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

#![feature(rustc_private, rustdoc)]

extern crate syntax;
extern crate rustdoc;
extern crate serialize as rustc_serialize;

use std::collections::BTreeMap;
use std::fs::{read_dir, File};
use std::io::{Read, Write};
use std::env;
use std::path::Path;
use std::error::Error;

use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap};

use rustdoc::html::markdown::Markdown;
use rustc_serialize::json;

/// Load all the metadata files from `metadata_dir` into an in-memory map.
fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
    let mut all_errors = BTreeMap::new();

    for entry in try!(read_dir(metadata_dir)) {
        let path = try!(entry).path();

        let mut metadata_str = String::new();
        try!(
            File::open(&path).and_then(|mut f|
            f.read_to_string(&mut metadata_str))
        );

        let some_errors: ErrorMetadataMap = try!(json::decode(&metadata_str));

        for (err_code, info) in some_errors {
            all_errors.insert(err_code, info);
        }
    }

    Ok(all_errors)
}

/// Output an HTML page for the errors in `err_map` to `output_path`.
fn render_error_page(err_map: &ErrorMetadataMap, output_path: &Path) -> Result<(), Box<Error>> {
    let mut output_file = try!(File::create(output_path));

    try!(write!(&mut output_file,
r##"<!DOCTYPE html>

<title>Rust Compiler Error Index</title>
<meta charset="utf-8">

<link rel="stylesheet" type="text/css" href="main.css"/>
<link rel="stylesheet" type="text/css" href="rust.css"/>

"##
    ));

    try!(write!(&mut output_file, "
Rust Compiler Error Index
\n"));

    for (err_code, info) in err_map {
        // Enclose each error in a div so they can be shown/hidden en masse.
        let desc_desc = match info.description {
            Some(_) => "error-described",
            None => "error-undescribed"
        };
        let use_desc = match info.use_site {
            Some(_) => "error-used",
            None => "error-unused"
        };
        try!(write!(&mut output_file, "
", desc_desc, use_desc));

        // Error title (with self-link).
        try!(write!(&mut output_file,
            "
{0}
\n",
            err_code
        ));

        // Description rendered as markdown.
        match info.description {
            Some(ref desc) => try!(write!(&mut output_file, "{}", Markdown(desc))),
            None => try!(write!(&mut output_file, "
No description.
\n"))
        }

        try!(write!(&mut output_file, "
\n"));
    }

    try!(write!(&mut output_file, "\n"));

    Ok(())
}

fn main_with_result() -> Result<(), Box<Error>> {
    let build_arch = try!(env::var("CFG_BUILD"));
    let metadata_dir = get_metadata_dir(&build_arch);
    let err_map = try!(load_all_errors(&metadata_dir));
    try!(render_error_page(&err_map, Path::new("doc/error-index.html")));
    Ok(())
}

fn main() {
    if let Err(e) = main_with_result() {
        panic!("{}", e.description());
    }
}
<commit_msg>Auto merge of #28986 - ahmedcharles:error-index-generator, r=brson<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(rustc_private, rustdoc)]

extern crate syntax;
extern crate rustdoc;
extern crate serialize as rustc_serialize;

use std::collections::BTreeMap;
use std::fs::{read_dir, File};
use std::io::{Read, Write};
use std::env;
use std::path::Path;
use std::error::Error;

use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap};

use rustdoc::html::markdown::Markdown;
use rustc_serialize::json;

/// Load all the metadata files from `metadata_dir` into an in-memory map.
fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
    let mut all_errors = BTreeMap::new();

    for entry in try!(read_dir(metadata_dir)) {
        let path = try!(entry).path();

        let mut metadata_str = String::new();
        try!(File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str)));

        let some_errors: ErrorMetadataMap = try!(json::decode(&metadata_str));

        for (err_code, info) in some_errors {
            all_errors.insert(err_code, info);
        }
    }

    Ok(all_errors)
}

/// Output an HTML page for the errors in `err_map` to `output_path`.
fn render_error_page(err_map: &ErrorMetadataMap, output_path: &Path) -> Result<(), Box<Error>> {
    let mut output_file = try!(File::create(output_path));

    try!(write!(&mut output_file,
r##"<!DOCTYPE html>

<title>Rust Compiler Error Index</title>
<meta charset="utf-8">

<link rel="stylesheet" type="text/css" href="main.css"/>
<link rel="stylesheet" type="text/css" href="rust.css"/>

"##
    ));

    try!(write!(&mut output_file, "
Rust Compiler Error Index
\n"));

    for (err_code, info) in err_map {
        // Enclose each error in a div so they can be shown/hidden en masse.
        let desc_desc = match info.description {
            Some(_) => "error-described",
            None => "error-undescribed",
        };
        let use_desc = match info.use_site {
            Some(_) => "error-used",
            None => "error-unused",
        };
        try!(write!(&mut output_file, "
", desc_desc, use_desc));

        // Error title (with self-link).
        try!(write!(&mut output_file,
                    "
{0}
\n",
                    err_code));

        // Description rendered as markdown.
        match info.description {
            Some(ref desc) => try!(write!(&mut output_file, "{}", Markdown(desc))),
            None => try!(write!(&mut output_file, "
No description.
\n")),
        }

        try!(write!(&mut output_file, "
\n"));
    }

    try!(write!(&mut output_file, "\n"));

    Ok(())
}

fn main_with_result() -> Result<(), Box<Error>> {
    let build_arch = try!(env::var("CFG_BUILD"));
    let metadata_dir = get_metadata_dir(&build_arch);
    let err_map = try!(load_all_errors(&metadata_dir));
    try!(render_error_page(&err_map, Path::new("doc/error-index.html")));
    Ok(())
}

fn main() {
    if let Err(e) = main_with_result() {
        panic!("{}", e.description());
    }
}
<|endoftext|>


<commit_before>//! ## Summary
//! One of cita's main core components is to execute transaction,
//! create contracts, maintain world state trees, and send executed
//! result block to chain.
//!
//! ### Message queuing situation
//!
//! 1. Subscribe channel
//!
//!     | Queue    | PubModule | Message Type       |
//!     | -------- | --------- | ------------------ |
//!     | executor | Chain     | LocalSync          |
//!     | executor | Chain     | Request            |
//!     | executor | Chain     | Richstatus         |
//!     | executor | Consensus | BlockWithProof     |
//!     | executor | Consensus | SignedProposal     |
//!     | executor | Consensus | MiscellaneousReq   |
//!     | executor | Net       | SyncResponse       |
//!     | executor | Net       | SignedProposal     |
//!     | executor | Snapshot  | SnapshotReq        |
//!
//! 2. Publish channel
//!
//!     | Queue    | PubModule | SubModule | Message Type   |
//!     | -------- | --------- | --------- | -------------- |
//!     | executor | Executor  | Snapshot  | SnapshotResp   |
//!     | executor | Executor  | Jsonrpc   | Response       |
//!     | executor | Executor  | Chain     | ExecutedResult |
//!     | executor | Executor  | Auth      | Miscellaneous  |
//!
//! ### Key behavior
//!
//! key struct:
//!
//! - `ExecutorInstance`: `executor_instance::ExecutorInstance`
//! - [`Executor`]
//! - [`GlobalSysConfig`]
//! - [`Genesis`]
//! - [`Contract`]
//! - [`Account`]
//! - `AccountEntry`: `core_executor::state::AccountEntry`
//! - [`State`]
//! - [`StateDB`]
//!
//! This is currently the most complex module that maintains the current state of
//! the entire chain and caches some data, keeps the hash values of the last 256
//! blocks and the information of each block (gas_limit/quota, etc.) in memory,
//! holds the current block map(heigh, block).
//!
//! Of course there is an evm interface in this module.
//!
//! The contract/transaction submission is first cached in memory before being committed
//! to the stateDB (disk).
//!
//! [`Executor`]: ../core_executor/libexecutor/executor/struct.Executor.html
//! [`GlobalSysConfig`]: ../core_executor/libexecutor/executor/struct.GlobalSysConfig.html
//! [`Genesis`]: ../core_executor/libexecutor/genesis/struct.Genesis.html
//! [`Contract`]: ../core_executor/libexecutor/genesis/struct.Contract.html
//! [`Account`]: ../core_executor/state/account/struct.Account.html
//! [`State`]: ../core_executor/state/struct.State.html
//! [`StateDB`]: ../core_executor/state_db/struct.StateDB.html
//!

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![feature(custom_attribute)]
#![allow(deprecated, unused_must_use, unused_mut, unused_assignments)]
#![feature(refcell_replace_swap)]
#![feature(try_from)]
extern crate bincode;
extern crate clap;
extern crate common_types as types;
extern crate core_executor as core;
extern crate dotenv;
extern crate error;
extern crate grpc;
extern crate jsonrpc_types;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate log;
extern crate logger;
extern crate proof;
extern crate pubsub;
extern crate serde_json;
#[macro_use]
extern crate util;

mod executor_instance;

use clap::App;
use core::libexecutor::{vm_grpc_server, ServiceMap};
use executor_instance::ExecutorInstance;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::start_pubsub;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;
use util::set_panic_handler;

include!(concat!(env!("OUT_DIR"), "/build_info.rs"));

fn main() {
    micro_service_init!("cita-executor", "CITA:executor");

    let matches = App::new("executor")
        .version(get_build_info_str(true))
        .long_version(get_build_info_str(false))
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .arg_from_usage("-g, --genesis=[FILE] 'Sets a genesis config file")
        .arg_from_usage("-c, --config=[FILE] 'Sets a switch config file'")
        .get_matches();

    let genesis_path = matches.value_of("genesis").unwrap_or("genesis.json");

    let config_path = matches.value_of("config").unwrap_or("executor.toml");

    let (tx, rx) = channel();
    let (write_sender, write_receiver) = channel();
    let (ctx_pub, crx_pub) = channel();
    start_pubsub(
        "executor",
        routing_key!([
            Chain >> LocalSync,
            Net >> SyncResponse,
            Consensus >> BlockWithProof,
            Chain >> Request,
            Chain >> RichStatus,
            Consensus >> SignedProposal,
            Consensus >> RawBytes,
            Net >> SignedProposal,
            Net >> RawBytes,
            Snapshot >> SnapshotReq,
            Auth >> MiscellaneousReq,
        ]),
        tx,
        crx_pub,
    );

    let service_map = Arc::new(ServiceMap::new());
    let mut ext_instance = ExecutorInstance::new(
        ctx_pub.clone(),
        write_sender,
        config_path,
        genesis_path,
        Arc::clone(&service_map),
    );
    let mut distribute_ext = ext_instance.clone();

    thread::spawn(move || loop {
        if let Ok((key, msg)) = rx.recv() {
            distribute_ext.distribute_msg(&key, &msg);
        }
    });
    let mut server: Option<::grpc::Server> = None;
    let grpc_ext = ext_instance.clone();
    thread::spawn(move || loop {
        if server.is_none() {
            server = vm_grpc_server(
                grpc_ext.grpc_port,
                Arc::clone(&service_map),
                Arc::clone(&grpc_ext.ext),
            );
        } else {
            thread::sleep(Duration::new(8, 0));
        }
    });

    loop {
        if let Ok(number) = write_receiver.recv_timeout(Duration::new(8, 0)) {
            ext_instance.execute_block(number);
        } else {
            for height in ext_instance.ext.executed_result.read().keys() {
                ext_instance
                    .ext
                    .send_executed_info_to_chain(*height, &ctx_pub);
            }
        }
    }
}
<commit_msg>executor add timeout factor<commit_after>//! ## Summary
//! One of cita's main core components is to execute transaction,
//! create contracts, maintain world state trees, and send executed
//! result block to chain.
//!
//! ### Message queuing situation
//!
//! 1. Subscribe channel
//!
//!     | Queue    | PubModule | Message Type       |
//!     | -------- | --------- | ------------------ |
//!     | executor | Chain     | LocalSync          |
//!     | executor | Chain     | Request            |
//!     | executor | Chain     | Richstatus         |
//!     | executor | Consensus | BlockWithProof     |
//!     | executor | Consensus | SignedProposal     |
//!     | executor | Consensus | MiscellaneousReq   |
//!     | executor | Net       | SyncResponse       |
//!     | executor | Net       | SignedProposal     |
//!     | executor | Snapshot  | SnapshotReq        |
//!
//! 2. Publish channel
//!
//!     | Queue    | PubModule | SubModule | Message Type   |
//!     | -------- | --------- | --------- | -------------- |
//!     | executor | Executor  | Snapshot  | SnapshotResp   |
//!     | executor | Executor  | Jsonrpc   | Response       |
//!     | executor | Executor  | Chain     | ExecutedResult |
//!     | executor | Executor  | Auth      | Miscellaneous  |
//!
//! ### Key behavior
//!
//! key struct:
//!
//! - `ExecutorInstance`: `executor_instance::ExecutorInstance`
//! - [`Executor`]
//! - [`GlobalSysConfig`]
//! - [`Genesis`]
//! - [`Contract`]
//! - [`Account`]
//! - `AccountEntry`: `core_executor::state::AccountEntry`
//! - [`State`]
//! - [`StateDB`]
//!
//! This is currently the most complex module that maintains the current state of
//! the entire chain and caches some data, keeps the hash values of the last 256
//! blocks and the information of each block (gas_limit/quota, etc.) in memory,
//! holds the current block map(heigh, block).
//!
//! Of course there is an evm interface in this module.
//!
//! The contract/transaction submission is first cached in memory before being committed
//! to the stateDB (disk).
//!
//! [`Executor`]: ../core_executor/libexecutor/executor/struct.Executor.html
//! [`GlobalSysConfig`]: ../core_executor/libexecutor/executor/struct.GlobalSysConfig.html
//! [`Genesis`]: ../core_executor/libexecutor/genesis/struct.Genesis.html
//! [`Contract`]: ../core_executor/libexecutor/genesis/struct.Contract.html
//! [`Account`]: ../core_executor/state/account/struct.Account.html
//! [`State`]: ../core_executor/state/struct.State.html
//! [`StateDB`]: ../core_executor/state_db/struct.StateDB.html
//!

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![feature(custom_attribute)]
#![allow(deprecated, unused_must_use, unused_mut, unused_assignments)]
#![feature(refcell_replace_swap)]
#![feature(try_from)]
extern crate bincode;
extern crate clap;
extern crate common_types as types;
extern crate core_executor as core;
extern crate dotenv;
extern crate error;
extern crate grpc;
extern crate jsonrpc_types;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate log;
extern crate logger;
extern crate proof;
extern crate pubsub;
extern crate serde_json;
#[macro_use]
extern crate util;

mod executor_instance;

use clap::App;
use core::libexecutor::{vm_grpc_server, ServiceMap};
use executor_instance::ExecutorInstance;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::start_pubsub;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;
use util::set_panic_handler;

include!(concat!(env!("OUT_DIR"), "/build_info.rs"));

fn main() {
    micro_service_init!("cita-executor", "CITA:executor");

    let matches = App::new("executor")
        .version(get_build_info_str(true))
        .long_version(get_build_info_str(false))
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .arg_from_usage("-g, --genesis=[FILE] 'Sets a genesis config file")
        .arg_from_usage("-c, --config=[FILE] 'Sets a switch config file'")
        .get_matches();

    let genesis_path = matches.value_of("genesis").unwrap_or("genesis.json");

    let config_path = matches.value_of("config").unwrap_or("executor.toml");

    let (tx, rx) = channel();
    let (write_sender, write_receiver) = channel();
    let (ctx_pub, crx_pub) = channel();
    start_pubsub(
        "executor",
        routing_key!([
            Chain >> LocalSync,
            Net >> SyncResponse,
            Consensus >> BlockWithProof,
            Chain >> Request,
            Chain >> RichStatus,
            Consensus >> SignedProposal,
            Consensus >> RawBytes,
            Net >> SignedProposal,
            Net >> RawBytes,
            Snapshot >> SnapshotReq,
            Auth >> MiscellaneousReq,
        ]),
        tx,
        crx_pub,
    );

    let service_map = Arc::new(ServiceMap::new());
    let mut ext_instance = ExecutorInstance::new(
        ctx_pub.clone(),
        write_sender,
        config_path,
        genesis_path,
        Arc::clone(&service_map),
    );
    let mut distribute_ext = ext_instance.clone();

    thread::spawn(move || loop {
        if let Ok((key, msg)) = rx.recv() {
            distribute_ext.distribute_msg(&key, &msg);
        }
    });
    let mut server: Option<::grpc::Server> = None;
    let grpc_ext = ext_instance.clone();
    thread::spawn(move || loop {
        if server.is_none() {
            server = vm_grpc_server(
                grpc_ext.grpc_port,
                Arc::clone(&service_map),
                Arc::clone(&grpc_ext.ext),
            );
        } else {
            thread::sleep(Duration::new(8, 0));
        }
    });

    let mut timeout_factor = 0u8;
    loop {
        if let Ok(number) = write_receiver.recv_timeout(Duration::new(18 * (2u64.pow(timeout_factor as u32)), 0)) {
            ext_instance.execute_block(number);
            timeout_factor = 0;
        } else {
            for height in ext_instance.ext.executed_result.read().keys() {
                ext_instance
                    .ext
                    .send_executed_info_to_chain(*height, &ctx_pub);
            }
            if timeout_factor < 6 {
                timeout_factor += 1
            }
        }
    }
}
<|endoftext|>


<commit_before>use roff::{bold, italic, roman, Inline, Roff};

pub(crate) fn subcommand_heading(cmd: &clap::Command) -> &str {
    match cmd.get_subcommand_help_heading() {
        Some(title) => title,
        None => "SUBCOMMANDS",
    }
}

pub(crate) fn about(roff: &mut Roff, cmd: &clap::Command) {
    let s = match cmd.get_about().or_else(|| cmd.get_long_about()) {
        Some(about) => format!("{} - {}", cmd.get_name(), about),
        None => cmd.get_name().to_string(),
    };
    roff.text([roman(&s)]);
}

pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_long_about().or_else(|| cmd.get_about()) {
        for line in about.to_string().lines() {
            if line.trim().is_empty() {
                roff.control("PP", []);
            } else {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) {
    let mut line = vec![bold(cmd.get_name()), roman(" ")];

    for opt in cmd.get_arguments().filter(|i| !i.is_hide_set()) {
        let (lhs, rhs) = option_markers(opt);
        match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{}", short)));
                line.push(roman("|"));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (Some(short), None) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{} ", short)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, None) => (),
        };
    }

    for arg in cmd.get_positionals() {
        let (lhs, rhs) = option_markers(arg);
        line.push(roman(lhs));
        if let Some(value) = arg.get_value_names() {
            line.push(italic(value.join(" ")));
        } else {
            line.push(italic(arg.get_id().as_str()));
        }
        line.push(roman(rhs));
        line.push(roman(" "));
    }

    if cmd.has_subcommands() {
        let (lhs, rhs) = subcommand_markers(cmd);
        line.push(roman(lhs));
        line.push(italic(
            cmd.get_subcommand_value_name()
                .unwrap_or_else(|| subcommand_heading(cmd))
                .to_lowercase(),
        ));
        line.push(roman(rhs));
    }

    roff.text(line);
}

pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
    let items: Vec<_> = cmd.get_arguments().filter(|i| !i.is_hide_set()).collect();

    for opt in items.iter().filter(|a| !a.is_positional()) {
        let mut header = match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                vec![short_option(short), roman(", "), long_option(long)]
            }
            (Some(short), None) => vec![short_option(short)],
            (None, Some(long)) => vec![long_option(long)],
            (None, None) => vec![],
        };

        if let Some(value) = &opt.get_value_names() {
            header.push(roman("="));
            header.push(italic(&value.join(" ")));
        }

        if let Some(defs) = option_default_values(opt) {
            header.push(roman(" "));
            header.push(roman(&defs));
        }

        let mut body = vec![];
        let mut arg_help_written = false;
        if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) {
            arg_help_written = true;
            body.push(roman(help.to_string()));
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);

        if let Some((possible_values_text, with_help)) = get_possible_values(opt) {
            if arg_help_written {
                // It looks nice to have a separation between the help and the values
                roff.text([Inline::LineBreak]);
            }
            if with_help {
                roff.text([Inline::LineBreak, italic("Possible values:")]);

                // Need to indent twice to get it to look right, because .TP heading indents, but
                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
                // size is 7 for terminal devices
                roff.control("RS", ["14"]);
                for line in possible_values_text {
                    roff.control("IP", ["\\(bu", "2"]);
                    roff.text([roman(line)]);
                }
                roff.control("RE", []);
            } else {
                let possible_value_text: Vec<Inline> = vec![
                    Inline::LineBreak,
                    roman("["),
                    italic("possible values: "),
                    roman(possible_values_text.join(", ")),
                    roman("]"),
                ];
                roff.text(possible_value_text);
            }
        }

        if let Some(env) = option_environment(opt) {
            roff.control("RS", []);
            roff.text(env);
            roff.control("RE", []);
        }
    }

    for pos in items.iter().filter(|a| a.is_positional()) {
        let mut header = vec![];
        let (lhs, rhs) = option_markers(pos);
        header.push(roman(lhs));
        if let Some(value) = pos.get_value_names() {
            header.push(italic(value.join(" ")));
        } else {
            header.push(italic(pos.get_id().as_str()));
        };
        header.push(roman(rhs));

        if let Some(defs) = option_default_values(pos) {
            header.push(roman(&format!(" {}", defs)));
        }

        let mut body = vec![];
        let mut arg_help_written = false;
        if let Some(help) = pos.get_long_help().or_else(|| pos.get_help()) {
            body.push(roman(&help.to_string()));
            arg_help_written = true;
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);

        if let Some(env) = option_environment(pos) {
            roff.control("RS", []);
            roff.text(env);
            roff.control("RE", []);
        }
        // If possible options are available
        if let Some((possible_values_text, with_help)) = get_possible_values(pos) {
            if arg_help_written {
                // It looks nice to have a separation between the help and the values
                roff.text([Inline::LineBreak]);
            }
            if with_help {
                roff.text([Inline::LineBreak, italic("Possible values:")]);

                // Need to indent twice to get it to look right, because .TP heading indents, but
                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
                // size is 7 for terminal devices
                roff.control("RS", ["14"]);
                for line in possible_values_text {
                    roff.control("IP", ["\\(bu", "2"]);
                    roff.text([roman(line)]);
                }
                roff.control("RE", []);
            } else {
                let possible_value_text: Vec<Inline> = vec![
                    Inline::LineBreak,
                    roman("["),
                    italic("possible values: "),
                    roman(possible_values_text.join(", ")),
                    roman("]"),
                ];
                roff.text(possible_value_text);
            }
        }
    }
}

pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) {
    for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) {
        roff.control("TP", []);

        let name = format!("{}-{}({})", cmd.get_name(), sub.get_name(), section);
        roff.text([roman(&name)]);

        if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) {
            for line in about.to_string().lines() {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn version(cmd: &clap::Command) -> String {
    format!(
        "v{}",
        cmd.get_long_version()
            .or_else(|| cmd.get_version())
            .unwrap()
    )
}

pub(crate) fn after_help(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_after_long_help().or_else(|| cmd.get_after_help()) {
        for line in about.to_string().lines() {
            roff.text([roman(line)]);
        }
    }
}

fn subcommand_markers(cmd: &clap::Command) -> (&'static str, &'static str) {
    markers(cmd.is_subcommand_required_set())
}

fn option_markers(opt: &clap::Arg) -> (&'static str, &'static str) {
    markers(opt.is_required_set())
}

fn markers(required: bool) -> (&'static str, &'static str) {
    if required {
        ("<", ">")
    } else {
        ("[", "]")
    }
}

fn short_option(opt: char) -> Inline {
    bold(&format!("-{}", opt))
}

fn long_option(opt: &str) -> Inline {
    bold(&format!("--{}", opt))
}

fn option_environment(opt: &clap::Arg) -> Option<Vec<Inline>> {
    if opt.is_hide_env_set() {
        return None;
    } else if let Some(env) = opt.get_env() {
        return Some(vec![
            roman("May also be specified with the "),
            bold(env.to_string_lossy().to_owned()),
            roman(" environment variable. "),
        ]);
    }

    None
}

fn option_default_values(opt: &clap::Arg) -> Option<String> {
    if !opt.get_default_values().is_empty() {
        let values = opt
            .get_default_values()
            .iter()
            .map(|s| s.to_string_lossy())
            .collect::<Vec<_>>()
            .join(",");

        return Some(format!("[default: {}]", values));
    }

    None
}

fn get_possible_values(arg: &clap::Arg) -> Option<(Vec<String>, bool)> {
    let possibles = &arg.get_possible_values();
    let possibles: Vec<&clap::builder::PossibleValue> =
        possibles.iter().filter(|pos| !pos.is_hide_set()).collect();

    if !(possibles.is_empty() || arg.is_hide_possible_values_set()) {
        return Some(format_possible_values(&possibles));
    }
    None
}

fn format_possible_values(possibles: &Vec<&clap::builder::PossibleValue>) -> (Vec<String>, bool) {
    let mut lines = vec![];
    let with_help = possibles.iter().any(|p| p.get_help().is_some());
    if with_help {
        for value in possibles {
            let val_name = value.get_name();
            match value.get_help() {
                Some(help) => lines.push(format!("{}: {}", val_name, help)),
                None => lines.push(val_name.to_string()),
            }
        }
    } else {
        lines.append(&mut possibles.iter().map(|p| p.get_name().to_string()).collect());
    }
    (lines, with_help)
}
<commit_msg>fix(mangen): Respect more hide values<commit_after>use roff::{bold, italic, roman, Inline, Roff};

pub(crate) fn subcommand_heading(cmd: &clap::Command) -> &str {
    match cmd.get_subcommand_help_heading() {
        Some(title) => title,
        None => "SUBCOMMANDS",
    }
}

pub(crate) fn about(roff: &mut Roff, cmd: &clap::Command) {
    let s = match cmd.get_about().or_else(|| cmd.get_long_about()) {
        Some(about) => format!("{} - {}", cmd.get_name(), about),
        None => cmd.get_name().to_string(),
    };
    roff.text([roman(&s)]);
}

pub(crate) fn description(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_long_about().or_else(|| cmd.get_about()) {
        for line in about.to_string().lines() {
            if line.trim().is_empty() {
                roff.control("PP", []);
            } else {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn synopsis(roff: &mut Roff, cmd: &clap::Command) {
    let mut line = vec![bold(cmd.get_name()), roman(" ")];

    for opt in cmd.get_arguments().filter(|i| !i.is_hide_set()) {
        let (lhs, rhs) = option_markers(opt);
        match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{}", short)));
                line.push(roman("|"));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (Some(short), None) => {
                line.push(roman(lhs));
                line.push(bold(&format!("-{} ", short)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, Some(long)) => {
                line.push(roman(lhs));
                line.push(bold(&format!("--{}", long)));
                line.push(roman(rhs));
                line.push(roman(" "));
            }
            (None, None) => (),
        };
    }

    for arg in cmd.get_positionals() {
        let (lhs, rhs) = option_markers(arg);
        line.push(roman(lhs));
        if let Some(value) = arg.get_value_names() {
            line.push(italic(value.join(" ")));
        } else {
            line.push(italic(arg.get_id().as_str()));
        }
        line.push(roman(rhs));
        line.push(roman(" "));
    }

    if cmd.has_subcommands() {
        let (lhs, rhs) = subcommand_markers(cmd);
        line.push(roman(lhs));
        line.push(italic(
            cmd.get_subcommand_value_name()
                .unwrap_or_else(|| subcommand_heading(cmd))
                .to_lowercase(),
        ));
        line.push(roman(rhs));
    }

    roff.text(line);
}

pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
    let items: Vec<_> = cmd.get_arguments().filter(|i| !i.is_hide_set()).collect();

    for opt in items.iter().filter(|a| !a.is_positional()) {
        let mut header = match (opt.get_short(), opt.get_long()) {
            (Some(short), Some(long)) => {
                vec![short_option(short), roman(", "), long_option(long)]
            }
            (Some(short), None) => vec![short_option(short)],
            (None, Some(long)) => vec![long_option(long)],
            (None, None) => vec![],
        };

        if let Some(value) = &opt.get_value_names() {
            header.push(roman("="));
            header.push(italic(&value.join(" ")));
        }

        if let Some(defs) = option_default_values(opt) {
            header.push(roman(" "));
            header.push(roman(&defs));
        }

        let mut body = vec![];
        let mut arg_help_written = false;
        if let Some(help) = option_help(opt) {
            arg_help_written = true;
            body.push(roman(help.to_string()));
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);

        if let Some((possible_values_text, with_help)) = get_possible_values(opt) {
            if arg_help_written {
                // It looks nice to have a separation between the help and the values
                roff.text([Inline::LineBreak]);
            }
            if with_help {
                roff.text([Inline::LineBreak, italic("Possible values:")]);

                // Need to indent twice to get it to look right, because .TP heading indents, but
                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
                // size is 7 for terminal devices
                roff.control("RS", ["14"]);
                for line in possible_values_text {
                    roff.control("IP", ["\\(bu", "2"]);
                    roff.text([roman(line)]);
                }
                roff.control("RE", []);
            } else {
                let possible_value_text: Vec<Inline> = vec![
                    Inline::LineBreak,
                    roman("["),
                    italic("possible values: "),
                    roman(possible_values_text.join(", ")),
                    roman("]"),
                ];
                roff.text(possible_value_text);
            }
        }

        if let Some(env) = option_environment(opt) {
            roff.control("RS", []);
            roff.text(env);
            roff.control("RE", []);
        }
    }

    for pos in items.iter().filter(|a| a.is_positional()) {
        let mut header = vec![];
        let (lhs, rhs) = option_markers(pos);
        header.push(roman(lhs));
        if let Some(value) = pos.get_value_names() {
            header.push(italic(value.join(" ")));
        } else {
            header.push(italic(pos.get_id().as_str()));
        };
        header.push(roman(rhs));

        if let Some(defs) = option_default_values(pos) {
            header.push(roman(&format!(" {}", defs)));
        }

        let mut body = vec![];
        let mut arg_help_written = false;
        if let Some(help) = option_help(pos) {
            body.push(roman(&help.to_string()));
            arg_help_written = true;
        }

        roff.control("TP", []);
        roff.text(header);
        roff.text(body);

        if let Some(env) = option_environment(pos) {
            roff.control("RS", []);
            roff.text(env);
            roff.control("RE", []);
        }
        // If possible options are available
        if let Some((possible_values_text, with_help)) = get_possible_values(pos) {
            if arg_help_written {
                // It looks nice to have a separation between the help and the values
                roff.text([Inline::LineBreak]);
            }
            if with_help {
                roff.text([Inline::LineBreak, italic("Possible values:")]);

                // Need to indent twice to get it to look right, because .TP heading indents, but
                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
                // size is 7 for terminal devices
                roff.control("RS", ["14"]);
                for line in possible_values_text {
                    roff.control("IP", ["\\(bu", "2"]);
                    roff.text([roman(line)]);
                }
                roff.control("RE", []);
            } else {
                let possible_value_text: Vec<Inline> = vec![
                    Inline::LineBreak,
                    roman("["),
                    italic("possible values: "),
                    roman(possible_values_text.join(", ")),
                    roman("]"),
                ];
                roff.text(possible_value_text);
            }
        }
    }
}

pub(crate) fn subcommands(roff: &mut Roff, cmd: &clap::Command, section: &str) {
    for sub in cmd.get_subcommands().filter(|s| !s.is_hide_set()) {
        roff.control("TP", []);

        let name = format!("{}-{}({})", cmd.get_name(), sub.get_name(), section);
        roff.text([roman(&name)]);

        if let Some(about) = sub.get_about().or_else(|| sub.get_long_about()) {
            for line in about.to_string().lines() {
                roff.text([roman(line)]);
            }
        }
    }
}

pub(crate) fn version(cmd: &clap::Command) -> String {
    format!(
        "v{}",
        cmd.get_long_version()
            .or_else(|| cmd.get_version())
            .unwrap()
    )
}

pub(crate) fn after_help(roff: &mut Roff, cmd: &clap::Command) {
    if let Some(about) = cmd.get_after_long_help().or_else(|| cmd.get_after_help()) {
        for line in about.to_string().lines() {
            roff.text([roman(line)]);
        }
    }
}

fn subcommand_markers(cmd: &clap::Command) -> (&'static str, &'static str) {
    markers(cmd.is_subcommand_required_set())
}

fn option_markers(opt: &clap::Arg) -> (&'static str, &'static str) {
    markers(opt.is_required_set())
}

fn markers(required: bool) -> (&'static str, &'static str) {
    if required {
        ("<", ">")
    } else {
        ("[", "]")
    }
}

fn short_option(opt: char) -> Inline {
    bold(&format!("-{}", opt))
}

fn long_option(opt: &str) -> Inline {
    bold(&format!("--{}", opt))
}

fn option_help(opt: &clap::Arg) -> Option<&clap::builder::StyledStr> {
    if !opt.is_hide_long_help_set() {
        let long_help = opt.get_long_help();
        if long_help.is_some() {
            return long_help;
        }
    }
    if !opt.is_hide_short_help_set() {
        return opt.get_help();
    }

    None
}

fn option_environment(opt: &clap::Arg) -> Option<Vec<Inline>> {
    if opt.is_hide_env_set() {
        return None;
    } else if let Some(env) = opt.get_env() {
        return Some(vec![
            roman("May also be specified with the "),
            bold(env.to_string_lossy().to_owned()),
            roman(" environment variable. "),
        ]);
    }

    None
}

fn option_default_values(opt: &clap::Arg) -> Option<String> {
    if opt.is_hide_default_value_set() {
        return None;
    } else if !opt.get_default_values().is_empty() {
        let values = opt
            .get_default_values()
            .iter()
            .map(|s| s.to_string_lossy())
            .collect::<Vec<_>>()
            .join(",");

        return Some(format!("[default: {}]", values));
    }

    None
}

fn get_possible_values(arg: &clap::Arg) -> Option<(Vec<String>, bool)> {
    let possibles = &arg.get_possible_values();
    let possibles: Vec<&clap::builder::PossibleValue> =
        possibles.iter().filter(|pos| !pos.is_hide_set()).collect();

    if !(possibles.is_empty() || arg.is_hide_possible_values_set()) {
        return Some(format_possible_values(&possibles));
    }
    None
}

fn format_possible_values(possibles: &Vec<&clap::builder::PossibleValue>) -> (Vec<String>, bool) {
    let mut lines = vec![];
    let with_help = possibles.iter().any(|p| p.get_help().is_some());
    if with_help {
        for value in possibles {
            let val_name = value.get_name();
            match value.get_help() {
                Some(help) => lines.push(format!("{}: {}", val_name, help)),
                None => lines.push(val_name.to_string()),
            }
        }
    } else {
        lines.append(&mut possibles.iter().map(|p| p.get_name().to_string()).collect());
    }
    (lines, with_help)
}
<|endoftext|>


<commit_before>#![no_main]
#[macro_use]
extern crate libfuzzer_sys;

use lzma_rs::error::Result;
use std::io::Write;

fn decode_lzma(compressed: &[u8]) -> Result<Vec<u8>> {
    let mut decomp: Vec<u8> = Vec::new();
    lzma_rs::lzma_decompress(&mut std::io::Cursor::new(compressed), &mut decomp)?;
    Ok(decomp)
}

fn decode_lzma_stream(compressed: &[u8], chunk_size: usize) -> Result<Vec<u8>> {
    let mut stream = lzma_rs::decompress::Stream::new(Vec::new());
    for chunk in compressed.chunks(chunk_size) {
        stream.write_all(chunk).unwrap();
    }
    let decomp = stream.finish().unwrap();
    Ok(decomp)
}

fuzz_target!(|input: &[u8]| {
    if !input.is_empty() {
        let (chunk_size, input) = input.split_at(1);
        // use input length if chunk_size is zero because std::slice::chunks
        // will otherwise panic
        let chunk_size = if chunk_size[0] == 0 {
            input.len()
        } else {
            chunk_size[0] as usize
        };
        let mut compressed = Vec::new();
        lzma_rs::lzma_compress(&mut std::io::Cursor::new(input), &mut compressed).unwrap();
        let decompressed = decode_lzma(&compressed).unwrap();
        let decompressed_stream = decode_lzma_stream(&compressed, chunk_size).unwrap();
        if decompressed_stream.len() != decompressed.len() {
            panic!(
                "chunk size: {}, ref len: {}, act len: {}",
                chunk_size,
                decompressed.len(),
                decompressed_stream.len()
            );
        }
        assert_eq!(decompressed_stream, decompressed);
        assert_eq!(decompressed_stream, input);
    }
});
<commit_msg>Apply suggestion from clippy::unnecessary-wraps.<commit_after>#![no_main]
#[macro_use]
extern crate libfuzzer_sys;

use lzma_rs::error::Result;
use std::io::Write;

fn decode_lzma(compressed: &[u8]) -> Result<Vec<u8>> {
    let mut decomp: Vec<u8> = Vec::new();
    lzma_rs::lzma_decompress(&mut std::io::Cursor::new(compressed), &mut decomp)?;
    Ok(decomp)
}

fn decode_lzma_stream(compressed: &[u8], chunk_size: usize) -> Vec<u8> {
    let mut stream = lzma_rs::decompress::Stream::new(Vec::new());
    for chunk in compressed.chunks(chunk_size) {
        stream.write_all(chunk).unwrap();
    }
    stream.finish().unwrap()
}

fuzz_target!(|input: &[u8]| {
    if !input.is_empty() {
        let (chunk_size, input) = input.split_at(1);
        // use input length if chunk_size is zero because std::slice::chunks
        // will otherwise panic
        let chunk_size = if chunk_size[0] == 0 {
            input.len()
        } else {
            chunk_size[0] as usize
        };
        let mut compressed = Vec::new();
        lzma_rs::lzma_compress(&mut std::io::Cursor::new(input), &mut compressed).unwrap();
        let decompressed = decode_lzma(&compressed).unwrap();
        let decompressed_stream = decode_lzma_stream(&compressed, chunk_size);
        if decompressed_stream.len() != decompressed.len() {
            panic!(
                "chunk size: {}, ref len: {}, act len: {}",
                chunk_size,
                decompressed.len(),
                decompressed_stream.len()
            );
        }
        assert_eq!(decompressed_stream, decompressed);
        assert_eq!(decompressed_stream, input);
    }
});
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

use {Args, Context, MessagePart};

/// A placeholder for a value. Used by `PluralFormat`.
#[derive(Debug)]
pub struct PlaceholderFormat {
}

impl PlaceholderFormat {
    /// Construct a `PlaceholderFormat`.
    pub fn new() -> Self {
        PlaceholderFormat {}
    }
}

impl MessagePart for PlaceholderFormat {
    fn apply_format(&self,
                    ctx: &Context,
                    stream: &mut fmt::Write,
                    _args: Option<&Args>)
                    -> fmt::Result {
        if let Some(value) = ctx.placeholder_value {
            try!(write!(stream, "{}", value));
            Ok(())
        } else {
            Err(fmt::Error {})
        }
    }
}

#[cfg(test)]
mod tests {
    use super::PlaceholderFormat;
    use {Context, Message};

    #[test]
    fn it_works() {
        let ctx = Context { placeholder_value: Some(3), ..Context::default() };

        let msg = Message::new(vec![Box::new(PlaceholderFormat::new())]);

        let output = format_message!(ctx, &msg);
        assert_eq!("3", output);
    }
}
<commit_msg>clippy: Add Default impl for PlaceholderFormat.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;

use {Args, Context, MessagePart};

/// A placeholder for a value. Used by `PluralFormat`.
#[derive(Debug, Default)]
pub struct PlaceholderFormat {
}

impl PlaceholderFormat {
    /// Construct a `PlaceholderFormat`.
    pub fn new() -> Self {
        PlaceholderFormat::default()
    }
}

impl MessagePart for PlaceholderFormat {
    fn apply_format(&self,
                    ctx: &Context,
                    stream: &mut fmt::Write,
                    _args: Option<&Args>)
                    -> fmt::Result {
        if let Some(value) = ctx.placeholder_value {
            try!(write!(stream, "{}", value));
            Ok(())
        } else {
            Err(fmt::Error {})
        }
    }
}

#[cfg(test)]
mod tests {
    use super::PlaceholderFormat;
    use {Context, Message};

    #[test]
    fn it_works() {
        let ctx = Context { placeholder_value: Some(3), ..Context::default() };

        let msg = Message::new(vec![Box::new(PlaceholderFormat::new())]);

        let output = format_message!(ctx, &msg);
        assert_eq!("3", output);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::HashSet;

use serde_json;
use failure::Context;

use errors::prelude::*;
use services::ledger::merkletree::merkletree::MerkleTree;
use services::pool::merkle_tree_factory;
use services::pool::rust_base58::{FromBase58, ToBase58};
use services::pool::types::{CatchupReq, Message};

pub enum CatchupProgress {
    ShouldBeStarted(
        Vec<u8>, //target_mt_root
        usize, //target_mt_size
        MerkleTree,
    ),
    NotNeeded(MerkleTree),
    Restart(MerkleTree),
    InProgress,
}

pub fn build_catchup_req(merkle: &MerkleTree, target_mt_size: usize) -> IndyResult<Option<(String, String)>> {
    let txns_cnt = target_mt_size - merkle.count();

    if txns_cnt <= 0 {
        warn!("No transactions to catch up!");
        return Ok(None);
    }
    let seq_no_start = merkle.count() + 1;
    let seq_no_end = target_mt_size;

    let cr = CatchupReq {
        ledgerId: 0,
        seqNoStart: seq_no_start.clone(),
        seqNoEnd: seq_no_end.clone(),
        catchupTill: target_mt_size,
    };

    let req_id = format!("{}{}", seq_no_start, seq_no_end);

    let req_json = serde_json::to_string(&Message::CatchupReq(cr))
        .to_indy(IndyErrorKind::InvalidState, "Cannot serialize CatchupRequest")?;

    trace!("catchup_req msg: {:?}", req_json);
    Ok(Some((req_id, req_json)))
}

pub fn check_nodes_responses_on_status(nodes_votes: &HashMap<(String, usize, Option<Vec<String>>), HashSet<String>>,
                                       merkle_tree: &MerkleTree,
                                       node_cnt: usize,
                                       f: usize,
                                       pool_name: &str) -> IndyResult<CatchupProgress> {
    if let Some((most_popular_vote, votes_cnt)) = nodes_votes.iter().map(|(key, val)| (key, val.len())).max_by_key(|entry| entry.1) {
        let is_consensus_reached = votes_cnt == node_cnt - f;
        if is_consensus_reached {
            if most_popular_vote.0.eq("timeout") {
                return _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "Pool timeout"))
            }

            return _try_to_catch_up(most_popular_vote, merkle_tree).or_else(|err| {
                if merkle_tree_factory::drop_cache(pool_name).is_ok() {
                    let merkle_tree = merkle_tree_factory::create(pool_name)?;
                    _try_to_catch_up(most_popular_vote, &merkle_tree)
                } else {
                    Err(err)
                }
            });
        } else {
            let reps_cnt: usize = nodes_votes.values().map(HashSet::len).sum();
            let positive_votes_cnt = votes_cnt + (node_cnt - reps_cnt);
            let is_consensus_reachable = positive_votes_cnt < node_cnt - f;
            if is_consensus_reachable {
               return  _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "No consensus possible"))
            }
        }
    }
    Ok(CatchupProgress::InProgress)
}

fn _try_to_restart_catch_up(pool_name: &str, err: IndyError) -> IndyResult<CatchupProgress>{
    if merkle_tree_factory::drop_cache(pool_name).is_ok() {
        let merkle_tree = merkle_tree_factory::create(pool_name)?;
        return Ok(CatchupProgress::Restart(merkle_tree))
    } else {
        //TODO: maybe we should change the error, but it was made to escape changing of ErrorCode returned to client
        return Err(err);
    }
}

fn _try_to_catch_up(ledger_status: &(String, usize, Option<Vec<String>>), merkle_tree: &MerkleTree) -> IndyResult<CatchupProgress> {
    let &(ref target_mt_root, target_mt_size, ref hashes) = ledger_status;
    let cur_mt_size = merkle_tree.count();
    let cur_mt_hash = merkle_tree.root_hash().to_base58();

    if target_mt_size == cur_mt_size {
        if cur_mt_hash.eq(target_mt_root) {
            return Ok(CatchupProgress::NotNeeded(merkle_tree.clone()));
        } else {
            return Err(err_msg(IndyErrorKind::InvalidState,
                               "Ledger merkle tree is not acceptable for current tree."));
        }
    } else if target_mt_size > cur_mt_size {
        let target_mt_root = target_mt_root
            .from_base58()
            .map_err(|err| Context::new(err))
            .to_indy(IndyErrorKind::InvalidStructure, "Can't parse target MerkleTree hash from nodes responses")?; // FIXME: review kind

        match hashes {
            &None => (),
            &Some(ref hashes) => check_cons_proofs(merkle_tree, hashes, &target_mt_root, target_mt_size)?,
        };

        return Ok(CatchupProgress::ShouldBeStarted(target_mt_root, target_mt_size, merkle_tree.clone()));
    } else {
        return Err(err_msg(IndyErrorKind::InvalidState, "Local merkle tree greater than mt from ledger"));
    }
}

pub fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> IndyResult<()> {
    let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();

    for cons_proof in cons_proofs {
        let cons_proof: &String = cons_proof;

        bytes_proofs.push(
            cons_proof
                .from_base58()
                .map_err(|err| Context::new(err))
                .to_indy(IndyErrorKind::InvalidStructure, "Can't decode node consistency proof")? // FIXME: review kind
        );
    }

    if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
        return Err(err_msg(IndyErrorKind::InvalidState, "Consistency proof verification failed")); // FIXME: review kind
    }

    Ok(())
}<commit_msg>Add draft version of new check for consensus and appropriate TODO.<commit_after>use std::collections::HashMap;
use std::collections::HashSet;

use serde_json;
use failure::Context;

use errors::prelude::*;
use services::ledger::merkletree::merkletree::MerkleTree;
use services::pool::merkle_tree_factory;
use services::pool::rust_base58::{FromBase58, ToBase58};
use services::pool::types::{CatchupReq, Message};

pub enum CatchupProgress {
    ShouldBeStarted(
        Vec<u8>, //target_mt_root
        usize, //target_mt_size
        MerkleTree,
    ),
    NotNeeded(MerkleTree),
    Restart(MerkleTree),
    InProgress,
}

pub fn build_catchup_req(merkle: &MerkleTree, target_mt_size: usize) -> IndyResult<Option<(String, String)>> {
    let txns_cnt = target_mt_size - merkle.count();

    if txns_cnt <= 0 {
        warn!("No transactions to catch up!");
        return Ok(None);
    }
    let seq_no_start = merkle.count() + 1;
    let seq_no_end = target_mt_size;

    let cr = CatchupReq {
        ledgerId: 0,
        seqNoStart: seq_no_start.clone(),
        seqNoEnd: seq_no_end.clone(),
        catchupTill: target_mt_size,
    };

    let req_id = format!("{}{}", seq_no_start, seq_no_end);

    let req_json = serde_json::to_string(&Message::CatchupReq(cr))
        .to_indy(IndyErrorKind::InvalidState, "Cannot serialize CatchupRequest")?;

    trace!("catchup_req msg: {:?}", req_json);
    Ok(Some((req_id, req_json)))
}

pub fn check_nodes_responses_on_status(nodes_votes: &HashMap<(String, usize, Option<Vec<String>>), HashSet<String>>,
                                       merkle_tree: &MerkleTree,
                                       node_cnt: usize,
                                       f: usize,
                                       pool_name: &str) -> IndyResult<CatchupProgress> {
    //FIXME fetch not just most popular vote but most popular not a timeout
    if let Some((most_popular_vote, votes_cnt)) = nodes_votes.iter().map(|(key, val)| (key, val.len())).max_by_key(|entry| entry.1) {
        let is_consensus_reached =
            votes_cnt == node_cnt - f
                || votes_cnt == f + 1 && most_popular_vote.0.neq("timeout");
        if is_consensus_reached {
            if most_popular_vote.0.eq("timeout") {
                return _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "Pool timeout"))
            }

            return _try_to_catch_up(most_popular_vote, merkle_tree).or_else(|err| {
                if merkle_tree_factory::drop_cache(pool_name).is_ok() {
                    let merkle_tree = merkle_tree_factory::create(pool_name)?;
                    _try_to_catch_up(most_popular_vote, &merkle_tree)
                } else {
                    Err(err)
                }
            });
        } else {
            let reps_cnt: usize = nodes_votes.values().map(HashSet::len).sum();
            let positive_votes_cnt = votes_cnt + (node_cnt - reps_cnt);
            let is_consensus_reachable = positive_votes_cnt < node_cnt - f;
            if is_consensus_reachable {
                //TODO: maybe we should change the error, but it was made to escape changing of ErrorCode returned to client
               return  _try_to_restart_catch_up(pool_name, err_msg(IndyErrorKind::PoolTimeout, "No consensus possible"))
            }
        }
    }
    Ok(CatchupProgress::InProgress)
}

fn _try_to_restart_catch_up(pool_name: &str, err: IndyError) -> IndyResult<CatchupProgress>{
    if merkle_tree_factory::drop_cache(pool_name).is_ok() {
        let merkle_tree = merkle_tree_factory::create(pool_name)?;
        return Ok(CatchupProgress::Restart(merkle_tree))
    } else {
        return Err(err);
    }
}

fn _try_to_catch_up(ledger_status: &(String, usize, Option<Vec<String>>), merkle_tree: &MerkleTree) -> IndyResult<CatchupProgress> {
    let &(ref target_mt_root, target_mt_size, ref hashes) = ledger_status;
    let cur_mt_size = merkle_tree.count();
    let cur_mt_hash = merkle_tree.root_hash().to_base58();

    if target_mt_size == cur_mt_size {
        if cur_mt_hash.eq(target_mt_root) {
            return Ok(CatchupProgress::NotNeeded(merkle_tree.clone()));
        } else {
            return Err(err_msg(IndyErrorKind::InvalidState,
                               "Ledger merkle tree is not acceptable for current tree."));
        }
    } else if target_mt_size > cur_mt_size {
        let target_mt_root = target_mt_root
            .from_base58()
            .map_err(|err| Context::new(err))
            .to_indy(IndyErrorKind::InvalidStructure, "Can't parse target MerkleTree hash from nodes responses")?; // FIXME: review kind

        match hashes {
            &None => (),
            &Some(ref hashes) => check_cons_proofs(merkle_tree, hashes, &target_mt_root, target_mt_size)?,
        };

        return Ok(CatchupProgress::ShouldBeStarted(target_mt_root, target_mt_size, merkle_tree.clone()));
    } else {
        return Err(err_msg(IndyErrorKind::InvalidState, "Local merkle tree greater than mt from ledger"));
    }
}

pub fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> IndyResult<()> {
    let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();

    for cons_proof in cons_proofs {
        let cons_proof: &String = cons_proof;

        bytes_proofs.push(
            cons_proof
                .from_base58()
                .map_err(|err| Context::new(err))
                .to_indy(IndyErrorKind::InvalidStructure, "Can't decode node consistency proof")? // FIXME: review kind
        );
    }

    if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
        return Err(err_msg(IndyErrorKind::InvalidState, "Consistency proof verification failed")); // FIXME: review kind
    }

    Ok(())
}<|endoftext|>