1use crate::{cache::Cache, err::Error, helper::Digit};
3use clap::Args;
4
5static CATEGORY_HELP: &str = r#"Filter problems by category name
6[algorithms, database, shell, concurrency]
7"#;
8
9static QUERY_HELP: &str = r#"Filter questions by conditions:
10Uppercase means negative
11e = easy E = m+h
12m = medium M = e+h
13h = hard H = e+m
14d = done D = not done
15l = locked L = not locked
16s = starred S = not starred"#;
17
18static LIST_AFTER_HELP: &str = r#"EXAMPLES:
19 leetcode list List all questions
20 leetcode list array List questions that has "array" in name, and this is letter non-sensitive
21 leetcode list -c database List questions that in database category
22 leetcode list -q eD List questions that with easy level and not done
23 leetcode list -t linked-list List questions that under tag "linked-list"
24 leetcode list -r 50 100 List questions that has id in between 50 and 100
25"#;
26
27#[derive(Args)]
29#[command(after_help = LIST_AFTER_HELP)]
30pub struct ListArgs {
31 pub keyword: Option<String>,
33
34 #[arg(short, long, help = CATEGORY_HELP)]
36 pub category: Option<String>,
37
38 #[arg(short, long)]
40 pub plan: Option<String>,
41
42 #[arg(short, long, help = QUERY_HELP)]
44 pub query: Option<String>,
45
46 #[arg(short, long, num_args = 2.., value_parser = clap::value_parser!(i32))]
48 pub range: Vec<i32>,
49
50 #[arg(short, long)]
52 pub stat: bool,
53
54 #[arg(short, long)]
56 pub tag: Option<String>,
57}
58
59impl ListArgs {
60 pub async fn run(&self) -> Result<(), Error> {
62 trace!("Input list command...");
63
64 let cache = Cache::new()?;
65 let mut ps = cache.get_problems()?;
66
67 if ps.is_empty() {
69 cache.download_problems().await?;
70 return Box::pin(self.run()).await;
71 }
72
73 #[cfg(feature = "pym")]
76 {
77 if let Some(ref plan) = self.plan {
78 let ids = crate::pym::exec(plan)?;
79 crate::helper::squash(&mut ps, ids)?;
80 }
81 }
82
83 if let Some(ref tag) = self.tag {
85 let ids = cache.get_tagged_questions(tag).await?;
86 crate::helper::squash(&mut ps, ids)?;
87 }
88
89 if let Some(ref category) = self.category {
91 ps.retain(|x| x.category == *category);
92 }
93
94 if let Some(ref query) = self.query {
96 crate::helper::filter(&mut ps, query.to_string());
97 }
98
99 if self.range.len() >= 2 {
101 ps.retain(|x| self.range[0] <= x.fid && x.fid <= self.range[1]);
102 }
103
104 if let Some(ref keyword) = self.keyword {
106 let lowercase_kw = keyword.to_lowercase();
107 ps.retain(|x| x.name.to_lowercase().contains(&lowercase_kw));
108 }
109
110 ps.sort_unstable_by_key(|p| p.fid);
114
115 let out: Vec<String> = ps.iter().map(ToString::to_string).collect();
116 println!("{}", out.join("\n"));
117
118 if self.stat {
120 let mut listed = 0;
121 let mut locked = 0;
122 let mut starred = 0;
123 let mut ac = 0;
124 let mut notac = 0;
125 let mut easy = 0;
126 let mut medium = 0;
127 let mut hard = 0;
128
129 for p in ps {
130 listed += 1;
131 if p.starred {
132 starred += 1;
133 }
134 if p.locked {
135 locked += 1;
136 }
137
138 match p.status.as_str() {
139 "ac" => ac += 1,
140 "notac" => notac += 1,
141 _ => {}
142 }
143
144 match p.level {
145 1 => easy += 1,
146 2 => medium += 1,
147 3 => hard += 1,
148 _ => {}
149 }
150 }
151
152 let remain = listed - ac - notac;
153 println!(
154 "
155 Listed: {} Locked: {} Starred: {}
156 Accept: {} Not-Ac: {} Remain: {}
157 Easy : {} Medium: {} Hard: {}",
158 listed.digit(4),
159 locked.digit(4),
160 starred.digit(4),
161 ac.digit(4),
162 notac.digit(4),
163 remain.digit(4),
164 easy.digit(4),
165 medium.digit(4),
166 hard.digit(4),
167 );
168 }
169 Ok(())
170 }
171}