mkutil/
lib.rs

1pub extern crate glob;
2pub extern crate regex;
3pub extern crate tempfile;
4pub extern crate unicode_names2;
5
6#[cfg(feature = "ron_deser")]
7pub extern crate ron;
8
9#[cfg(feature = "dag")]
10pub extern crate daggy;
11
12#[cfg(feature = "ldap")]
13pub extern crate ldap3;
14
15#[cfg(feature = "image_processing")]
16pub extern crate image;
17
18#[cfg(feature = "file_dialog")]
19pub extern crate rfd;
20
21#[cfg(feature = "grapheme")]
22pub extern crate unicode_segmentation;
23
24pub mod app_root_dir;
25mod base;
26pub mod clipboard;
27pub mod datetime;
28pub mod finder;
29// pub mod size;
30pub mod text;
31pub mod uploader;
32pub mod version;
33
34#[cfg(feature = "dag")]
35pub mod dag;
36
37#[cfg(feature = "file_dialog")]
38pub mod dialog;
39
40#[cfg(feature = "p4")]
41pub mod perforce;
42
43#[cfg(feature = "git")]
44pub mod git_cmd;
45
46#[cfg(feature = "html")]
47pub mod html_scraping;
48
49#[cfg(feature = "image_processing")]
50pub mod img_processing;
51
52#[cfg(feature = "ldap")]
53pub mod ldap;
54
55#[cfg(feature = "python")]
56pub mod py;
57
58#[cfg(feature = "sound")]
59pub mod sound;
60
61#[cfg(feature = "notify")]
62pub mod notify;
63
64#[cfg(feature = "grapheme")]
65pub mod grapheme;
66
67#[cfg(feature = "web_client")]
68pub mod web;
69
70pub use base::*;
71pub use uploader::{CopyOp, FileCopySpec, TemplateFile};
72
73use anyhow::{anyhow, Result as AnyResult};
74
75#[cfg(feature = "fern_log")]
76use fern::colors::{Color, ColoredLevelConfig};
77
78pub use log::{debug, error, info, warn};
79use serde::Deserialize;
80use std::{
81    collections::BTreeSet,
82    path::{Path, PathBuf},
83};
84
85/// A wrapper of `Result` to simplify display of info|error dialogs.
86pub type ResultInfo = Result<Option<Report>, Report>;
87
88// ----------------------------------------------------------------------------
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct Report {
91    title: String,
92    desc: String,
93}
94
95impl Report {
96    pub fn new(title: &str, desc: &str) -> Self {
97        Self::into_new(title, desc.to_owned())
98    }
99
100    pub fn into_new(title: &str, desc: String) -> Self {
101        Self {
102            title: title.to_owned(),
103            desc,
104        }
105    }
106}
107
108impl From<anyhow::Error> for Report {
109    fn from(err: anyhow::Error) -> Self {
110        Self::into_new("Context Error", err.to_string())
111    }
112}
113
114// ----------------------------------------------------------------------------
115pub fn unimplemented_not_panic() -> ResultInfo {
116    Err(Report::into_new("Error", "Unimplemented".to_owned()))
117}
118
119pub fn todo_not_panic() -> ResultInfo {
120    Ok(Some(Report::into_new("To Do", "Unimplemented".to_owned())))
121}
122
123pub fn ok_with_info(title: &str, desc: &str) -> ResultInfo {
124    Ok(Some(Report::new(title, desc)))
125}
126
127pub fn show_result_dialog(result_info: &ResultInfo) {
128    match result_info {
129        Ok(None) => {
130            // not showing info dialog
131        }
132        Ok(Some(_report)) => {
133            #[cfg(feature = "file_dialog")]
134            dialog::info_dialog(&_report.title, &_report.desc);
135        }
136        Err(_report) => {
137            #[cfg(feature = "file_dialog")]
138            dialog::error_dialog(&_report.title, &_report.desc);
139        }
140    }
141}
142
143// ----------------------------------------------------------------------------
144/// Constructs a `fern::Dispatch` that sends everything Debug and above to stdout.
145/// Outputting to another log file can be chained by using:
146/// `setup_logger()?.chain(fern::log_file("output.log")?)`
147#[cfg(feature = "fern_log")]
148pub fn setup_logger(level: log::LevelFilter) -> Result<fern::Dispatch, fern::InitError> {
149    let colors = ColoredLevelConfig::new()
150        .error(Color::Red)
151        .warn(Color::Yellow)
152        .info(Color::Green)
153        .debug(Color::Blue);
154    Ok(fern::Dispatch::new()
155        .format(move |out, message, record| {
156            out.finish(format_args!(
157                "{}[{}][{}] {}",
158                chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
159                record.target(),
160                colors.color(record.level()),
161                message
162            ))
163        })
164        .level(level)
165        .level_for("mkutil::finder", log::LevelFilter::Info)
166        .level_for("html5ever", log::LevelFilter::Warn)
167        .chain(std::io::stdout()))
168}
169
170/// This should only be invoked once.
171#[cfg(feature = "fern_log")]
172pub fn apply_fern_log(level: log::LevelFilter) {
173    let logger = setup_logger(level).unwrap();
174    logger.apply().unwrap();
175}
176
177/// Suppose we keep records of binary states in `state` by `key`, and whether we should
178/// add or remove depends on `should_add`.
179pub fn record_binary_state(state: &mut BTreeSet<String>, key: &str, should_add: bool) {
180    if should_add {
181        if !state.contains(key) {
182            state.insert(key.to_owned());
183        };
184    } else {
185        state.remove(key);
186    };
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    #[test]
193    fn get_app_root_dir() {
194        let root_dir = app_root_dir::application_root_dir();
195        eprintln!("App root dir: {:?}", root_dir);
196        assert!(root_dir.is_ok());
197    }
198}