fn build_help_impl()

in unsupported/juno/crates/command_line/src/cl.rs [152:314]


    fn build_help_impl(
        &self,
        prog_name: &str,
        show_hidden: bool,
    ) -> Result<String, std::fmt::Error> {
        // Collect information about the options.
        let opts = self.as_slice();
        let mut have_options = false;

        // An entry per category, containing all text for that category
        let mut cats: Vec<Vec<(String, Option<String>)>> = Vec::new();
        // Positional argument description.
        let mut pos_descs = String::new();

        cats.resize(self.categories.len(), Vec::new());

        fn add_value_desc(buf: &mut String, desc: Option<&str>) {
            buf.push('<');
            if let Some(vd) = desc {
                buf.push_str(vd);
            } else {
                buf.push_str("value");
            }
            buf.push('>');
        }

        for opt in opts {
            let info = opt.info();

            if info.is_positional() {
                if !pos_descs.is_empty() {
                    pos_descs.push(' ');
                }
                if let Some(d) = info.desc {
                    pos_descs.push_str(d);
                } else {
                    add_value_desc(&mut pos_descs, info.value_desc);
                    if info.list {
                        pos_descs.push(cond!(info.min_count == 0, '*', '+'));
                    } else if info.min_count == 0 {
                        pos_descs.push('?');
                    }
                }
                continue;
            }
            have_options = true;

            // Skip hidden options.
            if info.hidden == Hidden::ReallyHidden || info.hidden == Hidden::Yes && !show_hidden {
                continue;
            }

            let cat_index = info.category;

            // If there is more than one category, the first time we append to it,
            // we must generate its name and description.
            if self.categories.len() > 1 && cats[cat_index].is_empty() {
                // An empty line before every category except the first.
                if cat_index != 0 {
                    cats[cat_index].push(("".into(), None));
                }
                // Name.
                cats[cat_index].push((
                    format!("{}:", self.categories[cat_index].name.clone()),
                    None,
                ));
                // Optional description.
                if let Some(desc) = &self.categories[cat_index].desc {
                    cats[cat_index].push((desc.clone(), None));
                }
                // Another empty line.
                cats[cat_index].push(("".into(), None));
            }

            let mut left = String::from("  ");
            if let Some(sn) = info.short {
                left.push('-');
                left.push_str(sn);
                if info.long.is_none()
                    && info.expected_value != ExpectedValue::Disallowed
                    && !info.is_enum_value()
                {
                    left.push(' ');
                    if info.expected_value == ExpectedValue::Optional {
                        left.push('[');
                    }
                    add_value_desc(&mut left, info.value_desc);
                    if info.expected_value == ExpectedValue::Optional {
                        left.push(']');
                    }
                }
            }
            if let Some(ln) = info.long {
                if info.short.is_some() {
                    left.push_str(", ");
                }
                left.push_str("--");
                left.push_str(ln);
                if info.expected_value != ExpectedValue::Disallowed && !info.is_enum_value() {
                    if info.expected_value == ExpectedValue::Optional {
                        left.push('[');
                    }
                    left.push('=');
                    add_value_desc(&mut left, info.value_desc);
                    if info.expected_value == ExpectedValue::Optional {
                        left.push(']');
                    }
                }
            }
            if info.is_enum_value() {
                // An enum value generates descriptions for all alternatives.
                cats[cat_index].push((left, Some(info.desc.unwrap_or("").to_string())));
                for alt in info.values_desc.unwrap() {
                    cats[cat_index].push((format!("    ={}", alt.0), Some(format!("  {}", alt.1))));
                }
            } else if info.is_enum_option() {
                // An enum option generates descriptions for all alternatives.
                left.push_str(if let Some(d) = info.desc {
                    d
                } else {
                    "Choose one of:"
                });
                cats[cat_index].push((left, None));
                for alt in info.values_desc.unwrap() {
                    cats[cat_index].push((
                        format!("    {}{}", cond!(alt.0.len() > 1, "--", "-"), alt.0),
                        Some(alt.1.clone()),
                    ));
                }
            } else {
                cats[cat_index].push((left, Some(info.desc.unwrap_or("").to_string())));
            }
        }

        let descs: Vec<(String, Option<String>)> = cats.iter().flatten().cloned().collect();
        drop(cats);

        // Calc the width of the left column.
        let left_width = descs.iter().fold(0, |left_w, p| {
            cond!(p.1.is_none(), left_w, left_w.max(p.0.len()))
        });

        let mut out = String::new();
        writeln!(out, "OVERVIEW: {}\n", self.desc)?;
        write!(out, "USAGE: {}", prog_name)?;
        if have_options {
            write!(out, " [options]")?;
        }
        if !pos_descs.is_empty() {
            write!(out, " {}", pos_descs)?;
        }
        writeln!(out, "\n")?;
        writeln!(out, "OPTIONS:\n")?;

        for (left, right) in &descs {
            match right {
                Some(r) if !r.is_empty() => writeln!(out, "{0:1$} - {2}", left, left_width, r)?,
                _ => writeln!(out, "{}", left)?,
            }
        }

        Ok(out)
    }