1#![allow(unused)]
19
20pub mod base_change;
21pub mod datetime;
22pub mod dlog;
23pub mod file;
24pub mod format;
25
26use std::fmt::Display;
27use std::io::{self, Write};
28use std::str::FromStr;
29
30pub fn read_input<T>(prompt: Option<&str>) -> Result<T, String>
72where
73 T: FromStr + Default,
74 <T as FromStr>::Err: Display,
75{
76 if let Some(msg) = prompt {
77 print!("{}", msg);
78 io::stdout().flush().unwrap();
79 }
80
81 let mut input = String::new();
82 io::stdin()
83 .read_line(&mut input)
84 .expect("Failed to read line");
85
86 let trimmed = input.trim();
87
88 if trimmed.is_empty() {
89 return Ok(T::default());
90 }
91
92 trimmed.parse().map_err(|e| format!("Parse error: {}", e))
93}
94
95pub fn __delay_ms(ms: u64) {
97 std::thread::sleep(std::time::Duration::from_millis(ms));
98}
99
100pub mod helpers {
102 use std::collections::HashMap;
103 use std::env;
104 use std::fs;
105 use std::io;
106 use std::path::PathBuf;
107
108 use crate::format::{Color, Style, Stylize};
109
110 pub fn find_cargo_toml(start_path: &str) -> io::Result<PathBuf> {
112 let mut path = PathBuf::from(start_path);
113 loop {
114 path = match path.parent() {
115 Some(parent) => parent.to_path_buf(),
116 None => env::current_dir()?,
117 };
118
119 let cargo_path = path.join("Cargo.toml");
120 if cargo_path.exists() {
121 return Ok(cargo_path);
122 }
123
124 if path.as_os_str().is_empty() {
125 return Err(io::Error::new(
126 io::ErrorKind::NotFound,
127 "Cargo.toml not found in any parent directory",
128 ));
129 }
130 }
131 }
132
133 pub fn extract_app_data_with_sections<'a>(
134 data: &'a str,
135 sections: &[(&str, &[&str])],
136 ) -> HashMap<&'a str, HashMap<&'a str, String>> {
137 let mut app_data = HashMap::new();
138 let mut current_section = "";
139 let mut current_key = "";
140 let mut multi_line_value = String::new();
141
142 for line in data.lines() {
143 let trimmed_line = line.trim();
144
145 if trimmed_line.is_empty() || trimmed_line.starts_with('#') {
147 continue;
148 }
149
150 let line_without_comment = trimmed_line.split('#').next().unwrap().trim();
152
153 if line_without_comment.starts_with('[') && line_without_comment.ends_with(']') {
154 current_section = line_without_comment.trim_matches(&['[', ']'][..]);
155 } else if let Some((key, value)) = line_without_comment.split_once('=') {
156 let key = key.trim();
157 if sections
158 .iter()
159 .any(|&(s, keys)| s == current_section && keys.contains(&key))
160 {
161 let value = value.trim().trim_matches('"');
162 current_key = key;
163 if value.starts_with('[') && !value.ends_with(']') {
164 multi_line_value = value.to_string();
165 } else {
166 app_data
167 .entry(current_section)
168 .or_insert_with(HashMap::new)
169 .insert(key, value.to_string());
170 }
171 }
172 } else if !line_without_comment.is_empty() && !multi_line_value.is_empty() {
173 multi_line_value.push_str(line_without_comment);
174 if line_without_comment.ends_with(']') {
175 app_data
176 .entry(current_section)
177 .or_insert_with(HashMap::new)
178 .insert(
179 current_key,
180 multi_line_value.trim_matches(&['[', ']'][..]).to_string(),
181 );
182 multi_line_value.clear();
183 }
184 }
185 }
186
187 app_data
188 }
189
190 pub fn print_extracted_data(
191 app_data: &HashMap<&str, HashMap<&str, String>>,
192 skip_keys: &[&str],
193 ) {
194 for (section, data) in app_data {
195 println!("{}:", section.style(Style::Bold));
196 for (key, value) in data {
197 if !skip_keys.contains(key) {
198 println!("\t{key}: {}", value.style(Style::Italic).style(Style::Dim));
199 }
200 }
201 println!();
202 }
203 }
204}
205
206#[macro_export]
207macro_rules! app_dt {
208 ($file_path:expr $(, $($section:expr => [$($key:expr),+ $(,)?]),* $(,)?)?) => {{
209 use std::io::Write;
210 use $crate::format::*;
211 use $crate::helpers::{find_cargo_toml, extract_app_data_with_sections, print_extracted_data};
212
213 print!("\x1B[2J\x1B[1;1H");
215 let _ = std::io::stdout().flush();
216
217 let cargo_toml_path = find_cargo_toml($file_path).expect("Failed to find Cargo.toml");
219 let cargo_toml = std::fs::read_to_string(cargo_toml_path).expect("Failed to read Cargo.toml");
220
221 let all_data = extract_app_data_with_sections(&cargo_toml, &[
223 ("package", &["name", "version"]),
224 $( $(($section, &[$($key),+])),* )?
225 ]);
226
227 let package_data = all_data.get("package").expect("Failed to extract package data");
228
229 println!("{} v{}\n",
230 package_data.get("name").unwrap().color(Color::new(16, 192, 16)),
231 package_data.get("version").unwrap().color(Color::new(8, 64, 224)).style(Style::Italic),
232 );
233
234 if all_data.len() > 1 || all_data.get("package").map_or(false, |p| p.len() > 2) {
236 print_extracted_data(&all_data, &["name", "version"]);
237 }
238 }};
239}
240
241#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn some_useful_test() {
248 app_dt!(file!()); }
250}