@@ -55,7 +55,7 @@ pub enum Command {
5555 Stats ( StatsArgs ) ,
5656 #[ command( name = "view" , alias = "cat" , about = "Interactively browse filtered records in a terminal UI" ) ]
5757 View ( ViewArgs ) ,
58- #[ command( about = "Deduplicate log records, collapsing identical or similar bodies " ) ]
58+ #[ command( about = "Deduplicate log records across the whole selection or collapse nearby bursts " ) ]
5959 Dedup ( DedupArgs ) ,
6060}
6161
@@ -67,26 +67,36 @@ pub struct DedupArgs {
6767 #[ arg( short, long, value_name = "OUTPUT" , help = "Output .logjet file or - for stdout" ) ]
6868 pub output : PathBuf ,
6969
70- #[ arg( long, value_enum, default_value_t = DedupModeArg :: Hash2 ) ]
71- pub mode : DedupModeArg ,
70+ #[ arg( long, value_enum, default_value_t = DedupBehaviorArg :: Distinct , help = "Dedup behavior: distinct across the whole selection, or collapse nearby bursts" ) ]
71+ pub mode : DedupBehaviorArg ,
7272
73- #[ arg( long, value_name = "KEYS" , help = "Comma-separated bucket extensions: scope, source_line" ) ]
73+ #[ arg( long = "match" , alias = "matcher" , value_enum, default_value_t = DedupMatchArg :: Canon , help = "Matcher level inside the selected mode: exact, canon, or full" ) ]
74+ pub matcher : DedupMatchArg ,
75+
76+ #[ arg( long, value_name = "KEYS" , help = "Comma-separated bucket extensions: scope, source_line (used by collapse mode)" ) ]
7477 pub bucket_by : Option < String > ,
7578
76- #[ arg( long, value_name = "FLOAT" , help = "Drain3 similarity threshold (full mode only) [default: 0.7]" ) ]
79+ #[ arg( long, value_name = "FLOAT" , help = "Drain3 similarity threshold (full match only) [default: 0.7]" ) ]
7780 pub sim_th : Option < f64 > ,
7881
79- #[ arg( long, value_name = "INT" , help = "Drain3 prefix tree depth (full mode only) [default: 3]" ) ]
82+ #[ arg( long, value_name = "INT" , help = "Drain3 prefix tree depth (full match only) [default: 3]" ) ]
8083 pub drain_depth : Option < i64 > ,
8184
82- #[ arg( long, value_name = "DELIMS" , help = "Drain3 extra delimiters, comma-separated (full mode only)" ) ]
85+ #[ arg( long, value_name = "DELIMS" , help = "Drain3 extra delimiters, comma-separated (full match only)" ) ]
8386 pub extra_delimiters : Option < String > ,
8487}
8588
8689#[ derive( Debug , Clone , Copy , ValueEnum ) ]
87- pub enum DedupModeArg {
90+ pub enum DedupBehaviorArg {
91+ Distinct ,
92+ Collapse ,
93+ }
94+
95+ #[ derive( Debug , Clone , Copy , ValueEnum ) ]
96+ pub enum DedupMatchArg {
8897 Exact ,
89- Hash2 ,
98+ #[ value( alias = "hash2" ) ]
99+ Canon ,
90100 Full ,
91101}
92102
@@ -183,3 +193,26 @@ impl From<OutputCodec> for logjet::Codec {
183193 }
184194 }
185195}
196+
197+ #[ cfg( test) ]
198+ mod cli_utst {
199+ use super :: { Cli , Command , DedupBehaviorArg , DedupMatchArg } ;
200+ use clap:: Parser ;
201+
202+ #[ test]
203+ fn dedup_defaults_to_distinct_canon ( ) {
204+ let cli = Cli :: try_parse_from ( [ "ljx" , "dedup" , "input.logjet" , "-o" , "out.logjet" ] ) . expect ( "cli parses" ) ;
205+ let Command :: Dedup ( args) = cli. command else { panic ! ( "expected dedup command" ) } ;
206+ assert ! ( matches!( args. mode, DedupBehaviorArg :: Distinct ) ) ;
207+ assert ! ( matches!( args. matcher, DedupMatchArg :: Canon ) ) ;
208+ }
209+
210+ #[ test]
211+ fn dedup_accepts_collapse_and_full_matcher ( ) {
212+ let cli =
213+ Cli :: try_parse_from ( [ "ljx" , "dedup" , "input.logjet" , "-o" , "out.logjet" , "--mode" , "collapse" , "--match" , "full" ] ) . expect ( "cli parses" ) ;
214+ let Command :: Dedup ( args) = cli. command else { panic ! ( "expected dedup command" ) } ;
215+ assert ! ( matches!( args. mode, DedupBehaviorArg :: Collapse ) ) ;
216+ assert ! ( matches!( args. matcher, DedupMatchArg :: Full ) ) ;
217+ }
218+ }
0 commit comments