leetcode_api/render/
qs_detail.rs

1use 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::{Render, to_sub_sup_script};
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        // some content are not HTML
32        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}