Repository: pacak/bpaf
Pull request #229 (2023-07-04 14:24:46)

restore deduplication logic
Fixes https://github.com/pacak/bpaf/issues/224


Patch:
diff --git a/src/buffer.rs b/src/buffer.rs
index 7c0f342..93b80be 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -235,7 +235,7 @@ impl Doc {
 }
 
 /// Style of a text fragment inside of [`Doc`]
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
 pub enum Style {
     /// Plain text, no decorations
     Text,
@@ -253,7 +253,7 @@ pub enum Style {
     Invalid,
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
 pub(crate) enum Block {
     /// level 1 section header, block for separate command inside manpage, not used in --help
     Header,
@@ -292,14 +292,14 @@ pub(crate) enum Block {
     Meta,
 }
 
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
 pub(crate) enum Token {
     Text { bytes: usize, style: Style },
     BlockStart(Block),
     BlockEnd(Block),
 }
 
-#[derive(Debug, Clone, Default)]
+#[derive(Debug, Clone, Default, Eq, PartialEq, PartialOrd, Ord)]
 /// String with styled segments.
 ///
 /// You can add style information to generated documentation and help messages
diff --git a/src/info.rs b/src/info.rs
index 11ba369..8028c78 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -11,7 +11,7 @@ use crate::{
 /// Information about the parser
 ///
 /// No longer public, users are only interacting with it via [`OptionParser`]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
 #[doc(hidden)]
 pub struct Info {
     /// version field, see [`version`][Info::version]
diff --git a/src/item.rs b/src/item.rs
index 1e4f7e7..6ee3912 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -1,7 +1,7 @@
 use crate::{info::Info, meta_help::Metavar, parsers::NamedArg, Doc, Meta};
 
 #[doc(hidden)]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
 pub enum Item {
     Any {
         metavar: Doc,
@@ -60,7 +60,7 @@ impl Item {
 }
 
 #[doc(hidden)]
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq)]
 pub enum ShortLong {
     Short(char),
     Long(&'static str),
diff --git a/src/meta.rs b/src/meta.rs
index fe3f7ad..4553264 100644
--- a/src/meta.rs
+++ b/src/meta.rs
@@ -1,7 +1,7 @@
 use crate::{buffer::Doc, item::Item};
 
 #[doc(hidden)]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
 pub enum Meta {
     /// All arguments listed in a vector must be present
     And(Vec<Meta>),
@@ -244,7 +244,7 @@ impl Meta {
                 } else if let Meta::Many(many) = m.as_mut() {
                     // Optional(Many(Required(m))) => Many(Optional(m))
                     if let Meta::Required(x) = many.as_mut() {
-                        *self = Meta::Many(Box::new(Meta::Optional(std::mem::take(x))))
+                        *self = Meta::Many(Box::new(Meta::Optional(std::mem::take(x))));
                     }
                 }
             }
diff --git a/src/meta_help.rs b/src/meta_help.rs
index 28c790c..b141663 100644
--- a/src/meta_help.rs
+++ b/src/meta_help.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeSet;
+
 use crate::{
     buffer::{Block, Doc, Style, Token},
     info::Info,
@@ -6,10 +8,10 @@ use crate::{
 };
 
 #[doc(hidden)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
 pub struct Metavar(pub(crate) &'static str);
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
 pub(crate) enum HelpItem<'a> {
     DecorSuffix {
         help: &'a Doc,
@@ -100,7 +102,7 @@ pub(crate) struct HelpItems<'a> {
     pub(crate) items: Vec<HelpItem<'a>>,
 }
 
-#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord)]
 pub(crate) enum HiTy {
     Flag,
     Command,
@@ -570,8 +572,11 @@ impl Doc {
     #[inline(never)]
     pub(crate) fn write_help_item_groups(&mut self, mut items: HelpItems, include_env: bool) {
         while let Some(range) = items.find_group() {
+            let mut s = BTreeSet::new();
             for item in items.items.drain(range) {
-                write_help_item(self, &item, include_env);
+                if s.insert(item) {
+                    write_help_item(self, &item, include_env);
+                }
             }
         }
 
@@ -593,8 +598,11 @@ impl Doc {
             self.write_str(name, Style::Emphasis);
             self.token(Token::BlockEnd(Block::Section2));
             self.token(Token::BlockStart(Block::DefinitionList));
+            let mut s = BTreeSet::new();
             for item in xs {
-                write_help_item(self, item, include_env);
+                if s.insert(item) {
+                    write_help_item(self, item, include_env);
+                }
             }
             self.token(Token::BlockEnd(Block::DefinitionList));
             self.token(Token::BlockEnd(Block::Block));
diff --git a/src/meta_youmean.rs b/src/meta_youmean.rs
index c6ccbdd..fd497d8 100644
--- a/src/meta_youmean.rs
+++ b/src/meta_youmean.rs
@@ -72,7 +72,7 @@ pub(crate) fn suggest(args: &State, meta: &Meta) -> Option<(usize, Suggestion)>
                         }
                         HelpItem::Flag { name: nname, .. }
                         | HelpItem::Argument { name: nname, .. } => {
-                            if *nname == &actual {
+                            if *nname == actual.as_str() {
                                 nest = Some((name, Variant::Flag(*nname)));
                             }
                         }
diff --git a/src/params.rs b/src/params.rs
index 0c7be3f..147783c 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -103,7 +103,7 @@ use crate::{any, command, env, long, positional, short};
 ///    rules 1..3 `bpaf` would also parse environment variable `arg` which can be a string literal
 ///    or an expression.
 #[cfg_attr(not(doctest), doc = include_str!("docs2/named_arg_derive.md"))]
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
 pub struct NamedArg {
     pub(crate) short: Vec<char>,
     pub(crate) long: Vec<&'static str>,


Test patch:
diff --git a/tests/help_format.rs b/tests/help_format.rs
index 7e2a725..69df664 100644
--- a/tests/help_format.rs
+++ b/tests/help_format.rs
@@ -95,7 +95,6 @@ Available options:
     -a
     -c          c
     -b
-    -c          c
     -h, --help  Prints help information
 ";
 
@@ -129,7 +128,6 @@ Available options:
 }
 
 #[test]
-// no longer deduplicated
 fn duplicate_pos_items_same_help() {
     let a = short('a').req_flag(());
     let b = short('b').req_flag(());
@@ -146,7 +144,6 @@ Usage: (-a C | -b C)
 
 Available positional items:
     C           C
-    C           C
 
 Available options:
     -a