11use clap:: Parser ;
2+ use strsim:: jaro_winkler;
23
34pub mod catalog;
45pub mod cli;
@@ -8,7 +9,7 @@ pub mod target;
89pub mod ui;
910
1011use catalog:: load_catalog;
11- use cli:: { Cli , Command , SetArgs } ;
12+ use cli:: { Cli , Command , SearchArgs , SetArgs } ;
1213use config:: { Paths , load_config, reset_config, save_config} ;
1314use error:: { Error , Result } ;
1415use target:: set_theme;
@@ -21,6 +22,7 @@ pub fn run() -> Result<()> {
2122 match cli. command {
2223 Command :: Config => show_config_dir ( & paths) ,
2324 Command :: List => list_themes ( & paths) ,
25+ Command :: Search ( args) => search_themes ( & paths, args) ,
2426 Command :: Current => show_current_theme ( & paths) ,
2527 Command :: Reset => handle_reset ( & paths) ,
2628 Command :: Set ( args) => handle_set_theme ( & paths, args) ,
@@ -39,12 +41,72 @@ fn list_themes(paths: &Paths) -> Result<()> {
3941
4042 println ! (
4143 "{}" ,
42- render_theme_table( config. current_theme. as_deref( ) , & catalog)
44+ render_theme_table(
45+ config. current_theme. as_deref( ) ,
46+ catalog
47+ . iter( )
48+ . map( |( name, mapping) | ( name. as_str( ) , mapping) ) ,
49+ )
4350 ) ;
4451
4552 Ok ( ( ) )
4653}
4754
55+ struct ThemeSearchMatch < ' a > {
56+ theme_name : String ,
57+ mapping : & ' a catalog:: ThemeMapping ,
58+ score : f64 ,
59+ }
60+
61+ fn search_themes ( paths : & Paths , args : SearchArgs ) -> Result < ( ) > {
62+ let SearchArgs { query, limit } = args;
63+
64+ let config = load_config ( paths) ?;
65+ let catalog = load_catalog ( paths) ?;
66+ let query = query. trim ( ) . to_lowercase ( ) ;
67+
68+ let matches = {
69+ let mut matches = catalog
70+ . iter ( )
71+ . filter_map ( |( theme, mapping) | {
72+ const MIN_SEARCH_SCORE : f64 = 0.70 ;
73+ let score = jaro_winkler ( & query, & theme. trim ( ) . to_lowercase ( ) ) ;
74+
75+ ( score >= MIN_SEARCH_SCORE ) . then ( || ThemeSearchMatch {
76+ theme_name : theme. clone ( ) ,
77+ score,
78+ mapping,
79+ } )
80+ } )
81+ . collect :: < Vec < _ > > ( ) ;
82+
83+ matches. sort_by ( |left, right| {
84+ right
85+ . score
86+ . total_cmp ( & left. score )
87+ . then_with ( || left. theme_name . cmp ( & right. theme_name ) )
88+ } ) ;
89+
90+ matches. truncate ( limit. max ( 1 ) ) ;
91+ matches
92+ } ;
93+
94+ if matches. is_empty ( ) {
95+ println ! ( "No matches found for `{query}`." ) ;
96+ } else {
97+ let table = render_theme_table (
98+ config. current_theme . as_deref ( ) ,
99+ matches
100+ . iter ( )
101+ . map ( |item| ( item. theme_name . as_str ( ) , item. mapping ) ) ,
102+ ) ;
103+
104+ println ! ( "{table}" , ) ;
105+ }
106+
107+ Ok ( ( ) )
108+ }
109+
48110fn show_current_theme ( paths : & Paths ) -> Result < ( ) > {
49111 let config = load_config ( paths) ?;
50112 let current = config
0 commit comments