Skip to main content

j_cli/util/
log.rs

1/// 打印普通信息
2#[macro_export]
3macro_rules! info {
4    ($($arg:tt)*) => {{
5        println!($($arg)*)
6    }};
7}
8
9/// 打印错误信息
10#[macro_export]
11macro_rules! error {
12    ($($arg:tt)*) => {{
13        use colored::Colorize;
14        eprint!("{}", "[ERROR] ".red());
15        eprintln!($($arg)*)
16    }};
17}
18
19/// 打印 usage 提示
20#[macro_export]
21macro_rules! usage {
22    ($($arg:tt)*) => {{
23        use colored::Colorize;
24        print!("{}", "💡 Usage: ".green());
25        println!($($arg)*)
26    }};
27}
28
29/// 打印 debug 日志(仅 verbose 模式下输出)
30#[macro_export]
31macro_rules! debug_log {
32    ($config:expr, $($arg:tt)*) => {{
33        if $config.is_verbose() {
34            println!($($arg)*)
35        }
36    }};
37}
38
39/// 在终端中渲染 Markdown 文本
40/// 优先通过嵌入的 ask 二进制渲染(效果更佳),
41/// 如果不可用则 fallback 到 termimad
42#[macro_export]
43macro_rules! md {
44    ($($arg:tt)*) => {{
45        let text = format!($($arg)*);
46        $crate::util::log::render_md(&text);
47    }};
48}
49
50/// 在终端中渲染单行 Markdown(不换行,用于内联场景)
51#[macro_export]
52macro_rules! md_inline {
53    ($($arg:tt)*) => {{
54        let text = format!($($arg)*);
55        termimad::print_inline(&text);
56    }};
57}
58
59/// 打印分隔线
60#[allow(dead_code)]
61pub fn print_line() {
62    println!("- - - - - - - - - - - - - - - - - - - - - - -");
63}
64
65/// 首字母大写
66pub fn capitalize_first_letter(s: &str) -> String {
67    let mut chars = s.chars();
68    match chars.next() {
69        None => String::new(),
70        Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
71    }
72}
73
74/// 嵌入的 ask 二进制(macOS ARM64)
75/// 编译时从 plugin/ask/bin/ 目录嵌入
76#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
77const ASK_BINARY: &[u8] = include_bytes!("../../plugin/ask/bin/ask-darwin-arm64");
78
79/// 获取 ask 可执行文件路径
80/// 首次调用时释放嵌入的二进制到 ~/.jdata/bin/ask,后续复用
81fn get_ask_path() -> Option<std::path::PathBuf> {
82    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
83    {
84        return None;
85    }
86
87    #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
88    {
89        use std::os::unix::fs::PermissionsExt;
90
91        let data_dir = crate::config::YamlConfig::data_dir();
92        let bin_dir = data_dir.join("bin");
93        let ask_path = bin_dir.join("ask");
94
95        if ask_path.exists() {
96            // 已释放过,检查大小是否一致(版本更新时自动覆盖)
97            if let Ok(meta) = std::fs::metadata(&ask_path) {
98                if meta.len() == ASK_BINARY.len() as u64 {
99                    return Some(ask_path);
100                }
101            }
102        }
103
104        // 首次释放或版本更新,写入嵌入的二进制
105        if std::fs::create_dir_all(&bin_dir).is_err() {
106            return None;
107        }
108        if std::fs::write(&ask_path, ASK_BINARY).is_err() {
109            return None;
110        }
111        // 设置可执行权限 (chmod 755)
112        if let Ok(meta) = std::fs::metadata(&ask_path) {
113            let mut perms = meta.permissions();
114            perms.set_mode(0o755);
115            let _ = std::fs::set_permissions(&ask_path, perms);
116        }
117
118        Some(ask_path)
119    }
120}
121
122/// 渲染 Markdown 文本到终端
123/// 优先通过嵌入的 ask 二进制渲染(stdin → stdout,效果更佳),
124/// 如果不可用则 fallback 到 termimad
125pub fn render_md(text: &str) {
126    use std::io::Write;
127    use std::process::{Command, Stdio};
128
129    // 获取嵌入的 ask 二进制路径
130    let ask_path = get_ask_path();
131
132    if let Some(path) = ask_path {
133        // 调用 ask:直接从 stdin 读取 Markdown,渲染后输出 stdout
134        let result = Command::new(&path)
135            .stdin(Stdio::piped())
136            .stdout(Stdio::inherit())
137            .stderr(Stdio::inherit())
138            .spawn();
139
140        match result {
141            Ok(mut child) => {
142                if let Some(mut stdin) = child.stdin.take() {
143                    let _ = stdin.write_all(text.as_bytes());
144                    drop(stdin);
145                }
146                let _ = child.wait();
147                return;
148            }
149            Err(_) => {}
150        }
151    }
152
153    // fallback 到 termimad
154    termimad::print_text(text);
155}