aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mingling_core/src/any.rs59
-rw-r--r--mingling_core/src/asset.rs1
-rw-r--r--mingling_core/src/asset/chain/error.rs22
-rw-r--r--mingling_core/src/asset/core_invokes.rs31
-rw-r--r--mingling_core/src/asset/global_resource.rs39
-rw-r--r--mingling_core/src/asset/lazy_resource.rs34
-rw-r--r--mingling_core/src/asset/node.rs8
-rw-r--r--mingling_core/src/comp.rs192
-rw-r--r--mingling_core/src/comp/comp_ctx.rs1
-rw-r--r--mingling_core/src/comp/flags.rs10
-rw-r--r--mingling_core/src/comp/shell_ctx.rs4
-rw-r--r--mingling_core/src/comp/suggest.rs43
-rw-r--r--mingling_core/src/lib.rs6
-rw-r--r--mingling_core/src/program.rs8
-rw-r--r--mingling_core/src/program/collection.rs4
-rw-r--r--mingling_core/src/program/collection/mock.rs12
-rw-r--r--mingling_core/src/program/config.rs235
-rw-r--r--mingling_core/src/program/error.rs2
-rw-r--r--mingling_core/src/program/exec.rs29
-rw-r--r--mingling_core/src/program/exec/error.rs48
-rw-r--r--mingling_core/src/program/flag.rs18
-rw-r--r--mingling_core/src/program/hook.rs121
-rw-r--r--mingling_core/src/program/hook/control_unit.rs35
-rw-r--r--mingling_core/src/program/once_exec.rs14
-rw-r--r--mingling_core/src/program/repl_exec.rs32
-rw-r--r--mingling_core/src/program/repl_exec/splitter.rs8
-rw-r--r--mingling_core/src/program/single_instance.rs3
-rw-r--r--mingling_core/src/program/string_vec.rs11
-rw-r--r--mingling_core/src/renderer/render_result.rs46
-rw-r--r--mingling_core/src/renderer/structural/error.rs2
30 files changed, 592 insertions, 486 deletions
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index e922e2e..1c4cabe 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -45,7 +45,7 @@ impl<G> AnyOutput<G> {
}
}
- /// Create an `AnyOutput` from a raw value with a manually specified member_id.
+ /// Create an `AnyOutput` from a raw value with a manually specified [`member_id`].
///
/// This function bypasses the [`Grouped`] trait, meaning the `member_id` you provide
/// does **not** have to match the actual concrete type `T`. The scheduler uses
@@ -75,16 +75,16 @@ impl<G> AnyOutput<G> {
///
/// The `TypeId` is set during construction (via [`AnyOutput::new`] or [`AnyOutput::new_bare`])
/// and is used for subsequent downcasting and type checking.
- pub fn type_id(&self) -> std::any::TypeId {
+ pub const fn type_id(&self) -> std::any::TypeId {
self.type_id
}
- /// Get the [`member_id`] of the concrete type stored in `inner`.
+ /// Get the `member_id` of the concrete type stored in `inner`.
///
- /// [`member_id`] is set during construction (via [`AnyOutput::new`] or [`AnyOutput::new_bare`])
+ /// `member_id` is set during construction (via [`AnyOutput::new`] or [`AnyOutput::new_bare`])
/// and identifies which variant of the output enum this value corresponds to.
/// The scheduler uses this value to dispatch the output to the correct next step.
- pub fn member_id(&self) -> G
+ pub const fn member_id(&self) -> G
where
G: Copy,
{
@@ -114,12 +114,12 @@ impl<G> AnyOutput<G> {
}
/// Route the output to the next Chain
- pub fn route_chain(self) -> ChainProcess<G> {
+ pub const fn route_chain(self) -> ChainProcess<G> {
ChainProcess::Ok((self, NextProcess::Chain))
}
/// Route the output to the Renderer, ending execution
- pub fn route_renderer(self) -> ChainProcess<G> {
+ pub const fn route_renderer(self) -> ChainProcess<G> {
ChainProcess::Ok((self, NextProcess::Renderer))
}
@@ -132,10 +132,9 @@ impl<G> AnyOutput<G> {
/// `member_id` before calling `restore`.
pub fn restore<T: 'static>(self) -> Option<T> {
if self.type_id == std::any::TypeId::of::<T>() {
- match self.inner.downcast::<T>() {
- Ok(boxed) => Some(*boxed),
- Err(_) => None,
- }
+ self.inner
+ .downcast::<T>()
+ .map_or_else(|_| None, |boxed| Some(*boxed))
} else {
None
}
@@ -159,9 +158,9 @@ impl<G> std::ops::DerefMut for AnyOutput<G> {
/// Chain exec result type
///
/// Stores `Ok` and `Err` types of execution results, used to notify the scheduler what to execute next
-/// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`NextProcess::Chain`](./enum.NextProcess.html)`))` to continue execution with this type next
-/// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`NextProcess::Renderer`](./enum.NextProcess.html)`))` to render this type next and output to the terminal
-/// - Returns `Err(`[`ChainProcessError`](./error/enum.ChainProcessError.html)`]` to terminate the program directly
+/// - Returns <code>Ok(([AnyOutput](./struct.AnyOutput.html), [NextProcess::Chain](./enum.NextProcess.html)))</code> to continue execution with this type next
+/// - Returns <code>Ok(([AnyOutput](./struct.AnyOutput.html), [NextProcess::Renderer](./enum.NextProcess.html)))</code> to render this type next and output to the terminal
+/// - Returns <code>Err([ChainProcessError](./error/enum.ChainProcessError.html)]</code> to terminate the program directly
pub enum ChainProcess<G> {
/// Indicates success, containing the output value and the next step to execute.
Ok((AnyOutput<G>, NextProcess)),
@@ -185,15 +184,15 @@ pub enum NextProcess {
impl std::fmt::Display for NextProcess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- NextProcess::Chain => write!(f, "Chain"),
- NextProcess::Renderer => write!(f, "Renderer"),
+ Self::Chain => write!(f, "Chain"),
+ Self::Renderer => write!(f, "Renderer"),
}
}
}
impl<G> From<AnyOutput<G>> for ChainProcess<G> {
fn from(value: AnyOutput<G>) -> Self {
- ChainProcess::Ok((value, NextProcess::Chain))
+ Self::Ok((value, NextProcess::Chain))
}
}
@@ -211,7 +210,7 @@ mod tests {
use super::*;
use crate::Grouped;
- /// Mock enum for testing AnyOutput
+ /// Mock enum for testing `AnyOutput`
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
enum MockGroup {
@@ -223,9 +222,9 @@ mod tests {
impl std::fmt::Display for MockGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- MockGroup::Alpha => write!(f, "Alpha"),
- MockGroup::Beta => write!(f, "Beta"),
- MockGroup::Gamma => write!(f, "Gamma"),
+ Self::Alpha => write!(f, "Alpha"),
+ Self::Beta => write!(f, "Beta"),
+ Self::Gamma => write!(f, "Gamma"),
}
}
}
@@ -242,7 +241,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
@@ -264,7 +263,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
@@ -285,7 +284,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
@@ -358,7 +357,7 @@ mod tests {
assert_eq!(any.member_id, MockGroup::Alpha);
assert_eq!(next, NextProcess::Chain);
}
- _ => panic!("Expected ChainProcess::Ok"),
+ ChainProcess::Err(_) => panic!("Expected ChainProcess::Ok"),
}
}
@@ -375,7 +374,7 @@ mod tests {
assert_eq!(any.member_id, MockGroup::Alpha);
assert_eq!(next, NextProcess::Renderer);
}
- _ => panic!("Expected ChainProcess::Ok"),
+ ChainProcess::Err(_) => panic!("Expected ChainProcess::Ok"),
}
}
@@ -417,7 +416,7 @@ mod tests {
assert_eq!(any.member_id, MockGroup::Alpha);
assert_eq!(next, NextProcess::Chain);
}
- _ => panic!("Expected ChainProcess::Ok"),
+ ChainProcess::Err(_) => panic!("Expected ChainProcess::Ok"),
}
}
@@ -451,7 +450,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
@@ -488,7 +487,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
@@ -504,7 +503,7 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
diff --git a/mingling_core/src/asset.rs b/mingling_core/src/asset.rs
index fc1c81b..527607a 100644
--- a/mingling_core/src/asset.rs
+++ b/mingling_core/src/asset.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::redundant_pub_crate)]
pub(crate) mod chain;
pub(crate) mod core_invokes;
pub(crate) mod dispatcher;
diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs
index ad64195..bb5f679 100644
--- a/mingling_core/src/asset/chain/error.rs
+++ b/mingling_core/src/asset/chain/error.rs
@@ -13,8 +13,8 @@ pub enum ChainProcessError {
impl std::fmt::Display for ChainProcessError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- ChainProcessError::Other(s) => write!(f, "Other error: {s}"),
- ChainProcessError::IO(e) => write!(f, "IO error: {e}"),
+ Self::Other(s) => write!(f, "Other error: {s}"),
+ Self::IO(e) => write!(f, "IO error: {e}"),
}
}
}
@@ -22,15 +22,15 @@ impl std::fmt::Display for ChainProcessError {
impl std::error::Error for ChainProcessError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
- ChainProcessError::IO(e) => Some(e),
- ChainProcessError::Other(_) => None,
+ Self::IO(e) => Some(e),
+ Self::Other(_) => None,
}
}
}
impl From<std::io::Error> for ChainProcessError {
fn from(e: std::io::Error) -> Self {
- ChainProcessError::IO(e)
+ Self::IO(e)
}
}
@@ -38,17 +38,15 @@ impl From<ProgramInternalExecuteError> for ChainProcessError {
fn from(value: ProgramInternalExecuteError) -> Self {
match value {
ProgramInternalExecuteError::DispatcherNotFound => {
- ChainProcessError::Other("DispatcherNotFound".into())
+ Self::Other("DispatcherNotFound".into())
}
ProgramInternalExecuteError::RendererNotFound(r) => {
- ChainProcessError::Other(format!("RendererNotFound: {r}"))
- }
- ProgramInternalExecuteError::Other(e) => ChainProcessError::Other(e),
- ProgramInternalExecuteError::IO(e) => {
- ChainProcessError::Other(format!("IOError: {e:?}"))
+ Self::Other(format!("RendererNotFound: {r}"))
}
+ ProgramInternalExecuteError::Other(e) => Self::Other(e),
+ ProgramInternalExecuteError::IO(e) => Self::Other(format!("IOError: {e:?}")),
ProgramInternalExecuteError::REPLPanic(program_panic) => {
- ChainProcessError::Other(format!("REPLPanic: {program_panic}"))
+ Self::Other(format!("REPLPanic: {program_panic}"))
}
}
}
diff --git a/mingling_core/src/asset/core_invokes.rs b/mingling_core/src/asset/core_invokes.rs
index 6a3da2d..07e5092 100644
--- a/mingling_core/src/asset/core_invokes.rs
+++ b/mingling_core/src/asset/core_invokes.rs
@@ -48,18 +48,17 @@ where
C: ProgramCollect<Enum = C> + 'static,
T: Grouped<C>,
{
- if !self.create_by_res_injection {
- panic!(
- "The current RendererInvoker was not created by the resource injection system, so it cannot be executed!"
- );
- }
+ assert!(
+ self.create_by_res_injection,
+ "The current RendererInvoker was not created by the resource injection system, so it cannot be executed!"
+ );
C::render(AnyOutput::new(value))
}
}
impl<T> ResourceMarker for RendererInvoker<T> {
fn __resource_marker_clone(&self) -> Self {
- RendererInvoker {
+ Self {
phantom: PhantomData,
create_by_res_injection: self.create_by_res_injection,
}
@@ -71,7 +70,7 @@ impl<T> ResourceMarker for RendererInvoker<T> {
// Reason: RendererInvoker is designed to only be created through resource injection.
// When the resource injection does not find a corresponding value,
// this method will be used to generate a default value to pass in.
- RendererInvoker {
+ Self {
phantom: PhantomData,
create_by_res_injection: true,
}
@@ -190,7 +189,7 @@ where
}
}
- /// Continuously execute the chain until it is rendered into a RenderResult
+ /// Continuously execute the chain until it is rendered into a `RenderResult`
///
/// This function can only be called when the `ChainInvoker` was created by the resource injection system
/// (i.e., via `__resource_marker_default`). If the invoker was created manually or cloned outside
@@ -198,7 +197,7 @@ where
///
/// # Special Behavior
///
- /// If an error occurs during rendering, or the result type does not contain a renderer, it will be rendered as an empty RenderResult
+ /// If an error occurs during rendering, or the result type does not contain a renderer, it will be rendered as an empty `RenderResult`
///
/// # Panics
///
@@ -229,19 +228,17 @@ where
}
}
- #[inline(always)]
fn pre_check(&self) {
- if !self.create_by_res_injection {
- panic!(
- "The current ChainInvoker was not created by the resource injection system, so it cannot be executed!"
- );
- }
+ assert!(
+ self.create_by_res_injection,
+ "The current ChainInvoker was not created by the resource injection system, so it cannot be executed!"
+ );
}
}
impl<T> ResourceMarker for ChainInvoker<T> {
fn __resource_marker_clone(&self) -> Self {
- ChainInvoker {
+ Self {
phantom: PhantomData,
create_by_res_injection: self.create_by_res_injection,
}
@@ -253,7 +250,7 @@ impl<T> ResourceMarker for ChainInvoker<T> {
// Reason: ChainInvoker is designed to only be created through resource injection.
// When the resource injection does not find a corresponding value,
// this method will be used to generate a default value to pass in.
- ChainInvoker {
+ Self {
phantom: PhantomData,
create_by_res_injection: true,
}
diff --git a/mingling_core/src/asset/global_resource.rs b/mingling_core/src/asset/global_resource.rs
index 18e4446..f37edd0 100644
--- a/mingling_core/src/asset/global_resource.rs
+++ b/mingling_core/src/asset/global_resource.rs
@@ -6,7 +6,10 @@ use std::{
use crate::{ChainProcess, Program, ProgramCollect, this};
-pub(crate) type GlobalResources = Arc<Mutex<HashMap<TypeId, Box<dyn Any + Sync + Send>>>>;
+/// A thread-safe, type-erased container for storing global resources keyed by their type.
+///
+/// Use `Program::with_resource` to insert resources and `Program::res` to retrieve them.
+pub type GlobalResources = Arc<Mutex<HashMap<TypeId, Box<dyn Any + Sync + Send>>>>;
impl<C> Program<C>
where
@@ -87,17 +90,16 @@ where
let Ok(mut guard) = self.resources.lock() else {
return Res::__resource_marker_default();
};
- if let Some(arc_res) = guard
+ guard
.get_mut(&TypeId::of::<Res>())
.and_then(|a| a.downcast_mut::<Arc<Res>>())
- {
- match Arc::try_unwrap(std::mem::take(arc_res)) {
- Ok(val) => val,
- Err(arc) => (*arc).__resource_marker_clone(),
- }
- } else {
- Res::__resource_marker_default()
- }
+ .map_or_else(
+ Res::__resource_marker_default,
+ |arc_res| match Arc::try_unwrap(std::mem::take(arc_res)) {
+ Ok(val) => val,
+ Err(arc) => (*arc).__resource_marker_clone(),
+ },
+ )
}
/// Internal syntax for the `&mut MyResource` syntax of async #[chain], do not use directly.
@@ -116,18 +118,23 @@ where
let guard = self.resources.lock().ok()?;
let boxed_any = guard.get(&TypeId::of::<Res>())?;
let arc_res = boxed_any.as_ref().downcast_ref::<Arc<Res>>()?;
- Some(GlobalResource::from(Arc::clone(arc_res)))
+ let result = GlobalResource::from(Arc::clone(arc_res));
+ drop(guard);
+ Some(result)
}
- /// Get a resource by type, returning `GlobalResource<Res>` if present
+ /// Get a resource by type, returning `GlobalResource<Res>` if present.
+ ///
+ /// If the resource is not present, returns the provided [`ChainProcess`] as an `Err`.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Err(route)` when the resource of type `Res` is not present in the store.
pub fn res_or_route<Res: 'static + Send + Sync>(
&self,
route: ChainProcess<C>,
) -> Result<GlobalResource<Res>, ChainProcess<C>> {
- match self.res() {
- Some(r) => Ok(r),
- None => Err(route),
- }
+ self.res().map_or_else(|| Err(route), Ok)
}
/// Get a resource by type, returning `GlobalResource<Res>` or inserting a default
diff --git a/mingling_core/src/asset/lazy_resource.rs b/mingling_core/src/asset/lazy_resource.rs
index 3cb7563..f2a5e3d 100644
--- a/mingling_core/src/asset/lazy_resource.rs
+++ b/mingling_core/src/asset/lazy_resource.rs
@@ -54,12 +54,10 @@ impl<T: Send + Sync + 'static> LazyRes<T> {
/// dropped. If the resource has not yet been initialized, the callback is stored
/// and will be invoked once initialization happens *and* the `LazyRes` is later
/// dropped.
+ #[must_use]
pub fn with_on_drop(mut self, on_drop: impl FnOnce(T) + Send + Sync + 'static) -> Self {
match &mut self.inner {
- LazyInner::Uninit(_, existing_opt) => {
- *existing_opt = Some(Box::new(on_drop));
- }
- LazyInner::Init(_, existing_opt) => {
+ LazyInner::Uninit(_, existing_opt) | LazyInner::Init(_, existing_opt) => {
*existing_opt = Some(Box::new(on_drop));
}
}
@@ -72,17 +70,14 @@ impl<T: Send + Sync + 'static> LazyRes<T> {
/// upon initialization.
pub fn set_on_drop(&mut self, on_drop: impl FnOnce(T) + Send + Sync + 'static) {
match &mut self.inner {
- LazyInner::Uninit(_, existing_opt) => {
- *existing_opt = Some(Box::new(on_drop));
- }
- LazyInner::Init(_, existing_opt) => {
+ LazyInner::Uninit(_, existing_opt) | LazyInner::Init(_, existing_opt) => {
*existing_opt = Some(Box::new(on_drop));
}
}
}
/// Returns `true` if the resource has been initialized.
- pub fn is_initialized(&self) -> bool {
+ pub const fn is_initialized(&self) -> bool {
matches!(&self.inner, LazyInner::Init(_, _))
}
@@ -157,6 +152,10 @@ impl<T: Send + Sync + 'static> LazyRes<T> {
///
/// If the resource has not been initialized, the initializer is called first.
/// This is different from `into_inner()` which returns `None` if uninitialized.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the resource has not been initialized.
pub fn unwrap(mut self) -> T {
match std::mem::replace(
&mut self.inner,
@@ -216,10 +215,7 @@ impl<T: Send + Sync + 'static> Drop for LazyRes<T> {
}
}
-impl<T: Send + Sync + 'static> Default for LazyRes<T>
-where
- T: Default,
-{
+impl<T: Send + Sync + Default + 'static> Default for LazyRes<T> {
/// Creates an uninitialized `LazyRes<T>` whose initializer returns `T::default()`.
fn default() -> Self {
Self::new(|| T::default())
@@ -237,6 +233,7 @@ impl<T: Send + Sync + 'static> From<T> for LazyRes<T> {
impl<T: Send + Sync + 'static> LazyRes<T> {
/// Creates a lazily initialized resource using `T::default()` as the initializer.
+ #[must_use]
pub fn lazy_default() -> Self
where
T: Default,
@@ -256,6 +253,7 @@ impl<T: Send + Sync + 'static> LazyRes<T> {
/// create a corresponding `LazyRes<T>`.
pub trait LazyInit: Send + Sync + 'static {
/// Creates a lazily initialized resource for this type using `Default` as the initializer.
+ #[must_use]
fn lazy_default() -> LazyRes<Self>
where
Self: Default,
@@ -274,10 +272,7 @@ pub trait LazyInit: Send + Sync + 'static {
impl<T: Send + Sync + 'static> LazyInit for T {}
-impl<T: Send + Sync + 'static> ResourceMarker for LazyRes<T>
-where
- T: Default + Clone,
-{
+impl<T: Send + Sync + 'static + Default + Clone> ResourceMarker for LazyRes<T> {
/// Clones the lazy resource. The cloned resource retains any initialized value,
/// but the initializer is reset to `T::default()`.
fn __resource_marker_clone(&self) -> Self {
@@ -290,10 +285,7 @@ where
}
/// Returns a default lazy resource (uninitialized, using `T::default()` as the initializer).
- fn __resource_marker_default() -> Self
- where
- T: Default,
- {
+ fn __resource_marker_default() -> Self {
Self::default()
}
diff --git a/mingling_core/src/asset/node.rs b/mingling_core/src/asset/node.rs
index caf34ec..b9002b6 100644
--- a/mingling_core/src/asset/node.rs
+++ b/mingling_core/src/asset/node.rs
@@ -12,24 +12,24 @@ pub struct Node {
impl Node {
/// Append a new part to the node path.
#[must_use]
- pub fn join(self, node: impl Into<String>) -> Node {
+ pub fn join(self, node: impl Into<String>) -> Self {
let mut new_node = self.node;
new_node.push(node.into());
- Node { node: new_node }
+ Self { node: new_node }
}
}
impl From<&str> for Node {
fn from(s: &str) -> Self {
let node = s.split('.').map(|part| kebab_case!(part)).collect();
- Node { node }
+ Self { node }
}
}
impl From<String> for Node {
fn from(s: String) -> Self {
let node = s.split('.').map(|part| kebab_case!(part)).collect();
- Node { node }
+ Self { node }
}
}
diff --git a/mingling_core/src/comp.rs b/mingling_core/src/comp.rs
index a49ea0c..7312a06 100644
--- a/mingling_core/src/comp.rs
+++ b/mingling_core/src/comp.rs
@@ -23,7 +23,10 @@ pub use shell_ctx::*;
#[doc(hidden)]
pub use suggest::*;
-use crate::{ProgramCollect, debug, metadata::Description, only_debug, this, trace};
+use crate::{ProgramCollect, debug, metadata::Description, this, trace};
+
+#[cfg(feature = "debug")]
+use crate::debug::init_env_logger;
#[cfg(not(feature = "dispatch_tree"))]
use crate::ChainProcess;
@@ -104,10 +107,11 @@ impl CompletionHelper {
where
P: ProgramCollect<Enum = P> + Display + PartialEq + 'static + std::fmt::Debug,
{
- only_debug! {
- crate::debug::init_env_logger();
+ #[cfg(feature = "debug")]
+ {
+ init_env_logger();
trace_ctx(ctx);
- };
+ }
// Everything before the first argument that matches a command node
// is treated as global parameters (flags and their values), which do
@@ -116,10 +120,7 @@ impl CompletionHelper {
// style invocations.
let all_args = ctx.all_words.iter().skip(1).cloned().collect::<Vec<_>>();
let first_cmd_match = first_command_arg_index::<P>(&all_args);
- let args = match first_cmd_match {
- Some(start) => all_args[start..].to_vec(),
- None => Vec::new(),
- };
+ let args = first_cmd_match.map_or_else(Vec::new, |start| all_args[start..].to_vec());
trace!("arguments=\"{}\"", args.join(", "));
#[cfg(not(feature = "dispatch_tree"))]
@@ -168,26 +169,8 @@ impl CompletionHelper {
None
};
- match suggest {
- Some(suggest) => {
- // A concrete entry was dispatched. Merge the entry's own
- // completion with the default subcommand suggestions so that,
- // e.g. `thanks <tab>`, suggests both the leaf nodes (`bob`,
- // `alice`) and the `thanks` entry's own completion.
- trace!("using custom completion: {:?}", suggest);
- let default = default_completion::<P>(ctx);
- if suggest == Suggest::FileCompletion {
- trace!(
- "custom completion is FileCompletion, using default: {:?}",
- default
- );
- default
- } else {
- trace!("combining custom completion with default");
- suggest.combine(default)
- }
- }
- None => {
+ suggest.map_or_else(
+ || {
if first_cmd_match.is_some() {
// A command node has been matched: the global
// EntryFallback must not run afterwards, only the
@@ -198,14 +181,45 @@ impl CompletionHelper {
trace!("using default completion");
let fallback = P::do_comp(&P::build_entry_fallback(vec![]), ctx);
let default = default_completion::<P>(ctx);
- if fallback == Suggest::FileCompletion {
+ match fallback {
+ Suggest::FileCompletion => default,
+ _ => fallback.combine(default),
+ }
+ }
+ },
+ |custom_suggest| {
+ // A concrete entry was dispatched. Merge the entry's own
+ // completion with the default subcommand suggestions so that,
+ // e.g. `thanks <tab>`, suggests both the leaf nodes (`bob`,
+ // `alice`) and the `thanks` entry's own completion.
+ trace!("using custom completion: {:?}", &custom_suggest);
+ let default = default_completion::<P>(ctx);
+ match custom_suggest {
+ Suggest::FileCompletion => {
+ trace!(
+ "custom completion is FileCompletion, using default: {:?}",
+ default
+ );
default
- } else {
- fallback.combine(default)
+ }
+ Suggest::Suggest(a) => {
+ trace!("combining custom completion with default");
+ match default {
+ Suggest::Suggest(b) => {
+ let mut combined = a;
+ combined.extend(b);
+ Suggest::Suggest(combined)
+ }
+ Suggest::FileCompletion => {
+ // If custom completion has items and default is file
+ // completion, the custom items take priority.
+ Suggest::Suggest(a)
+ }
+ }
}
}
- }
- }
+ },
+ )
}
/// Renders the completion suggestions to standard output.
@@ -219,7 +233,7 @@ impl CompletionHelper {
/// - If the suggestion is [`Suggest::Suggest`] with a set of candidates, it
/// formats and prints them according to the shell type (Zsh/PowerShell, Fish,
/// or default).
- pub fn render_suggest<P>(ctx: ShellContext, suggest: Suggest)
+ pub fn render_suggest<P>(ctx: &ShellContext, suggest: Suggest)
where
P: ProgramCollect<Enum = P> + Display + 'static,
{
@@ -296,7 +310,7 @@ where
let lazy_member = match match_user_input(this::<P>(), &words) {
Ok((dispatcher, args)) => match dispatcher.begin(args) {
ChainProcess::Ok((any, _)) => Some(any.member_id),
- _ => None,
+ ChainProcess::Err(_) => None,
},
Err(_) => None,
};
@@ -305,7 +319,10 @@ where
P::get_metadata::<Description>(member_id).map(String::from)
}
-fn default_completion<P>(ctx: &ShellContext) -> Suggest
+/// Builds suggestions from command nodes given an input path.
+///
+/// Extracted from `default_completion` to keep it under the line-count limit.
+fn build_node_suggestions<P>(ctx: &ShellContext, input_path: &[&str]) -> Suggest
where
P: ProgramCollect<Enum = P> + Display + 'static,
{
@@ -315,61 +332,22 @@ where
.filter(|(s, _)| !s.starts_with('_'))
.map(|(s, _)| s)
.collect();
- debug!("cmd_nodes: {:?}", cmd_nodes);
-
- // If the current position is less than 1, do not perform completion
- if ctx.word_index < 1 {
- debug!("word_index < 1, returning file suggestions");
- return file_suggest();
- }
-
- // Get the current input path
- let input_end = ctx.word_index.min(ctx.all_words.len());
-
- debug!(
- "input_path before filter: {:?}",
- &ctx.all_words.get(1..input_end).unwrap_or(&[])
- );
-
- // Skip global parameters (arguments before the first command node match)
- // when resolving the command path, so `prog [PARAM]... <subcommand>`
- // style invocations suggest the subcommand.
- let input_slice = ctx.all_words.get(1..input_end).unwrap_or(&[]);
- let input_path: Vec<&str> = match first_command_arg_index::<P>(input_slice) {
- Some(start) => input_slice[start..]
- .iter()
- .map(std::string::String::as_str)
- .collect(),
- None => Vec::new(),
- };
- debug!(
- "input_path={:?}, current_word='{}'",
- input_path, ctx.current_word
- );
- debug!("input_path after filter: {:?}", input_path);
-
- debug!(
- "default_completion: input_path = {:?}, word_index = {}, all_words = {:?}",
- input_path, ctx.word_index, ctx.all_words
- );
// Build a suggestion item for `token`, attaching the owning entry's
// `Description` metadata (resolved via `node_path`) when one is available.
let make_item = |token: &str, node_path: &str| -> SuggestItem {
- match entry_description::<P>(node_path) {
- Some(desc) => SuggestItem::new_with_desc(token.to_string(), desc),
- None => SuggestItem::new(token.to_string()),
- }
+ entry_description::<P>(node_path).map_or_else(
+ || SuggestItem::new(token.to_string()),
+ |desc| SuggestItem::new_with_desc(token.to_string(), desc),
+ )
};
// Track both the suggestion text and the node path used to look up its
// description, then deduplicate by suggestion text.
- let mut suggestions: std::collections::BTreeSet<SuggestItem> =
- std::collections::BTreeSet::new();
+ let mut suggestions: BTreeSet<SuggestItem> = BTreeSet::new();
// Special case: if input_path is empty, return all first-level commands
if input_path.is_empty() {
- debug!("input_path empty, returning first-level commands");
for node in cmd_nodes {
let node_parts: Vec<&str> = node.split(' ').collect();
if let Some(first) = node_parts.first() {
@@ -377,7 +355,6 @@ where
}
}
} else {
- debug!("input_path NOT empty, doing next-level suggestions");
// Get the current word
let current_word = input_path.last().unwrap();
@@ -396,10 +373,6 @@ where
// If suggestions for the current word are found, return directly
if !suggestions.is_empty() {
- debug!(
- "default_completion: current word suggestions = {:?}",
- suggestions
- );
return Suggest::Suggest(suggestions);
}
}
@@ -408,8 +381,6 @@ where
for node in cmd_nodes {
let node_parts: Vec<&str> = node.split(' ').collect();
- debug!("Checking node: '{}', parts: {:?}", node, node_parts);
-
// If input path is longer than node parts, skip
if input_path.len() > node_parts.len() {
continue;
@@ -463,8 +434,6 @@ where
}
}
- debug!("default_completion: suggestions = {:?}", suggestions);
-
if suggestions.is_empty() {
file_suggest()
} else {
@@ -472,8 +441,49 @@ where
}
}
-fn file_suggest() -> Suggest {
- trace!("file_suggest called");
+fn default_completion<P>(ctx: &ShellContext) -> Suggest
+where
+ P: ProgramCollect<Enum = P> + Display + 'static,
+{
+ debug!("cmd_nodes: {:?}", {
+ let nodes: BTreeSet<String> = this::<P>()
+ .get_nodes()
+ .into_iter()
+ .filter(|(s, _)| !s.starts_with('_'))
+ .map(|(s, _)| s)
+ .collect();
+ nodes
+ });
+
+ // If the current position is less than 1, do not perform completion
+ if ctx.word_index < 1 {
+ debug!("word_index < 1, returning file suggestions");
+ return file_suggest();
+ }
+
+ // Get the current input path
+ let input_end = ctx.word_index.min(ctx.all_words.len());
+
+ // Skip global parameters (arguments before the first command node match)
+ // when resolving the command path, so `prog [PARAM]... <subcommand>`
+ // style invocations suggest the subcommand.
+ let input_slice = ctx.all_words.get(1..input_end).unwrap_or(&[]);
+ let input_path: Vec<&str> =
+ first_command_arg_index::<P>(input_slice).map_or_else(Vec::new, |start| {
+ input_slice[start..]
+ .iter()
+ .map(std::string::String::as_str)
+ .collect()
+ });
+ debug!(
+ "input_path={:?}, current_word='{}'",
+ input_path, ctx.current_word
+ );
+
+ build_node_suggestions::<P>(ctx, &input_path)
+}
+
+const fn file_suggest() -> Suggest {
Suggest::FileCompletion
}
diff --git a/mingling_core/src/comp/comp_ctx.rs b/mingling_core/src/comp/comp_ctx.rs
index 8d7fa5c..6384909 100644
--- a/mingling_core/src/comp/comp_ctx.rs
+++ b/mingling_core/src/comp/comp_ctx.rs
@@ -10,6 +10,7 @@ where
/// (defined by [`COMPLETION_SUBCOMMAND`]) appears among the parsed arguments.
/// When `true`, the program should generate shell completions instead of
/// running its normal execution path.
+ #[must_use]
pub fn is_completing(&self) -> bool {
// Check if the first argument (args[1]) is the completion subcommand
self.args
diff --git a/mingling_core/src/comp/flags.rs b/mingling_core/src/comp/flags.rs
index 8aecf1b..0867122 100644
--- a/mingling_core/src/comp/flags.rs
+++ b/mingling_core/src/comp/flags.rs
@@ -23,11 +23,11 @@ pub enum ShellFlag {
impl From<String> for ShellFlag {
fn from(s: String) -> Self {
match s.trim().to_lowercase().as_str() {
- "zsh" => ShellFlag::Zsh,
- "bash" => ShellFlag::Bash,
- "fish" => ShellFlag::Fish,
- "pwsh" | "ps1" | "powershell" => ShellFlag::Powershell,
- other => ShellFlag::Other(snake_case!(other)),
+ "zsh" => Self::Zsh,
+ "bash" => Self::Bash,
+ "fish" => Self::Fish,
+ "pwsh" | "ps1" | "powershell" => Self::Powershell,
+ other => Self::Other(snake_case!(other)),
}
}
}
diff --git a/mingling_core/src/comp/shell_ctx.rs b/mingling_core/src/comp/shell_ctx.rs
index 734b5d2..ceb433f 100644
--- a/mingling_core/src/comp/shell_ctx.rs
+++ b/mingling_core/src/comp/shell_ctx.rs
@@ -64,7 +64,7 @@ impl TryFrom<Vec<String>> for ShellContext {
.map(|s| s.replace('^', "-"))
.collect();
- Ok(ShellContext {
+ Ok(Self {
command_line: command_line.replace('^', "-"),
cursor_position,
current_word: current_word.replace('^', "-"),
@@ -201,7 +201,7 @@ impl ShellContext {
}
#[cfg(not(target_os = "windows"))]
{
- self.current_word.starts_with("-")
+ self.current_word.starts_with('-')
}
}
diff --git a/mingling_core/src/comp/suggest.rs b/mingling_core/src/comp/suggest.rs
index 804d622..27b227f 100644
--- a/mingling_core/src/comp/suggest.rs
+++ b/mingling_core/src/comp/suggest.rs
@@ -18,15 +18,15 @@ pub enum Suggest {
}
impl Suggest {
- /// Creates a new Suggest variant containing a `BTreeSet` of suggestions.
+ /// Creates a new `Suggest` variant containing an empty `BTreeSet` of suggestions.
#[must_use]
- pub fn new() -> Self {
+ pub const fn new() -> Self {
Self::Suggest(BTreeSet::new())
}
/// Creates a `FileCompletion` variant.
#[must_use]
- pub fn file_comp() -> Self {
+ pub const fn file_comp() -> Self {
Self::FileCompletion
}
@@ -47,11 +47,12 @@ impl Suggest {
/// If both values are `Suggest::Suggest`, their `BTreeSet`s are merged
/// (all items from `other` are added into `self`). Otherwise, the first
/// `Suggest::Suggest` (or `FileCompletion`) is returned unchanged.
- pub fn combine(self, other: impl Into<Suggest>) -> Self {
+ #[must_use]
+ pub fn combine(self, other: impl Into<Self>) -> Self {
let other = other.into();
match (self, other) {
- (Suggest::Suggest(suggest), Suggest::Suggest(other)) => {
- Suggest::Suggest(suggest.into_iter().chain(other).collect())
+ (Self::Suggest(suggest), Self::Suggest(other)) => {
+ Self::Suggest(suggest.into_iter().chain(other).collect())
}
(suggest, _) => suggest,
}
@@ -109,10 +110,11 @@ impl Suggest {
/// A new `Suggest` value where each item's suggestion text is prefixed
/// with the given string. For example, `["foo", "bar"]` with prefix `"--"`
/// becomes `["--foo", "--bar"]`.
- pub fn add_prefix(self, prefix: impl Into<String>) -> Suggest {
+ #[must_use]
+ pub fn add_prefix(self, prefix: impl Into<String>) -> Self {
let suggest = match self {
- Suggest::Suggest(s) => s,
- Suggest::FileCompletion => return Suggest::FileCompletion,
+ Self::Suggest(s) => s,
+ Self::FileCompletion => return Self::FileCompletion,
};
let prefix = prefix.into();
let prefixed = suggest
@@ -123,7 +125,7 @@ impl Suggest {
new_item
})
.collect();
- Suggest::Suggest(prefixed)
+ Self::Suggest(prefixed)
}
/// Appends a suffix to every suggestion in the `Suggest` set.
@@ -142,10 +144,11 @@ impl Suggest {
/// A new `Suggest` value where each item's suggestion text is suffixed
/// with the given string. For example, `["foo", "bar"]` with suffix `"="`
/// becomes `["foo=", "bar="]`.
- pub fn add_suffix(self, suffix: impl Into<String>) -> Suggest {
+ #[must_use]
+ pub fn add_suffix(self, suffix: impl Into<String>) -> Self {
let suggest = match self {
- Suggest::Suggest(s) => s,
- Suggest::FileCompletion => return Suggest::FileCompletion,
+ Self::Suggest(s) => s,
+ Self::FileCompletion => return Self::FileCompletion,
};
let suffix = suffix.into();
let suffixed = suggest
@@ -156,7 +159,7 @@ impl Suggest {
new_item
})
.collect();
- Suggest::Suggest(suffixed)
+ Self::Suggest(suffixed)
}
}
@@ -170,7 +173,7 @@ where
.into_iter()
.map(|item| SuggestItem::new(item.into()))
.collect();
- Suggest::Suggest(suggests)
+ Self::Suggest(suggests)
}
}
@@ -213,7 +216,7 @@ pub enum SuggestItem {
impl Default for SuggestItem {
fn default() -> Self {
- SuggestItem::Simple(String::new())
+ Self::Simple(String::new())
}
}
@@ -232,13 +235,13 @@ impl Ord for SuggestItem {
impl SuggestItem {
/// Creates a new simple suggestion without description.
#[must_use]
- pub fn new(suggest: String) -> Self {
+ pub const fn new(suggest: String) -> Self {
Self::Simple(suggest)
}
/// Creates a new suggestion with a description.
#[must_use]
- pub fn new_with_desc(suggest: String, description: String) -> Self {
+ pub const fn new_with_desc(suggest: String, description: String) -> Self {
Self::WithDescription(suggest, description)
}
@@ -254,7 +257,7 @@ impl SuggestItem {
/// Returns the suggestion text.
#[must_use]
- pub fn suggest(&self) -> &String {
+ pub const fn suggest(&self) -> &String {
match self {
Self::Simple(suggest) | Self::WithDescription(suggest, _) => suggest,
}
@@ -269,7 +272,7 @@ impl SuggestItem {
/// Returns the description if present.
#[must_use]
- pub fn description(&self) -> Option<&String> {
+ pub const fn description(&self) -> Option<&String> {
match self {
Self::Simple(_) => None,
Self::WithDescription(_, description) => Some(description),
diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs
index 3db3d31..9de1f00 100644
--- a/mingling_core/src/lib.rs
+++ b/mingling_core/src/lib.rs
@@ -9,8 +9,12 @@
//!
//! Recommended to import [mingling](https://crates.io/crates/mingling) to use its features.
-// Private Modules
+#![deny(clippy::pedantic)]
+#![deny(clippy::nursery)]
+// Using `"".to_string()` is clearer than `String::new()` for expressing "creating an empty value"
+#![allow(clippy::manual_string_new)]
+// Private Modules
mod any;
mod asset;
mod program;
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index 11e1bbf..3c43cee 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -109,7 +109,7 @@ where
/// Creates a new Program instance with the provided command-line arguments.
pub fn new_with_args(args: impl Into<StringVec>) -> Self {
- Program {
+ Self {
collect: std::marker::PhantomData,
args: args.into().into(),
@@ -133,14 +133,14 @@ where
/// # Panics
///
/// Panics if the program has not been initialized yet.
- pub fn this_program() -> &'static Program<C>
+ pub fn this_program() -> &'static Self
where
C: 'static,
{
THIS_PROGRAM
.get_raw()
.unwrap()
- .downcast_ref::<Program<C>>()
+ .downcast_ref::<Self>()
.unwrap()
}
@@ -173,7 +173,7 @@ where
/// # Returns
///
/// The previous command-line arguments.
- pub fn replace_args(&mut self, args: Vec<String>) -> Vec<String> {
+ pub const fn replace_args(&mut self, args: Vec<String>) -> Vec<String> {
std::mem::replace(&mut self.args, args)
}
diff --git a/mingling_core/src/program/collection.rs b/mingling_core/src/program/collection.rs
index 5b1152a..66ec9f1 100644
--- a/mingling_core/src/program/collection.rs
+++ b/mingling_core/src/program/collection.rs
@@ -41,6 +41,10 @@ pub trait ProgramCollect {
#[cfg(not(feature = "dispatch_tree"))]
/// Use a prefix tree to quickly match arguments and dispatch to an Entry
+ ///
+ /// # Errors
+ ///
+ /// Returns an error if the program fails to execute the given arguments.
fn dispatch_args_trie(
_raw: &[String],
) -> Result<AnyOutput<Self::Enum>, crate::error::ProgramInternalExecuteError> {
diff --git a/mingling_core/src/program/collection/mock.rs b/mingling_core/src/program/collection/mock.rs
index cd2abf5..52fbd17 100644
--- a/mingling_core/src/program/collection/mock.rs
+++ b/mingling_core/src/program/collection/mock.rs
@@ -26,17 +26,17 @@ pub enum MockProgramCollect {
/// SAFETY: This is a mock type used only for temporary testing.
/// It will never actually enter the macro system.
/// The internal `panic!` ensures that `member_id` will never be executed.
-unsafe impl Grouped<MockProgramCollect> for MockProgramCollect {
- fn member_id() -> MockProgramCollect {
+unsafe impl Grouped<Self> for MockProgramCollect {
+ fn member_id() -> Self {
panic!("Attempting to read an unsafe enum type");
}
}
impl ProgramCollect for MockProgramCollect {
- type Enum = MockProgramCollect;
- type EntryFallback = MockProgramCollect;
- type ErrorRendererNotFound = MockProgramCollect;
- type ResultEmpty = MockProgramCollect;
+ type Enum = Self;
+ type EntryFallback = Self;
+ type ErrorRendererNotFound = Self;
+ type ResultEmpty = Self;
#[cfg(feature = "dispatch_tree")]
fn dispatch_args_trie(
diff --git a/mingling_core/src/program/config.rs b/mingling_core/src/program/config.rs
index c5f91da..b098961 100644
--- a/mingling_core/src/program/config.rs
+++ b/mingling_core/src/program/config.rs
@@ -1,41 +1,95 @@
+/// Output mode for error messages
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ErrorOutput {
+ /// Show error messages
+ Show,
+ /// Hide error messages
+ Hide,
+}
+
+/// Output mode for rendered results
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum RenderOutput {
+ /// Render results and output
+ Show,
+ /// Hide rendered results
+ Hide,
+}
+
+/// Panic message handling
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PanicSilence {
+ /// Allow panic messages to be shown
+ Show,
+ /// Silence panic messages
+ Silence,
+}
+
+/// Verbosity level for program output
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Verbosity {
+ /// Normal output
+ Normal,
+ /// Verbose output: provide detailed information
+ Verbose,
+ /// Quiet mode: suppress status messages, show only errors and results
+ Quiet,
+ /// Debug mode: output internal state and detailed diagnostics
+ Debug,
+}
+
+/// Color output mode
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ColorOutput {
+ /// Enable colored output
+ Enabled,
+ /// Disable colored output
+ Disabled,
+}
+
+/// Progress indicator mode
+///
+/// Automatically disabled when stdout is not a tty.
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ProgressOutput {
+ /// Show progress indicators (e.g. progress bars, spinners)
+ Enabled,
+ /// Hide progress indicators
+ Disabled,
+}
+
/// Program stdout settings
#[derive(Debug, Clone)]
pub struct ProgramStdoutSetting {
/// Output error messages
- pub error_output: bool,
+ pub error_output: ErrorOutput,
/// Render results and output
- pub render_output: bool,
+ pub render_output: RenderOutput,
/// Silence panic messages
- pub silence_panic: bool,
+ pub silence_panic: PanicSilence,
- /// Verbose output: provide detailed information
+ /// Verbosity level for program output
///
/// **NOTE**: Convention only, not a configuration
- pub verbose: bool,
-
- /// Quiet mode: suppress status messages, show only errors and results
- ///
- /// **NOTE**: Convention only, not a configuration
- pub quiet: bool,
-
- /// Debug mode: output internal state and detailed diagnostics
- ///
- /// **NOTE**: Convention only, not a configuration
- pub debug: bool,
+ pub verbosity: Verbosity,
/// Enable colored output
///
/// **NOTE**: Convention only, not a configuration
- pub color: bool,
+ pub color: ColorOutput,
/// Show progress indicators (e.g. progress bars, spinners)
///
- /// Automatically disabled when stdout is not a tty.
- ///
/// **NOTE**: Convention only, not a configuration
- pub progress: bool,
+ pub progress: ProgressOutput,
#[cfg(feature = "clap")]
/// Behavior when Clap Dispatcher outputs help information
@@ -63,21 +117,65 @@ pub enum ClapHelpPrintBehaviour {
impl Default for ProgramStdoutSetting {
fn default() -> Self {
- ProgramStdoutSetting {
- error_output: true,
- render_output: true,
- silence_panic: false,
- verbose: false,
- quiet: false,
- debug: false,
- color: true,
- progress: true,
+ Self {
+ error_output: ErrorOutput::Show,
+ render_output: RenderOutput::Show,
+ silence_panic: PanicSilence::Show,
+ verbosity: Verbosity::Normal,
+ color: ColorOutput::Enabled,
+ progress: ProgressOutput::Enabled,
#[cfg(feature = "clap")]
clap_help_print_behaviour: ClapHelpPrintBehaviour::default(),
}
}
}
+/// Confirmation mode for user prompts
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConfirmationMode {
+ /// Require confirmation from the user
+ Confirm,
+ /// Skip user confirmation step
+ Skip,
+}
+
+/// Execution mode
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ExecutionMode {
+ /// Normal execution
+ Normal,
+ /// Dry-run mode: simulate actions without making changes
+ DryRun,
+ /// Force execution, skipping safety checks
+ Force,
+}
+
+/// Interaction mode
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum InteractionMode {
+ /// Interactive terminal (has a tty)
+ Interactive,
+ /// Non-interactive terminal
+ NonInteractive,
+}
+
+/// Yes assumption mode for prompts
+///
+/// **NOTE**: Convention only, not a configuration
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum YesAssumption {
+ /// Do not assume "yes" for any prompt
+ None,
+ /// Assume "yes" for all confirmation prompts
+ AssumeYes,
+}
+
/// Program user context
#[derive(Debug, Clone)]
pub struct ProgramUserContext {
@@ -87,30 +185,25 @@ pub struct ProgramUserContext {
/// Execute hooks during the program lifecycle
pub run_hook: bool,
- /// Skip user confirmation step
+ /// Confirmation mode for user prompts
///
/// **NOTE**: Convention only, not a configuration
- pub confirm: bool,
+ pub confirmation: ConfirmationMode,
- /// Dry-run mode: simulate actions without making changes
+ /// Execution mode
///
/// **NOTE**: Convention only, not a configuration
- pub dry_run: bool,
-
- /// Force execution, skipping safety checks
- ///
- /// **NOTE**: Convention only, not a configuration
- pub force: bool,
+ pub execution: ExecutionMode,
/// Whether the program is running in an interactive terminal (has a tty)
///
/// **NOTE**: Convention only, not a configuration
- pub interactive: bool,
+ pub interaction: InteractionMode,
- /// Assume "yes" for all confirmation prompts
+ /// Whether to assume "yes" for all confirmation prompts
///
/// **NOTE**: Convention only, not a configuration
- pub assume_yes: bool,
+ pub yes_assumption: YesAssumption,
}
impl Default for ProgramUserContext {
@@ -118,11 +211,10 @@ impl Default for ProgramUserContext {
Self {
help: false,
run_hook: true,
- confirm: false,
- dry_run: false,
- force: false,
- interactive: false,
- assume_yes: false,
+ confirmation: ConfirmationMode::Confirm,
+ execution: ExecutionMode::Normal,
+ interaction: InteractionMode::NonInteractive,
+ yes_assumption: YesAssumption::None,
}
}
}
@@ -162,19 +254,19 @@ impl std::str::FromStr for StructuralRendererSetting {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match just_fmt::kebab_case!(s).as_str() {
- "disable" => Ok(StructuralRendererSetting::Disable),
+ "disable" => Ok(Self::Disable),
#[cfg(feature = "json_serde_fmt")]
- "json" => Ok(StructuralRendererSetting::Json),
+ "json" => Ok(Self::Json),
#[cfg(feature = "json_serde_fmt")]
- "json-pretty" => Ok(StructuralRendererSetting::JsonPretty),
+ "json-pretty" => Ok(Self::JsonPretty),
#[cfg(feature = "yaml_serde_fmt")]
- "yaml" => Ok(StructuralRendererSetting::Yaml),
+ "yaml" => Ok(Self::Yaml),
#[cfg(feature = "toml_serde_fmt")]
- "toml" => Ok(StructuralRendererSetting::Toml),
+ "toml" => Ok(Self::Toml),
#[cfg(feature = "ron_serde_fmt")]
- "ron" => Ok(StructuralRendererSetting::Ron),
+ "ron" => Ok(Self::Ron),
#[cfg(feature = "ron_serde_fmt")]
- "ron-pretty" => Ok(StructuralRendererSetting::RonPretty),
+ "ron-pretty" => Ok(Self::RonPretty),
_ => Err(format!("Invalid renderer: '{s}'")),
}
}
@@ -183,7 +275,7 @@ impl std::str::FromStr for StructuralRendererSetting {
#[cfg(feature = "structural_renderer")]
impl From<&str> for StructuralRendererSetting {
fn from(s: &str) -> Self {
- s.parse().unwrap_or(StructuralRendererSetting::Disable)
+ s.parse().unwrap_or(Self::Disable)
}
}
@@ -198,19 +290,19 @@ impl From<String> for StructuralRendererSetting {
impl std::fmt::Display for StructuralRendererSetting {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- StructuralRendererSetting::Disable => write!(f, "disable"),
+ Self::Disable => write!(f, "disable"),
#[cfg(feature = "json_serde_fmt")]
- StructuralRendererSetting::Json => write!(f, "json"),
+ Self::Json => write!(f, "json"),
#[cfg(feature = "json_serde_fmt")]
- StructuralRendererSetting::JsonPretty => write!(f, "json-pretty"),
+ Self::JsonPretty => write!(f, "json-pretty"),
#[cfg(feature = "yaml_serde_fmt")]
- StructuralRendererSetting::Yaml => write!(f, "yaml"),
+ Self::Yaml => write!(f, "yaml"),
#[cfg(feature = "toml_serde_fmt")]
- StructuralRendererSetting::Toml => write!(f, "toml"),
+ Self::Toml => write!(f, "toml"),
#[cfg(feature = "ron_serde_fmt")]
- StructuralRendererSetting::Ron => write!(f, "ron"),
+ Self::Ron => write!(f, "ron"),
#[cfg(feature = "ron_serde_fmt")]
- StructuralRendererSetting::RonPretty => write!(f, "ron-pretty"),
+ Self::RonPretty => write!(f, "ron-pretty"),
}
}
}
@@ -222,14 +314,12 @@ mod tests {
#[test]
fn program_stdout_setting_default() {
let s = ProgramStdoutSetting::default();
- assert!(s.error_output);
- assert!(s.render_output);
- assert!(!s.silence_panic);
- assert!(!s.verbose);
- assert!(!s.quiet);
- assert!(!s.debug);
- assert!(s.color);
- assert!(s.progress);
+ assert_eq!(s.error_output, ErrorOutput::Show);
+ assert_eq!(s.render_output, RenderOutput::Show);
+ assert_eq!(s.silence_panic, PanicSilence::Show);
+ assert_eq!(s.verbosity, Verbosity::Normal);
+ assert_eq!(s.color, ColorOutput::Enabled);
+ assert_eq!(s.progress, ProgressOutput::Enabled);
}
#[test]
@@ -237,11 +327,10 @@ mod tests {
let ctx = ProgramUserContext::default();
assert!(!ctx.help);
assert!(ctx.run_hook);
- assert!(!ctx.confirm);
- assert!(!ctx.dry_run);
- assert!(!ctx.force);
- assert!(!ctx.interactive);
- assert!(!ctx.assume_yes);
+ assert_eq!(ctx.confirmation, ConfirmationMode::Confirm);
+ assert_eq!(ctx.execution, ExecutionMode::Normal);
+ assert_eq!(ctx.interaction, InteractionMode::NonInteractive);
+ assert_eq!(ctx.yes_assumption, YesAssumption::None);
}
#[cfg(feature = "structural_renderer")]
diff --git a/mingling_core/src/program/error.rs b/mingling_core/src/program/error.rs
index 144b5ab..0a5bfd0 100644
--- a/mingling_core/src/program/error.rs
+++ b/mingling_core/src/program/error.rs
@@ -21,7 +21,7 @@ impl fmt::Display for ProgramPanic {
impl ProgramPanic {
#[must_use]
pub fn new(payload: Box<dyn Any + Send>) -> Self {
- ProgramPanic { payload }
+ Self { payload }
}
}
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index d9b4dd8..df42249 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -1,4 +1,5 @@
#![allow(clippy::borrowed_box)]
+#![allow(clippy::too_many_lines)]
use crate::{
AnyOutput, ChainProcess, Dispatcher, NextProcess, Program, ProgramCollect, RenderResult,
@@ -49,7 +50,7 @@ where
// Run hooks
control!(
- program.run_hook_pre_dispatch(crate::hook::HookPreDispatchInfo { arguments: args }),
+ program.run_hook_pre_dispatch(&crate::hook::HookPreDispatchInfo { arguments: args }),
current
);
@@ -62,7 +63,7 @@ where
// Run hook
control!(
- program.run_hook_post_dispatch(crate::hook::HookPostDispatchInfo {
+ program.run_hook_post_dispatch(&crate::hook::HookPostDispatchInfo {
entry: &current.member_id,
}),
current
@@ -75,7 +76,7 @@ where
let mut render_result = render_help::<C>(program, current);
// Run hook
- control!(program.run_hook_finish(crate::hook::HookFinishInfo {}));
+ control!(program.run_hook_finish(&crate::hook::HookFinishInfo {}));
render_result.exit_code = exit_code;
return Ok(render_result);
@@ -89,7 +90,7 @@ where
if C::has_chain(&current) {
// Run hook
control!(
- program.run_hook_pre_chain(crate::hook::HookPreChainInfo {
+ program.run_hook_pre_chain(&crate::hook::HookPreChainInfo {
input: &current.member_id,
raw: current.inner.as_ref(),
}),
@@ -102,7 +103,7 @@ where
let mut render_result = render::<C>(program, any);
// Run hook
- control!(program.run_hook_finish(crate::hook::HookFinishInfo {}));
+ control!(program.run_hook_finish(&crate::hook::HookFinishInfo {}));
render_result.exit_code = exit_code;
return Ok(render_result);
@@ -111,7 +112,7 @@ where
ChainProcess::Ok((mut any, NextProcess::Chain)) => {
// Run hook
control!(
- program.run_hook_post_chain(crate::hook::HookPostChainInfo {
+ program.run_hook_post_chain(&crate::hook::HookPostChainInfo {
output: &any
}),
any
@@ -121,7 +122,7 @@ where
ChainProcess::Err(e) => {
// Run hook
control!(
- program.run_hook_finish(crate::hook::HookFinishInfo {}),
+ program.run_hook_finish(&crate::hook::HookFinishInfo {}),
&mut C::build_empty_result()
);
return Err(e.into());
@@ -132,7 +133,7 @@ where
else if C::has_renderer(&current) {
// Run hook
control!(
- program.run_hook_pre_render(crate::hook::HookPreRenderInfo {
+ program.run_hook_pre_render(&crate::hook::HookPreRenderInfo {
input: &current.member_id,
raw: current.inner.as_ref(),
}),
@@ -143,12 +144,12 @@ where
// Run hooks
control!(
- program.run_hook_post_render(crate::hook::HookPostRenderInfo {
+ program.run_hook_post_render(&crate::hook::HookPostRenderInfo {
result: &render_result,
})
);
- control!(program.run_hook_finish(crate::hook::HookFinishInfo {}));
+ control!(program.run_hook_finish(&crate::hook::HookFinishInfo {}));
render_result.exit_code = exit_code;
return Ok(render_result);
@@ -168,7 +169,7 @@ where
// Run hook
control!(
- program.run_hook_finish(crate::hook::HookFinishInfo {}),
+ program.run_hook_finish(&crate::hook::HookFinishInfo {}),
current
);
render_result.exit_code = exit_code;
@@ -252,7 +253,7 @@ pub(crate) fn handle_program_control<C: ProgramCollect<Enum = C>>(
mut current: Option<&mut AnyOutput<C>>,
exit_code: &mut i32,
) -> Option<RenderResult> {
- for unit in controls.into_iter() {
+ for unit in controls {
match unit {
super::hook::ProgramControlUnit::OverrideExitCode(c) => *exit_code = c,
super::hook::ProgramControlUnit::RouteToChain(any_output) => {
@@ -264,7 +265,7 @@ pub(crate) fn handle_program_control<C: ProgramCollect<Enum = C>>(
// Note: Hooks triggered by ProgramControl will not trigger ProgramControl again
// Pre render
- let _ = program.run_hook_pre_render(crate::hook::HookPreRenderInfo {
+ let _ = program.run_hook_pre_render(&crate::hook::HookPreRenderInfo {
input: &any_output.member_id,
raw: any_output.inner.as_ref(),
});
@@ -273,7 +274,7 @@ pub(crate) fn handle_program_control<C: ProgramCollect<Enum = C>>(
r.exit_code = *exit_code;
// Post render
- program.run_hook_post_render(crate::hook::HookPostRenderInfo { result: &r });
+ program.run_hook_post_render(&crate::hook::HookPostRenderInfo { result: &r });
return Some(r);
}
diff --git a/mingling_core/src/program/exec/error.rs b/mingling_core/src/program/exec/error.rs
index 944e89a..c8cb15a 100644
--- a/mingling_core/src/program/exec/error.rs
+++ b/mingling_core/src/program/exec/error.rs
@@ -24,12 +24,12 @@ pub enum ProgramExecuteError {
impl fmt::Display for ProgramExecuteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- ProgramExecuteError::DispatcherNotFound => write!(f, "No Dispatcher Found"),
- ProgramExecuteError::RendererNotFound(s) => {
+ Self::DispatcherNotFound => write!(f, "No Dispatcher Found"),
+ Self::RendererNotFound(s) => {
write!(f, "No Renderer (`{s}`) Found")
}
- ProgramExecuteError::Panic(p) => write!(f, "Panic: {p:?}"),
- ProgramExecuteError::Other(s) => write!(f, "Other error: {s}"),
+ Self::Panic(p) => write!(f, "Panic: {p:?}"),
+ Self::Other(s) => write!(f, "Other error: {s}"),
}
}
}
@@ -38,7 +38,7 @@ impl std::error::Error for ProgramExecuteError {}
impl From<ProgramPanic> for ProgramExecuteError {
fn from(value: ProgramPanic) -> Self {
- ProgramExecuteError::Panic(value)
+ Self::Panic(value)
}
}
@@ -70,17 +70,11 @@ pub enum ProgramInternalExecuteError {
impl fmt::Display for ProgramInternalExecuteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- ProgramInternalExecuteError::DispatcherNotFound => {
- write!(f, "No Dispatcher Found")
- }
- ProgramInternalExecuteError::RendererNotFound(s) => {
- write!(f, "No Renderer (`{s}`) Found")
- }
- ProgramInternalExecuteError::Other(s) => write!(f, "Other error: {s}"),
- ProgramInternalExecuteError::IO(e) => write!(f, "IO error: {e}"),
- ProgramInternalExecuteError::REPLPanic(panic) => {
- write!(f, "A single REPL execution failed: {panic}")
- }
+ Self::DispatcherNotFound => write!(f, "No Dispatcher Found"),
+ Self::RendererNotFound(s) => write!(f, "No Renderer (`{s}`) Found"),
+ Self::Other(s) => write!(f, "Other error: {s}"),
+ Self::IO(e) => write!(f, "IO error: {e}"),
+ Self::REPLPanic(panic) => write!(f, "A single REPL execution failed: {panic}"),
}
}
}
@@ -88,7 +82,7 @@ impl fmt::Display for ProgramInternalExecuteError {
impl std::error::Error for ProgramInternalExecuteError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
- ProgramInternalExecuteError::IO(e) => Some(e),
+ Self::IO(e) => Some(e),
_ => None,
}
}
@@ -96,23 +90,19 @@ impl std::error::Error for ProgramInternalExecuteError {
impl From<std::io::Error> for ProgramInternalExecuteError {
fn from(e: std::io::Error) -> Self {
- ProgramInternalExecuteError::IO(e)
+ Self::IO(e)
}
}
impl From<ProgramInternalExecuteError> for ProgramExecuteError {
fn from(value: ProgramInternalExecuteError) -> Self {
match value {
- ProgramInternalExecuteError::DispatcherNotFound => {
- ProgramExecuteError::DispatcherNotFound
- }
- ProgramInternalExecuteError::RendererNotFound(s) => {
- ProgramExecuteError::RendererNotFound(s)
- }
- ProgramInternalExecuteError::Other(s) => ProgramExecuteError::Other(s),
- ProgramInternalExecuteError::IO(e) => ProgramExecuteError::Other(format!("{e}")),
+ ProgramInternalExecuteError::DispatcherNotFound => Self::DispatcherNotFound,
+ ProgramInternalExecuteError::RendererNotFound(s) => Self::RendererNotFound(s),
+ ProgramInternalExecuteError::Other(s) => Self::Other(s),
+ ProgramInternalExecuteError::IO(e) => Self::Other(format!("{e}")),
ProgramInternalExecuteError::REPLPanic(p) => {
- ProgramExecuteError::Other(format!("A single REPL execution failed: {p}"))
+ Self::Other(format!("A single REPL execution failed: {p}"))
}
}
}
@@ -121,8 +111,8 @@ impl From<ProgramInternalExecuteError> for ProgramExecuteError {
impl From<ChainProcessError> for ProgramInternalExecuteError {
fn from(value: ChainProcessError) -> Self {
match value {
- ChainProcessError::Other(s) => ProgramInternalExecuteError::Other(s),
- ChainProcessError::IO(error) => ProgramInternalExecuteError::IO(error),
+ ChainProcessError::Other(s) => Self::Other(s),
+ ChainProcessError::IO(error) => Self::IO(error),
}
}
}
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 6cf126d..81d83e6 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -44,27 +44,27 @@ pub struct Flag {
vec: Vec<&'static str>,
}
-impl From<&Flag> for Flag {
- fn from(value: &Flag) -> Self {
+impl From<&Self> for Flag {
+ fn from(value: &Self) -> Self {
value.clone()
}
}
impl From<()> for Flag {
fn from((): ()) -> Self {
- Flag { vec: vec![] }
+ Self { vec: vec![] }
}
}
impl From<&'static str> for Flag {
fn from(s: &'static str) -> Self {
- Flag { vec: vec![s] }
+ Self { vec: vec![s] }
}
}
impl From<&'static [&'static str]> for Flag {
fn from(slice: &'static [&'static str]) -> Self {
- Flag {
+ Self {
vec: slice.to_vec(),
}
}
@@ -72,7 +72,7 @@ impl From<&'static [&'static str]> for Flag {
impl<const N: usize> From<[&'static str; N]> for Flag {
fn from(slice: [&'static str; N]) -> Self {
- Flag {
+ Self {
vec: slice.to_vec(),
}
}
@@ -80,7 +80,7 @@ impl<const N: usize> From<[&'static str; N]> for Flag {
impl<const N: usize> From<&'static [&'static str; N]> for Flag {
fn from(slice: &'static [&'static str; N]) -> Self {
- Flag {
+ Self {
vec: slice.to_vec(),
}
}
@@ -169,7 +169,7 @@ where
/// Registers a global argument (with value) and its handler.
pub fn global_argument<F, A>(&mut self, arguments: A, mut do_fn: F)
where
- F: FnMut(&mut Program<C>, String),
+ F: FnMut(&mut Self, String),
A: Into<Flag>,
{
let flag = arguments.into();
@@ -185,7 +185,7 @@ where
/// Registers a global flag (boolean) and its handler.
pub fn global_flag<F, A>(&mut self, flag: A, mut do_fn: F)
where
- F: FnMut(&mut Program<C>),
+ F: FnMut(&mut Self),
A: Into<Flag>,
{
let flag = flag.into();
diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs
index 50c53d7..0bcc051 100644
--- a/mingling_core/src/program/hook.rs
+++ b/mingling_core/src/program/hook.rs
@@ -149,19 +149,19 @@ where
self
}
- pub(crate) fn run_hook_on_begin(&self, info: HookBeginInfo) {
+ pub(crate) fn run_hook_on_begin(&self, info: &HookBeginInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref begin) = hook.begin {
- begin(&info);
+ begin(info);
}
}
}
- pub(crate) fn run_hook_pre_dispatch(&self, info: HookPreDispatchInfo) -> ProgramControls<C> {
+ pub(crate) fn run_hook_pre_dispatch(&self, info: &HookPreDispatchInfo) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -169,7 +169,7 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref pre_dispatch) = hook.pre_dispatch {
- controls = pre_dispatch(&info);
+ controls = pre_dispatch(info);
}
}
controls
@@ -177,7 +177,7 @@ where
pub(crate) fn run_hook_post_dispatch(
&self,
- info: HookPostDispatchInfo<C>,
+ info: &HookPostDispatchInfo<C>,
) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
@@ -186,13 +186,13 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref post_dispatch) = hook.post_dispatch {
- controls = post_dispatch(&info);
+ controls = post_dispatch(info);
}
}
controls
}
- pub(crate) fn run_hook_pre_chain(&self, info: HookPreChainInfo<C>) -> ProgramControls<C> {
+ pub(crate) fn run_hook_pre_chain(&self, info: &HookPreChainInfo<C>) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -200,13 +200,13 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref pre_chain) = hook.pre_chain {
- controls = pre_chain(&info);
+ controls = pre_chain(info);
}
}
controls
}
- pub(crate) fn run_hook_post_chain(&self, info: HookPostChainInfo<C>) -> ProgramControls<C> {
+ pub(crate) fn run_hook_post_chain(&self, info: &HookPostChainInfo<C>) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -214,13 +214,13 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref post_chain) = hook.post_chain {
- controls = post_chain(&info);
+ controls = post_chain(info);
}
}
controls
}
- pub(crate) fn run_hook_pre_render(&self, info: HookPreRenderInfo<C>) -> ProgramControls<C> {
+ pub(crate) fn run_hook_pre_render(&self, info: &HookPreRenderInfo<C>) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -228,13 +228,13 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref pre_render) = hook.pre_render {
- controls = pre_render(&info);
+ controls = pre_render(info);
}
}
controls
}
- pub(crate) fn run_hook_post_render(&self, info: HookPostRenderInfo) -> ProgramControls<C> {
+ pub(crate) fn run_hook_post_render(&self, info: &HookPostRenderInfo) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -242,7 +242,7 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref post_render) = hook.post_render {
- controls = post_render(&info);
+ controls = post_render(info);
}
}
controls
@@ -250,19 +250,19 @@ where
#[allow(dead_code)]
#[cfg(not(feature = "async"))]
- pub(crate) fn run_hook_exec_panic(&self, info: HookPanicInfo) {
+ pub(crate) fn run_hook_exec_panic(&self, info: &HookPanicInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref exec_panic) = hook.exec_panic {
- exec_panic(&info);
+ exec_panic(info);
}
}
}
- pub(crate) fn run_hook_finish(&self, info: HookFinishInfo) -> ProgramControls<C> {
+ pub(crate) fn run_hook_finish(&self, info: &HookFinishInfo) -> ProgramControls<C> {
if !self.user_context.run_hook {
return ProgramControls::Empty;
}
@@ -270,7 +270,7 @@ where
let mut controls = ProgramControls::Empty;
for hook in &self.hooks {
if let Some(ref finish) = hook.finish {
- controls = finish(&info);
+ controls = finish(info);
}
}
controls
@@ -278,28 +278,28 @@ where
/// Runs the REPL begin hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_on_begin(&self, info: HookREPLBeginInfo) {
+ pub(crate) fn run_hook_repl_on_begin(&self, info: &HookREPLBeginInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_on_begin) = hook.repl_on_begin {
- repl_on_begin(&info);
+ repl_on_begin(info);
}
}
}
/// Runs the REPL pre-readline hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_pre_readline(&self, info: HookREPLPreReadlineInfo) {
+ pub(crate) fn run_hook_repl_pre_readline(&self, info: &HookREPLPreReadlineInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_pre_readline) = hook.repl_pre_readline {
- repl_pre_readline(&info);
+ repl_pre_readline(info);
}
}
}
@@ -307,14 +307,14 @@ where
/// Runs the custom REPL readline hook (only available with `repl` feature)
/// Returns `Some(line)` if a hook was set and returned Some, otherwise `None`.
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_readline(&self, info: HookREPLReadlineInfo) -> Option<String> {
+ pub(crate) fn run_hook_repl_readline(&self, info: &HookREPLReadlineInfo) -> Option<String> {
if !self.user_context.run_hook {
return None;
}
for hook in &self.hooks {
if let Some(ref repl_readline) = hook.repl_readline {
- return repl_readline(&info);
+ return repl_readline(info);
}
}
None
@@ -322,98 +322,98 @@ where
/// Runs the REPL post-readline hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_post_readline(&self, info: HookREPLPostReadlineInfo) {
+ pub(crate) fn run_hook_repl_post_readline(&self, info: &HookREPLPostReadlineInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_post_readline) = hook.repl_post_readline {
- repl_post_readline(&info);
+ repl_post_readline(info);
}
}
}
/// Runs the REPL pre-exec hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_pre_exec(&self, info: HookREPLPreExecInfo) {
+ pub(crate) fn run_hook_repl_pre_exec(&self, info: &HookREPLPreExecInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_pre_exec) = hook.repl_pre_exec {
- repl_pre_exec(&info);
+ repl_pre_exec(info);
}
}
}
/// Runs the REPL post-exec hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_post_exec(&self, info: HookREPLPostExecInfo) {
+ pub(crate) fn run_hook_repl_post_exec(&self, info: &HookREPLPostExecInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_post_exec) = hook.repl_post_exec {
- repl_post_exec(&info);
+ repl_post_exec(info);
}
}
}
/// Runs the REPL receive result hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_on_receive_result(&self, info: HookREPLOnReceiveResultInfo) {
+ pub(crate) fn run_hook_repl_on_receive_result(&self, info: &HookREPLOnReceiveResultInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_on_receive_result) = hook.repl_on_receive_result {
- repl_on_receive_result(&info);
+ repl_on_receive_result(info);
}
}
}
/// Runs the REPL panic hooks (only available with `repl` feature)
#[cfg(all(feature = "repl", not(feature = "async")))]
- pub(crate) fn run_hook_repl_on_panic(&self, info: HookREPLOnPanicInfo) {
+ pub(crate) fn run_hook_repl_on_panic(&self, info: &HookREPLOnPanicInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_on_panic) = hook.repl_on_panic {
- repl_on_panic(&info);
+ repl_on_panic(info);
}
}
}
/// Runs the REPL exit hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_exit(&self, info: HookREPLExitInfo) {
+ pub(crate) fn run_hook_repl_exit(&self, info: &HookREPLExitInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_exit) = hook.repl_exit {
- repl_exit(&info);
+ repl_exit(info);
}
}
}
- /// Runs the REPL loop_once hooks (only available with `repl` feature)
+ /// Runs the REPL [`loop_once`] hooks (only available with `repl` feature)
#[cfg(feature = "repl")]
- pub(crate) fn run_hook_repl_loop_once(&self, info: HookREPLLoopOnceInfo) {
+ pub(crate) fn run_hook_repl_loop_once(&self, info: &HookREPLLoopOnceInfo) {
if !self.user_context.run_hook {
return;
}
for hook in &self.hooks {
if let Some(ref repl_loop_once) = hook.repl_loop_once {
- repl_loop_once(&info);
+ repl_loop_once(info);
}
}
}
@@ -663,7 +663,7 @@ where
self
}
- /// Sets the handler for the REPL loop_once event (only available with `repl` feature).
+ /// Sets the handler for the REPL [`loop_once`] event (only available with `repl` feature).
/// This hook runs after each REPL loop iteration.
#[cfg(feature = "repl")]
#[must_use]
@@ -691,7 +691,7 @@ mod tests {
impl std::fmt::Display for MockHookEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{:?}", self)
+ write!(f, "{self:?}")
}
}
@@ -701,65 +701,62 @@ mod tests {
/// Since this code only constructs `AnyOutput` and calls methods like
/// `downcast`, `is`, `restore`, `route_chain`, and `route_renderer` —
/// none of which involve `ProgramCollect::do_chain` or
- /// `ProgramCollect::render` — the type/member_id correspondence is
+ /// `ProgramCollect::render` — the `type`/`member_id` correspondence is
/// never exploited in an unsafe way here.
/// The caller must ensure that the associated `member_id` correctly
/// corresponds to the type's role in the group.
- unsafe impl Grouped<MockHookEnum> for MockHookEnum {
- fn member_id() -> MockHookEnum {
- MockHookEnum::A
+ unsafe impl Grouped<Self> for MockHookEnum {
+ fn member_id() -> Self {
+ Self::A
}
}
impl ProgramCollect for MockHookEnum {
- type Enum = MockHookEnum;
- type EntryFallback = MockHookEnum;
- type ErrorRendererNotFound = MockHookEnum;
- type ResultEmpty = MockHookEnum;
+ type Enum = Self;
+ type EntryFallback = Self;
+ type ErrorRendererNotFound = Self;
+ type ResultEmpty = Self;
- fn build_renderer_not_found(_member_id: MockHookEnum) -> crate::AnyOutput<MockHookEnum> {
+ fn build_renderer_not_found(_member_id: Self) -> crate::AnyOutput<Self> {
unreachable!()
}
- fn build_entry_fallback(_args: Vec<String>) -> crate::AnyOutput<MockHookEnum> {
+ fn build_entry_fallback(_args: Vec<String>) -> crate::AnyOutput<Self> {
unreachable!()
}
- fn build_empty_result() -> crate::AnyOutput<MockHookEnum> {
+ fn build_empty_result() -> crate::AnyOutput<Self> {
unreachable!()
}
- fn render(_any: crate::AnyOutput<MockHookEnum>) -> crate::RenderResult {
+ fn render(_any: crate::AnyOutput<Self>) -> crate::RenderResult {
unreachable!()
}
- fn render_help(_any: crate::AnyOutput<MockHookEnum>) -> crate::RenderResult {
+ fn render_help(_any: crate::AnyOutput<Self>) -> crate::RenderResult {
unreachable!()
}
- fn do_chain(_any: crate::AnyOutput<MockHookEnum>) -> crate::ChainProcess<MockHookEnum> {
+ fn do_chain(_any: crate::AnyOutput<Self>) -> crate::ChainProcess<Self> {
unreachable!()
}
- fn has_renderer(_any: &crate::AnyOutput<MockHookEnum>) -> bool {
+ fn has_renderer(_any: &crate::AnyOutput<Self>) -> bool {
unreachable!()
}
- fn has_chain(_any: &crate::AnyOutput<MockHookEnum>) -> bool {
+ fn has_chain(_any: &crate::AnyOutput<Self>) -> bool {
unreachable!()
}
#[cfg(feature = "comp")]
- fn do_comp(
- _any: &crate::AnyOutput<MockHookEnum>,
- _ctx: &crate::ShellContext,
- ) -> crate::Suggest {
+ fn do_comp(_any: &crate::AnyOutput<Self>, _ctx: &crate::ShellContext) -> crate::Suggest {
unreachable!()
}
#[cfg(feature = "structural_renderer")]
fn structural_render(
- _any: crate::AnyOutput<MockHookEnum>,
+ _any: crate::AnyOutput<Self>,
_setting: &crate::StructuralRendererSetting,
) -> Result<crate::RenderResult, crate::error::StructuralRendererSerializeError> {
unreachable!()
diff --git a/mingling_core/src/program/hook/control_unit.rs b/mingling_core/src/program/hook/control_unit.rs
index 5bf0e8c..d71a203 100644
--- a/mingling_core/src/program/hook/control_unit.rs
+++ b/mingling_core/src/program/hook/control_unit.rs
@@ -22,8 +22,8 @@ where
C: ProgramCollect<Enum = C>,
{
/// Returns `true` if the collection is empty.
- pub fn is_empty(&self) -> bool {
- matches!(self, ProgramControls::Empty)
+ pub const fn is_empty(&self) -> bool {
+ matches!(self, Self::Empty)
}
}
@@ -31,7 +31,7 @@ impl<C> From<()> for ProgramControls<C>
where
C: ProgramCollect<Enum = C>,
{
- fn from(_: ()) -> Self {
+ fn from((): ()) -> Self {
Self::Empty
}
}
@@ -63,13 +63,13 @@ where
fn into_iter(self) -> Self::IntoIter {
match self {
- ProgramControls::Empty => ProgramControlsIter {
+ Self::Empty => ProgramControlsIter {
inner: vec![].into_iter(),
},
- ProgramControls::Single(unit) => ProgramControlsIter {
+ Self::Single(unit) => ProgramControlsIter {
inner: vec![unit].into_iter(),
},
- ProgramControls::Multi(units) => ProgramControlsIter {
+ Self::Multi(units) => ProgramControlsIter {
inner: units.into_iter(),
},
}
@@ -139,17 +139,19 @@ where
RouteToHelp(AnyOutput<C>),
}
-impl<C> From<ChainProcess<C>> for ProgramControlUnit<C>
+impl<C> TryFrom<ChainProcess<C>> for ProgramControlUnit<C>
where
C: ProgramCollect<Enum = C>,
{
- fn from(val: ChainProcess<C>) -> Self {
+ type Error = String;
+
+ fn try_from(val: ChainProcess<C>) -> Result<Self, Self::Error> {
match val {
ChainProcess::Ok((any, next)) => match next {
- NextProcess::Chain => ProgramControlUnit::RouteToChain(any),
- NextProcess::Renderer => ProgramControlUnit::RouteToRender(any),
+ NextProcess::Chain => Ok(Self::RouteToChain(any)),
+ NextProcess::Renderer => Ok(Self::RouteToRender(any)),
},
- ChainProcess::Err(e) => panic!("{}", &e),
+ ChainProcess::Err(e) => Err(e.to_string()),
}
}
}
@@ -159,7 +161,14 @@ where
C: ProgramCollect<Enum = C>,
{
fn from(val: ChainProcess<C>) -> Self {
- let unit: ProgramControlUnit<C> = val.into();
- unit.into()
+ match val {
+ ChainProcess::Ok((any, next)) => match next {
+ NextProcess::Chain => Self::Single(ProgramControlUnit::RouteToChain(any)),
+ NextProcess::Renderer => Self::Single(ProgramControlUnit::RouteToRender(any)),
+ },
+ ChainProcess::Err(e) => Self::Single(ProgramControlUnit::OverrideExitCode(
+ e.to_string().parse::<i32>().unwrap_or(1),
+ )),
+ }
}
}
diff --git a/mingling_core/src/program/once_exec.rs b/mingling_core/src/program/once_exec.rs
index 96723fd..18e06ef 100644
--- a/mingling_core/src/program/once_exec.rs
+++ b/mingling_core/src/program/once_exec.rs
@@ -21,7 +21,7 @@ where
C: 'static + Send + Sync,
{
// Run hooks
- self.run_hook_on_begin(crate::hook::HookBeginInfo {});
+ self.run_hook_on_begin(&crate::hook::HookBeginInfo {});
self.args = self.args.iter().skip(1).cloned().collect();
@@ -43,11 +43,11 @@ where
let program = THIS_PROGRAM
.get_raw()
.unwrap()
- .downcast_ref::<Program<C>>()
+ .downcast_ref::<Self>()
.unwrap();
#[cfg(not(feature = "async"))]
- program.run_hook_exec_panic(crate::hook::HookPanicInfo {
+ program.run_hook_exec_panic(&crate::hook::HookPanicInfo {
panic: &panic_payload,
});
@@ -98,7 +98,7 @@ where
// Read exit code
// Render result
- if stdout_setting.render_output {
+ if stdout_setting.render_output == crate::RenderOutput::Show {
result.std_print();
}
@@ -152,17 +152,17 @@ where
pub(crate) fn exec_wrapper<F, R>(self, f: F) -> R
where
C: 'static + Send + Sync,
- F: FnOnce(&'static Program<C>) -> R + Send + Sync,
+ F: FnOnce(&'static Self) -> R + Send + Sync,
{
THIS_PROGRAM.set(Box::new(self));
let program = THIS_PROGRAM
.get_raw()
.unwrap()
- .downcast_ref::<Program<C>>()
+ .downcast_ref::<Self>()
.unwrap();
#[cfg(not(panic = "abort"))]
- if program.stdout_setting.silence_panic {
+ if program.stdout_setting.silence_panic == super::PanicSilence::Silence {
std::panic::set_hook(Box::new(|_| {}));
}
diff --git a/mingling_core/src/program/repl_exec.rs b/mingling_core/src/program/repl_exec.rs
index f84b291..ea36c75 100644
--- a/mingling_core/src/program/repl_exec.rs
+++ b/mingling_core/src/program/repl_exec.rs
@@ -29,7 +29,7 @@ where
// Inject default REPL resource
self.with_resource(ResREPL::default());
- self.run_hook_repl_on_begin(crate::hook::HookREPLBeginInfo {});
+ self.run_hook_repl_on_begin(&crate::hook::HookREPLBeginInfo {});
might_be_async::select!(
self.exec_wrapper(async |p| -> () {
@@ -48,43 +48,43 @@ where
C: ProgramCollect<Enum = C> + Send + Sync + 'static,
{
loop {
- p.run_hook_repl_pre_readline(crate::hook::HookREPLPreReadlineInfo {});
+ p.run_hook_repl_pre_readline(&crate::hook::HookREPLPreReadlineInfo {});
let mut readline = p
- .run_hook_repl_readline(crate::hook::HookREPLReadlineInfo {})
+ .run_hook_repl_readline(&crate::hook::HookREPLReadlineInfo {})
.unwrap_or_default();
- p.run_hook_repl_post_readline(crate::hook::HookREPLPostReadlineInfo {
+ p.run_hook_repl_post_readline(&crate::hook::HookREPLPostReadlineInfo {
line: &mut readline,
});
- let args = split_input_string(readline.clone());
+ let args = split_input_string(&readline);
- p.run_hook_repl_pre_exec(crate::hook::HookREPLPreExecInfo { args: &args });
- match might_be_async::invoke!(exec_once(p, args)) {
+ p.run_hook_repl_pre_exec(&crate::hook::HookREPLPreExecInfo { args: &args });
+ match might_be_async::invoke!(exec_once(p, &args)) {
Ok(r) => {
- p.run_hook_repl_on_receive_result(crate::hook::HookREPLOnReceiveResultInfo {
+ p.run_hook_repl_on_receive_result(&crate::hook::HookREPLOnReceiveResultInfo {
result: &r,
});
}
Err(ProgramInternalExecuteError::REPLPanic(panic)) => {
- p.run_hook_repl_on_panic(crate::hook::HookREPLOnPanicInfo { panic: &panic });
+ p.run_hook_repl_on_panic(&crate::hook::HookREPLOnPanicInfo { panic: &panic });
}
_ => {}
}
- p.run_hook_repl_post_exec(crate::hook::HookREPLPostExecInfo {});
+ p.run_hook_repl_post_exec(&crate::hook::HookREPLPostExecInfo {});
if this::<C>().res::<ResREPL>().unwrap().exit {
- p.run_hook_repl_exit(crate::hook::HookREPLExitInfo {});
+ p.run_hook_repl_exit(&crate::hook::HookREPLExitInfo {});
break;
}
- p.run_hook_repl_loop_once(crate::hook::HookREPLLoopOnceInfo {});
+ p.run_hook_repl_loop_once(&crate::hook::HookREPLLoopOnceInfo {});
}
}
#[cfg(not(feature = "async"))]
fn exec_once<C>(
p: &'static Program<C>,
- args: Vec<String>,
+ args: &[String],
) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C> + Send + Sync + 'static,
@@ -95,7 +95,7 @@ where
#[cfg(not(panic = "abort"))]
let exec_result = {
let exec_unwind_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
- super::exec::exec_with_args(p, &args)
+ super::exec::exec_with_args(p, args)
}));
match exec_unwind_result {
@@ -108,7 +108,7 @@ where
.unwrap()
.downcast_ref::<Program<C>>()
.unwrap();
- program.run_hook_repl_on_panic(crate::hook::HookREPLOnPanicInfo {
+ program.run_hook_repl_on_panic(&crate::hook::HookREPLOnPanicInfo {
panic: &panic_payload,
});
Err(ProgramInternalExecuteError::REPLPanic(panic_payload))
@@ -123,7 +123,7 @@ where
#[cfg(feature = "async")]
async fn exec_once<C>(
p: &'static Program<C>,
- args: Vec<String>,
+ args: &[String],
) -> Result<RenderResult, ProgramInternalExecuteError>
where
C: ProgramCollect<Enum = C> + Send + Sync + 'static,
diff --git a/mingling_core/src/program/repl_exec/splitter.rs b/mingling_core/src/program/repl_exec/splitter.rs
index 267f42c..c74a3f1 100644
--- a/mingling_core/src/program/repl_exec/splitter.rs
+++ b/mingling_core/src/program/repl_exec/splitter.rs
@@ -1,14 +1,14 @@
/// Wraps `split_input` to work with owned `String` inputs.
-pub(crate) fn split_input_string(input: String) -> Vec<String> {
- split_input(&input)
+pub fn split_input_string(input: &str) -> Vec<String> {
+ split_input(input)
}
/// Splits a string input into arguments, respecting single quotes, double quotes,
/// and backslash escaping.
-pub(crate) fn split_input(input: &str) -> Vec<String> {
+pub fn split_input(input: &str) -> Vec<String> {
let mut result: Vec<String> = Vec::new();
let mut current = String::new();
- let mut chars = input.chars().peekable();
+ let mut chars = input.chars();
while let Some(ch) = chars.next() {
match ch {
diff --git a/mingling_core/src/program/single_instance.rs b/mingling_core/src/program/single_instance.rs
index 8b165bf..083897d 100644
--- a/mingling_core/src/program/single_instance.rs
+++ b/mingling_core/src/program/single_instance.rs
@@ -13,7 +13,7 @@ use crate::{Program, ProgramCollect};
/// the inner value is immutable once set until `take()`).
/// - `take()` is called only after execution completes, when no code still
/// holds a reference from `get_raw()`.
-pub(crate) struct ProgramCell {
+pub struct ProgramCell {
initialized: AtomicBool,
inner: UnsafeCell<Option<Box<dyn std::any::Any + Send + Sync>>>,
}
@@ -88,6 +88,7 @@ impl ProgramCell {
}
/// Global static reference to the current program instance
+#[allow(clippy::redundant_pub_crate)]
pub(crate) static THIS_PROGRAM: ProgramCell = ProgramCell::new();
/// Returns a reference to the current program instance, panics if not set.
diff --git a/mingling_core/src/program/string_vec.rs b/mingling_core/src/program/string_vec.rs
index c2e6220..a2eb433 100644
--- a/mingling_core/src/program/string_vec.rs
+++ b/mingling_core/src/program/string_vec.rs
@@ -20,7 +20,7 @@ impl From<StringVec> for Vec<String> {
impl<const N: usize> From<[&str; N]> for StringVec {
fn from(slice: [&str; N]) -> Self {
- StringVec {
+ Self {
vec: slice.iter().map(|&s| s.to_string()).collect(),
}
}
@@ -28,21 +28,20 @@ impl<const N: usize> From<[&str; N]> for StringVec {
impl From<&[&str]> for StringVec {
fn from(slice: &[&str]) -> Self {
- StringVec {
+ Self {
vec: slice.iter().map(|&s| s.to_string()).collect(),
}
}
}
-
impl From<Vec<String>> for StringVec {
fn from(vec: Vec<String>) -> Self {
- StringVec { vec }
+ Self { vec }
}
}
impl From<&[String]> for StringVec {
fn from(slice: &[String]) -> Self {
- StringVec {
+ Self {
vec: slice.to_vec(),
}
}
@@ -50,7 +49,7 @@ impl From<&[String]> for StringVec {
impl From<Vec<&str>> for StringVec {
fn from(vec: Vec<&str>) -> Self {
- StringVec {
+ Self {
vec: vec.iter().map(|&s| s.to_string()).collect(),
}
}
diff --git a/mingling_core/src/renderer/render_result.rs b/mingling_core/src/renderer/render_result.rs
index 3e63a00..a14bfda 100644
--- a/mingling_core/src/renderer/render_result.rs
+++ b/mingling_core/src/renderer/render_result.rs
@@ -50,7 +50,7 @@ pub enum RenderResultMode {
impl<F> From<F> for RenderResult
where
- F: FnOnce() -> RenderResult,
+ F: FnOnce() -> Self,
{
fn from(value: F) -> Self {
value()
@@ -79,7 +79,7 @@ impl Display for RenderResult {
impl From<()> for RenderResult {
fn from(_value: ()) -> Self {
- RenderResult::new()
+ Self::new()
}
}
@@ -88,8 +88,8 @@ macro_rules! impl_from_int {
$(
impl From<$ty> for RenderResult {
fn from(exit_code: $ty) -> Self {
- RenderResult {
- exit_code: exit_code as i32,
+ Self {
+ exit_code: <i32>::try_from(exit_code).unwrap_or_default(),
..Default::default()
}
}
@@ -102,13 +102,13 @@ impl_from_int!(i32, i16, i8, u32, u16, u8, usize);
impl From<RenderResult> for ExitCode {
fn from(value: RenderResult) -> Self {
- ExitCode::from(value.exit_code as u8)
+ Self::from(u8::try_from(value.exit_code).unwrap_or_default())
}
}
impl From<&RenderResult> for ExitCode {
fn from(value: &RenderResult) -> Self {
- ExitCode::from(value.exit_code as u8)
+ Self::from(u8::try_from(value.exit_code).unwrap_or_default())
}
}
@@ -156,6 +156,7 @@ impl RenderResult {
/// assert_eq!(result.exit_code, 0);
/// assert!(result.is_empty());
/// ```
+ #[must_use]
pub fn new() -> Self {
Self::default()
}
@@ -175,7 +176,7 @@ impl RenderResult {
/// let mut result = RenderResult::default();
/// result.immediate_output();
/// ```
- pub fn immediate_output(&mut self) -> &mut Self {
+ pub const fn immediate_output(&mut self) -> &mut Self {
self.immediate_output = true;
self
}
@@ -258,7 +259,7 @@ impl RenderResult {
/// dest.append_other(src);
/// assert_eq!(dest.to_string(), "Hello Error");
/// ```
- pub fn append_other(&mut self, other: impl Into<RenderResult>) {
+ pub fn append_other(&mut self, other: impl Into<Self>) {
let other = other.into();
// If self has immediate output enabled, but the input does not, the input needs immediate output.
@@ -290,7 +291,7 @@ impl RenderResult {
pub fn print(&mut self, text: impl Into<String>) {
let text = text.into();
if self.immediate_output {
- print!("{}", text)
+ print!("{text}");
}
self.append_to_buffer(text, Stdout);
}
@@ -310,7 +311,7 @@ impl RenderResult {
pub fn println(&mut self, text: impl Into<String>) {
let text = text.into();
if self.immediate_output {
- println!("{}", text)
+ println!("{text}");
}
self.append_line_to_buffer(text, Stdout);
}
@@ -330,7 +331,7 @@ impl RenderResult {
pub fn eprint(&mut self, text: impl Into<String>) {
let text = text.into();
if self.immediate_output {
- eprint!("{}", text)
+ eprint!("{text}");
}
self.append_to_buffer(text, Stderr);
}
@@ -350,7 +351,7 @@ impl RenderResult {
pub fn eprintln(&mut self, text: impl Into<String>) {
let text = text.into();
if self.immediate_output {
- eprintln!("{}", text)
+ eprintln!("{text}");
}
self.append_line_to_buffer(text, Stderr);
}
@@ -392,10 +393,10 @@ impl RenderResult {
/// result.std_print(); // prints "Hello" to stdout and "Error" to stderr
/// ```
pub fn std_print(&self) {
- for (content, mode) in self.render_buffer.iter() {
+ for (content, mode) in &self.render_buffer {
match mode {
- Stdout => print!("{}", content),
- Stderr => eprint!("{}", content),
+ Stdout => print!("{content}"),
+ Stderr => eprint!("{content}"),
}
}
}
@@ -415,6 +416,7 @@ impl RenderResult {
/// result.print(", 世界");
/// assert_eq!(result.len(), 9); // "Hello, 世界" has 9 chars
/// ```
+ #[must_use]
pub fn len(&self) -> usize {
self.render_buffer
.iter()
@@ -434,6 +436,7 @@ impl RenderResult {
/// result.print("Hello");
/// assert!(!result.is_empty());
/// ```
+ #[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
@@ -465,7 +468,8 @@ impl RenderResult {
/// let trimmed = result.trim_buffer();
/// assert_eq!(trimmed.to_string().trim(), "Hello, world!");
/// ```
- pub fn trim_buffer(self) -> RenderResult {
+ #[must_use]
+ pub fn trim_buffer(self) -> Self {
if self.render_buffer.is_empty() {
return self;
}
@@ -490,7 +494,7 @@ impl RenderResult {
buffer.push((trimmed_last, last_mode));
}
- RenderResult {
+ Self {
render_buffer: buffer,
immediate_output: self.immediate_output,
exit_code: self.exit_code,
@@ -516,16 +520,16 @@ impl RenderResult {
}
}
-#[inline(always)]
+#[inline]
fn render_result_to_string(result: &RenderResult) -> String {
let mut buffer = String::new();
- for item in result.render_buffer.iter() {
+ for item in &result.render_buffer {
buffer += &item.0;
}
buffer
}
-#[inline(always)]
+#[inline]
fn string_to_render_result(string: impl Into<String>, mode: RenderResultMode) -> RenderResult {
RenderResult {
render_buffer: vec![(string.into(), mode)],
@@ -573,7 +577,7 @@ mod tests {
fn display_trims_trailing_whitespace() {
let mut result = RenderResult::default();
result.print(" hello world \n");
- let formatted = format!("{}", result);
+ let formatted = format!("{result}");
assert_eq!(formatted, "hello world");
}
diff --git a/mingling_core/src/renderer/structural/error.rs b/mingling_core/src/renderer/structural/error.rs
index 63ded81..2787dff 100644
--- a/mingling_core/src/renderer/structural/error.rs
+++ b/mingling_core/src/renderer/structural/error.rs
@@ -11,7 +11,7 @@ pub struct StructuralRendererSerializeError {
impl StructuralRendererSerializeError {
/// Creates a new `StructuralRendererSerializeError` with the given error message.
#[must_use]
- pub fn new(error: String) -> Self {
+ pub const fn new(error: String) -> Self {
Self { error }
}
}