leetcode_api/render/
qs_detail.rs1use std::fmt::{Display, Write as _};
2
3use lcode_config::global::G_USER_CONFIG;
4#[cfg(feature = "ratatui")]
5use ratatui::{
6 style::{Style, Stylize},
7 text::{Line, Span},
8};
9
10use super::{to_sub_sup_script, Render};
11use crate::leetcode::question::qs_detail::Question;
12
13impl Render for Question {
14 fn to_md_str(&self, with_env: bool) -> String {
15 let content = if G_USER_CONFIG.config.translate {
16 self.translated_content
17 .as_deref()
18 .unwrap_or_default()
19 }
20 else {
21 self.content
22 .as_deref()
23 .unwrap_or_default()
24 };
25
26 let content = to_sub_sup_script(content)
27 .trim_matches('"')
28 .replace("\\n", "\n");
29 let env_info = self.env_info.to_string();
30
31 let md_str = if content.contains("<p>") {
33 html2text::from_read(content.as_bytes(), 80).map_or(content, |s| s)
34 }
35 else {
36 content
37 };
38 let t_case = format!("```txt\n{}\n```", self.example_testcases);
39 let mut res = format!(
40 "{qs}\n## Content\n\n{md}\n---\n\n## Test Case\n\n{test}\n",
41 qs = self,
42 md = md_str,
43 test = t_case
44 );
45
46 if !self.hints.is_empty() {
47 let join = self.hints.join("\n");
48 let hints = html2text::from_read(join.as_bytes(), 80).map_or(join, |s| s);
49 res = format!("{}\n\nhints:\n{}\n---\n", res, hints);
50 }
51 if !self.mysql_schemas.is_empty() {
52 let str = format!("\n```sql\n{}\n```\n", self.mysql_schemas.join("\n"));
53 res.push_str(&str);
54 }
55 if with_env {
56 let _ = write!(&mut res, "\n## EnvInfo\n\n{}", env_info);
57 }
58 res
59 }
60
61 #[cfg(feature = "ratatui")]
62 fn to_para_vec(&self) -> Vec<Line> {
63 use scraper::Html;
64
65 let content = if G_USER_CONFIG.config.translate {
66 self.translated_content
67 .as_deref()
68 .unwrap_or_else(|| {
69 self.content
70 .as_deref()
71 .unwrap_or_default()
72 })
73 }
74 else {
75 self.content
76 .as_deref()
77 .unwrap_or_default()
78 };
79
80 let content = to_sub_sup_script(content);
81
82 let frag = Html::parse_fragment(&content);
83 let res = frag
84 .root_element()
85 .text()
86 .fold(String::new(), |acc, e| acc + e);
87
88 let res = res
89 .replace("\\\"", "\"")
90 .replace("\\\\", "")
91 .replace("\\n", "\n")
92 .replace("\\t", " ")
93 .replace("\n\n\n", "\n");
94
95 let res = res
96 .trim_matches(|c| c == '"' || c == '\n' || c == ' ')
97 .split('\n')
98 .map(|v| -> Line<'_> { v.to_owned().into() });
99
100 let topic = self
101 .topic_tags
102 .iter()
103 .map(|v| {
104 if G_USER_CONFIG.config.translate {
105 v.translated_name
106 .as_ref()
107 .map_or_else(|| v.name.as_str(), |v| v.as_str())
108 }
109 else {
110 v.name.as_str()
111 }
112 })
113 .fold(String::new(), |acc, v| {
114 if acc.is_empty() {
115 return v.to_owned();
116 }
117 format!("{}, {}", acc, v)
118 });
119
120 let mut res1 = vec![
121 vec![
122 Span::styled("• ID: ", Style::default()),
123 Span::styled(self.question_id.as_str(), Style::default().bold()),
124 Span::styled(" | Passing rate: ", Style::default()),
125 Span::styled(self.stats.ac_rate.as_str(), Style::default().bold()),
126 Span::styled(" | PaidOnly: ", Style::default()),
127 Span::styled(self.is_paid_only.to_string(), Style::default().bold()),
128 Span::styled(" | Difficulty: ", Style::default()),
129 Span::styled(self.difficulty.as_str(), Style::default().bold()),
130 ]
131 .into(),
132 vec![
133 Span::styled("• Topic: ", Style::default().bold()),
134 Span::styled(topic, Style::default()),
135 ]
136 .into(),
137 vec![
138 Span::styled("• Url: ", Style::default()),
139 Span::styled(
140 G_USER_CONFIG.urls.get_qs_url(
141 self.qs_slug
142 .as_deref()
143 .unwrap_or_default(),
144 ),
145 Style::default().bold(),
146 ),
147 ]
148 .into(),
149 "".into(),
150 ];
151 res1.extend(res);
152
153 res1
154 }
155}
156
157impl Display for Question {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 let title = if G_USER_CONFIG.config.translate {
160 self.translated_title
161 .as_ref()
162 .map_or(self.title.as_str(), |v| v.as_str())
163 .trim_matches('"')
164 }
165 else {
166 self.title.as_str()
167 };
168
169 let topic = self
170 .topic_tags
171 .iter()
172 .map(|v| {
173 if G_USER_CONFIG.config.translate {
174 return v
175 .translated_name
176 .as_ref()
177 .map_or(v.name.as_str(), |translated_name| translated_name.as_str());
178 }
179 v.name.as_str()
180 })
181 .fold(String::new(), |acc, v| {
182 if acc.is_empty() {
183 return v.to_owned();
184 }
185 format!("{}, {}", acc, v)
186 });
187
188 format!(
189 "# {tit:62}\n\n* ID: [{id:07}]({url}) | Passing rate: {rt:.6} | PaidOnly: {pd:6} | \
190 Difficulty: {di}\n* Topic: {tp}\n",
191 tit = title,
192 id = self.question_id,
193 rt = self.stats.ac_rate,
194 pd = self.is_paid_only,
195 di = self.difficulty,
196 tp = topic,
197 url = G_USER_CONFIG.urls.get_qs_url(
198 self.qs_slug
199 .as_deref()
200 .unwrap_or_default()
201 )
202 )
203 .fmt(f)
204 }
205}