1use std::{
2 fs::OpenOptions,
3 io::{Read, Write},
4 path::{Path, PathBuf},
5};
6
7pub fn escape_html_double_quote(text: &str) -> String {
8 text.chars()
9 .map(|x| {
10 if x == '"' {
11 """.to_string()
12 } else {
13 x.to_string()
14 }
15 })
16 .collect::<String>()
17}
18
19pub fn escape_to_html(text: &str) -> String {
20 let mut html = String::new();
21 for ch in text.chars() {
22 match ch {
23 '&' => {
24 html.push_str("&");
25 }
26 '>' => {
27 html.push_str(">");
28 }
29 '<' => {
30 html.push_str("<");
31 }
32 _ => {
33 html.push(ch);
34 }
35 }
36 }
37 html
38}
39
40pub fn escape_to_tex(text: &str) -> String {
41 let mut content = String::new();
42 for ch in text.chars() {
43 match ch {
44 '$' => {
45 content.push_str(r#"\$"#);
46 }
47 '%' => {
48 content.push_str(r#"\%"#);
49 }
50 '`' => content.push_str(r#"\verb|`|"#),
51 '\\' => content.push_str(r#"\textbackslash"#),
52 _ => {
53 content.push(ch);
54 }
55 }
56 }
57 content
58}
59
60pub fn download_image_fs<S1, S2, P>(
63 url: S1,
64 dir: P,
65 name: S2,
66) -> Option<PathBuf>
67where
68 S1: AsRef<str>,
69 S2: AsRef<str>,
70 P: AsRef<Path>,
71{
72 let url = url.as_ref();
73 let dir = dir.as_ref();
74 let name = name.as_ref();
75 if let Some((content_type, data)) = download_image(url) {
76 let suffix = match content_type.as_str() {
77 "image/png" => "png",
78 "image/jpeg" => "jpg",
79 "image/svg+xml" => "svg",
80 _ => ".unknwon",
81 };
82 let name = name
84 .replace("%", "_")
85 .replace("/", "_")
86 .replace("\\", "_")
87 .replace(".", "_");
88 let output_path = dir.join(format!("{name}.{suffix}"));
89 let mut f = OpenOptions::new()
90 .truncate(true)
91 .write(true)
92 .create(true)
93 .open(&output_path)
94 .ok()?;
95 f.write_all(&data[..]).ok()?;
96 Some(output_path)
97 } else {
98 None
99 }
100}
101
102pub fn download_image<S: AsRef<str>>(url: S) -> Option<(String, Vec<u8>)> {
104 let url = url.as_ref();
105 let mut data: Vec<u8> = vec![];
106 match ureq::get(url).call() {
107 Ok(resp) => {
108 let content_type = resp.content_type().to_owned();
109 if let Err(e) =
111 resp.into_reader().take(10_000_000).read_to_end(&mut data)
112 {
113 log::error!("failed to read media data into buffer: {e:?}");
115 }
116 Some((content_type, data))
117 }
118 Err(e) => {
119 println!("error: {e:?} ==> {url}");
120 log::error!("failed to download media {} with error {e:?}", url);
122 None
123 }
124 }
125}
126
127pub fn remove_indent<S: AsRef<str>>(content: S) -> String {
129 let content = content.as_ref();
130 if !content.contains("\n") {
131 return content.trim_start().to_string();
132 }
133
134 let mut indent = content.len();
135 for line in content
136 .split_inclusive("\n")
137 .filter(|line| !line.trim().is_empty())
138 {
139 let current_indent = line.len() - line.trim_start().len();
140 if current_indent < indent {
141 indent = current_indent;
142 }
143 }
144 let content = content
145 .split_inclusive("\n")
146 .map(|line| {
147 if !line.trim().is_empty() {
148 &line[indent..]
149 } else {
150 line
151 }
152 })
153 .collect::<Vec<&str>>();
154 content.join("").to_string()
155}