Skip to main content

fyi_msg/
lib.rs

1/*!
2# FYI Msg
3
4[![docs.rs](https://img.shields.io/docsrs/fyi_msg.svg?style=flat-square&label=docs.rs)](https://docs.rs/fyi_msg/)
5<br>
6[![crates.io](https://img.shields.io/crates/v/fyi_msg.svg?style=flat-square&label=crates.io)](https://crates.io/crates/fyi_msg)
7[![ci](https://img.shields.io/github/actions/workflow/status/Blobfolio/fyi/ci.yaml?style=flat-square&label=ci)](https://github.com/Blobfolio/fyi/actions)
8[![deps.rs](https://deps.rs/crate/fyi_msg/latest/status.svg?style=flat-square&label=deps.rs)](https://deps.rs/crate/fyi_msg/)<br>
9[![license](https://img.shields.io/badge/license-wtfpl-ff1493?style=flat-square)](https://en.wikipedia.org/wiki/WTFPL)
10
11This crate contains the objects providing the heart of the [FYI command line application](https://github.com/blobfolio/fyi), namely [`Msg`], a simple struct for status-like messages that can be easily printed to `STDOUT` or `STDERR`.
12
13
14
15## Examples
16
17```
18use fyi_msg::{Msg, MsgKind};
19
20// One way.
21Msg::new(MsgKind::Success, "You did it!")
22    .with_newline(true)
23    .print();
24
25// Another equivalent way.
26Msg::success("You did it!").print();
27```
28
29For more usage examples, check out the `examples/msg` demo, which covers just about every common use case.
30
31
32
33## Macros
34
35| Macro | Equivalent |
36| ----- | ---------- |
37| `confirm!(…)` | `Msg::new(MsgKind::Confirm, "Some question…").prompt()` |
38
39
40
41## Optional Features
42
43| Feature | Description |
44| ------- | ----------- |
45| `fitted` | Enables [`Msg::fitted`] for obtaining a slice trimmed to a specific display width. |
46| `progress` | Enables [`Progless`], a thread-safe CLI progress bar displayer.
47| `timestamps` | Enables timestamp-related methods and flags like [`Msg::with_timestamp`]. |
48*/
49
50#![deny(unsafe_code)]
51
52#![deny(
53	clippy::allow_attributes_without_reason,
54	clippy::correctness,
55	unreachable_pub,
56)]
57
58#![warn(
59	clippy::complexity,
60	clippy::nursery,
61	clippy::pedantic,
62	clippy::perf,
63	clippy::style,
64
65	clippy::allow_attributes,
66	clippy::clone_on_ref_ptr,
67	clippy::create_dir,
68	clippy::filetype_is_file,
69	clippy::format_push_string,
70	clippy::get_unwrap,
71	clippy::impl_trait_in_params,
72	clippy::implicit_clone,
73	clippy::lossy_float_literal,
74	clippy::missing_assert_message,
75	clippy::missing_docs_in_private_items,
76	clippy::needless_raw_strings,
77	clippy::panic_in_result_fn,
78	clippy::pub_without_shorthand,
79	clippy::rest_pat_in_fully_bound_structs,
80	clippy::semicolon_inside_block,
81	clippy::str_to_string,
82	clippy::todo,
83	clippy::undocumented_unsafe_blocks,
84	clippy::unneeded_field_pattern,
85	clippy::unseparated_literal_suffix,
86	clippy::unwrap_in_result,
87
88	macro_use_extern_crate,
89	missing_copy_implementations,
90	missing_docs,
91	non_ascii_idents,
92	trivial_casts,
93	trivial_numeric_casts,
94	unused_crate_dependencies,
95	unused_extern_crates,
96	unused_import_braces,
97)]
98
99#![expect(clippy::redundant_pub_crate, reason = "Unresolvable.")]
100
101#![cfg_attr(docsrs, feature(doc_cfg))]
102
103
104
105mod ansi;
106mod msg;
107#[cfg(feature = "fitted")]   mod fitted;
108#[cfg(feature = "progress")] mod progress;
109
110pub use ansi::AnsiColor;
111pub use msg::{
112	Msg,
113	kind::MsgKind,
114};
115#[cfg(feature = "bin_kinds")]
116pub use msg::kind::CliCommandArg;
117
118#[cfg(feature = "fitted")]
119#[cfg_attr(docsrs, doc(cfg(feature = "fitted")))]
120pub use fitted::{
121	fit_to_width,
122	length_width,
123	width,
124};
125
126#[cfg(feature = "progress")]
127#[cfg_attr(docsrs, doc(cfg(feature = "progress")))]
128pub use progress::{
129	ba::BeforeAfter,
130	Progless,
131	error::ProglessError,
132	guard::ProglessTaskGuard,
133};
134
135// Re-export.
136pub use fyi_ansi;
137#[cfg_attr(docsrs, doc(cfg(feature = "signal-hook")))]
138#[cfg(feature = "signal-hook")] pub use signal_hook;
139
140#[cfg(test)] use brunch as _;
141#[cfg(test)] use rayon as _;
142
143#[macro_use]
144/// # Macros.
145mod macros {
146	#[macro_export(local_inner_macros)]
147	/// # Confirm.
148	///
149	/// This convenience macro prints a message, prompts the user for a
150	/// yes/no response, and interprets/returns that value as a `bool`.
151	///
152	/// ## Example
153	///
154	/// ```no_run
155	/// use fyi_msg::{confirm, Msg, MsgKind};
156	///
157	/// // The manual way:
158	/// if Msg::new(MsgKind::Confirm, "Do you like chickens?").prompt() {
159	///     println!("That's great! They like you too!");
160	/// }
161	///
162	/// // The macro way:
163	/// if confirm!("Do you like chickens?") {
164	///     println!("That's great! They like you too!");
165	/// }
166	/// ```
167	///
168	/// The following modifiers are supported:
169	/// * `@indent $literal`: indent the message `$literal` "tabs";
170	/// * `@stderr`: pop the question over STDERR (instead of STDOUT);
171	/// * `@yes`: default to "Y" (instead of "N");
172	///
173	/// ```no_run
174	/// # use fyi_msg::confirm;
175	/// // Indent one "tabs" (four spaces):
176	/// if confirm!(@indent 1 "Do you like chickens?") {
177	///     println!("    That's great! They like you too!");
178	/// }
179	///
180	/// // Print to STDERR instead of STDOUT:
181	/// if confirm!(@stderr "Do you like chickens?") {
182	///     println!("That's great! They like you too!");
183	/// }
184	///
185	/// // Default to yes instead of no.
186	/// if confirm!(@yes "Do you like chickens?") {
187	///     println!("That's great! They like you too!");
188	/// }
189	/// ```
190	///
191	/// Modifiers can be stacked together any which way.
192	///
193	/// ```no_run
194	/// # use fyi_msg::confirm;
195	/// // Indent three "tabs" _and_ print to STDERR _and_ default to yes:
196	/// if confirm!(@indent 3 @stderr @yes "Do you like chickens?") {
197	///     println!("            That's great! They like you too!");
198	/// }
199	///
200	/// // Same as above.
201	/// if confirm!(@stderr @indent 3 @yes "Do you like chickens?") {
202	///     println!("            That's great! They like you too!");
203	/// }
204	///
205	/// // Same again.
206	/// if confirm!(@stderr @yes @indent 3 "Do you like chickens?") {
207	///     println!("            That's great! They like you too!");
208	/// }
209	///
210	/// // …
211	/// ```
212	macro_rules! confirm {
213		// Maybe-indent.
214		($( @indent $indent:literal )? $text:expr) => (
215			$crate::Msg::new($crate::MsgKind::Confirm, $text)
216				$( .with_indent($indent) )?
217				.prompt()
218		);
219
220		// Maybe-indent, yes.
221		($( @indent $indent:literal )? @yes $text:expr) => (
222			$crate::Msg::new($crate::MsgKind::Confirm, $text)
223				$( .with_indent($indent) )?
224				.prompt_with_default(true)
225		);
226		// Maybe-indent, yes.
227		(@yes $( @indent $indent:literal )? $text:expr) => (
228			$crate::Msg::new($crate::MsgKind::Confirm, $text)
229				$( .with_indent($indent) )?
230				.prompt_with_default(true)
231		);
232
233		// Maybe-indent, STDERR.
234		($( @indent $indent:literal )? @stderr $text:expr) => (
235			$crate::Msg::new($crate::MsgKind::Confirm, $text)
236				$( .with_indent($indent) )?
237				.eprompt()
238		);
239		(@stderr $( @indent $indent:literal )? $text:expr) => (
240			$crate::Msg::new($crate::MsgKind::Confirm, $text)
241				$( .with_indent($indent) )?
242				.eprompt()
243		);
244
245		// STDERR, yes.
246		(@stderr @yes $text:expr) => (
247			$crate::Msg::new($crate::MsgKind::Confirm, $text)
248				.eprompt_with_default(true)
249		);
250		(@yes @stderr $text:expr) => (
251			$crate::Msg::new($crate::MsgKind::Confirm, $text)
252				.eprompt_with_default(true)
253		);
254
255		// Indent, STDERR, yes.
256		(@indent $indent:literal @stderr @yes $text:expr) => (
257			$crate::Msg::new($crate::MsgKind::Confirm, $text)
258				.with_indent($indent)
259				.eprompt_with_default(true)
260		);
261		(@indent $indent:literal @yes @stderr $text:expr) => (
262			$crate::Msg::new($crate::MsgKind::Confirm, $text)
263				.with_indent($indent)
264				.eprompt_with_default(true)
265		);
266		(@stderr @indent $indent:literal @yes $text:expr) => (
267			$crate::Msg::new($crate::MsgKind::Confirm, $text)
268				.with_indent($indent)
269				.eprompt_with_default(true)
270		);
271		(@stderr @yes @indent $indent:literal $text:expr) => (
272			$crate::Msg::new($crate::MsgKind::Confirm, $text)
273				.with_indent($indent)
274				.eprompt_with_default(true)
275		);
276		(@yes @indent $indent:literal @stderr $text:expr) => (
277			$crate::Msg::new($crate::MsgKind::Confirm, $text)
278				.with_indent($indent)
279				.eprompt_with_default(true)
280		);
281		(@yes @stderr @indent $indent:literal $text:expr) => (
282			$crate::Msg::new($crate::MsgKind::Confirm, $text)
283				.with_indent($indent)
284				.eprompt_with_default(true)
285		);
286	}
287}