apputils/lib.rs
1//! A lightweight Rust crate to help you build awesome tools
2//!
3//! It's designed to be framework-less and relatively simple while providing awesome helper functions for basic tasks that almost any program needs to do. These tasks include reading a config file with multiple paths (user and global), printing with color similar to `println!()` and getting user directories cross-platform.
4//!
5//! To add it to your dependencies, either run:
6//! ```bash
7//! cargo add apputils
8//! ```
9//!
10//! Or update your `Cargo.toml`:
11//! ```toml
12//! [dependencies]
13//! apputils = "0.1.0"
14//! ```
15//!
16//!
17//! ## Printing with color
18//! ```rust
19//! use apputils::Colors;
20//! use apputils::paintln;
21//!
22//! paintln!(Colors::White, "I'm white.");
23//! paintln!(Colors::Black, "I'm black.");
24//! paintln!(Colors::Yellow, "I'm yellow.");
25//! paintln!(Colors::Red, "I'm red.");
26//! paintln!(Colors::Rgb(35, 170, 242), "I'm #23AAF2.");
27//! ```
28//!
29//! ## Reading config and data files
30//! An example with [wallpaper-dl](https://crates.io/crates/wallpaper-dl)'s file structure:
31//! ```rust
32//! use apputils::config::{Cfg, Appdata};
33//! use apputils::dirs::{config_home, data_home};
34//!
35//! const APP_NAME: &str = "wallpaper-dl";
36//! const CONFIG_FILE: &str = "config.toml";
37//! const DB_FILE: &str = "wallpapers.toml";
38//!
39//! let cfg_path = Cfg::path(APP_NAME, CONFIG_FILE);
40//! let db_path = Appdata::path(APP_NAME, DB_FILE);
41//!
42//! assert_eq!(cfg_path, config_home().unwrap().join(APP_NAME).join(CONFIG_FILE)); // e.g. ~/.config/wallpaper-dl/config.toml
43//! assert_eq!(db_path, data_home().unwrap().join(APP_NAME).join(DB_FILE)); // e.g. ~/.local/share/wallpaper-dl/wallpapers.toml
44
45use std::fmt;
46
47/// Console Colors
48///
49/// This enum contains a member for every color, one for True Color via RGB, and a dummy to disable color in-place.
50pub enum Colors {
51 None,
52
53 Black,
54 BlackBold,
55
56 Red,
57 RedBold,
58
59 Green,
60 GreenBold,
61
62 Yellow,
63 YellowBold,
64
65 Blue,
66 BlueBold,
67
68 Magenta,
69 MagentaBold,
70
71 Cyan,
72 CyanBold,
73
74 White,
75 WhiteBold,
76
77 Rgb(u8, u8, u8),
78 RgbBold(u8, u8, u8)
79}
80
81impl Colors {
82 /// Pretty [print] an [Iterator]
83 ///
84 /// Pretty prints an [Iterable](IntoIterator) type with the given color and separator.
85 pub fn print<I: IntoIterator>(&self, sep: impl fmt::Display, vec: I)
86 where
87 <I as IntoIterator>::Item: fmt::Display,
88 {
89 let mut iter = vec.into_iter().peekable();
90 while let Some(item) = iter.next() {
91 paint!(self, "{item}");
92 if iter.peek().is_some() {
93 print!("{}", sep);
94 }
95 }
96 }
97
98 /// Pretty [println] an [Iterator]
99 ///
100 /// Like [print](Colors::print) but with a newline.
101 pub fn println<I: IntoIterator>(&self, sep: impl fmt::Display, vec: I)
102 where
103 <I as IntoIterator>::Item: fmt::Display,
104 {
105 self.print(sep, vec);
106 println!();
107 }
108}
109
110impl From<&Colors> for u8 {
111 fn from(val: &Colors) -> Self {
112 match val {
113 Colors::None => 0,
114 Colors::Black | Colors::BlackBold => 30,
115 Colors::Red | Colors::RedBold => 31,
116 Colors::Green | Colors::GreenBold => 32,
117 Colors::Yellow | Colors::YellowBold => 33,
118 Colors::Blue | Colors::BlueBold => 34,
119 Colors::Magenta | Colors::MagentaBold => 35,
120 Colors::Cyan | Colors::CyanBold => 36,
121 Colors::White | Colors::WhiteBold => 37,
122 Colors::Rgb(_, _, _) | Colors::RgbBold(_, _, _) => 38,
123 }
124 }
125}
126
127impl From<&Colors> for (u8, u8) {
128 fn from(val: &Colors) -> Self {
129 match val {
130 Colors::None
131 | Colors::Black
132 | Colors::Red
133 | Colors::Green
134 | Colors::Yellow
135 | Colors::Blue
136 | Colors::Magenta
137 | Colors::Cyan
138 | Colors::White
139 | Colors::Rgb(_, _, _) => (val.into(), 22),
140
141 Colors::BlackBold
142 | Colors::RedBold
143 | Colors::GreenBold
144 | Colors::YellowBold
145 | Colors::BlueBold
146 | Colors::MagentaBold
147 | Colors::CyanBold
148 | Colors::WhiteBold
149 | Colors::RgbBold(_, _, _) => (val.into(), 1),
150 }
151 }
152}
153
154impl fmt::Display for Colors {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 Colors::None => write!(f, ""),
158 Colors::Rgb(r, g, b) => write!(f, "\x1b[38;2;{};{};{}m", r, g, b),
159 Colors::RgbBold(r, g, b) => write!(f, "\x1b[38;2;{};{};{};1m", r, g, b),
160 _ => {
161 let cc: (u8, u8) = self.into();
162 write!(f, "\x1b[{};{}m", cc.0, cc.1)
163 }
164 }
165 }
166}
167
168/// [print!] with [Colors]
169///
170/// Drop-in replacement for the [print!]-macro from [std].
171#[macro_export]
172macro_rules! paint {
173 ($color:expr, $($arg:tt)*) => {{
174 print!("{}", $color);
175 print!($($arg)*);
176 print!("\x1b[0m");
177 }};
178}
179
180/// [println!] with [Colors]
181///
182/// Drop-in replacement for the [println!]-macro from [std].
183/// This macro is just a wrapper around the [paint!]-macro with an added newline.
184#[macro_export]
185macro_rules! paintln {
186 ($color:expr, $($arg:tt)*) => {{
187 $crate::paint!($color, $($arg)*);
188 println!("");
189 }};
190}
191
192/// Config file helpers
193///
194/// Functions to aid in loading and managing config files.
195/// It consists mostly of neat wrappers.
196pub mod config;
197
198/// User environment wrappers
199///
200/// This submodule's purpose is similar to [dirs](https://crates.io/crates/dirs), [directories](https://crates.io/crates/directories) or [xdg](https://crates.io/crates/xdg).
201///
202/// It currently just includes the XDG User Directories and a Windows translation of them.
203pub mod dirs;