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;