summaryrefslogtreecommitdiff
path: root/mingling_core
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_core')
-rw-r--r--mingling_core/src/any.rs31
-rw-r--r--mingling_core/src/any/group.rs2
-rw-r--r--mingling_core/src/asset.rs4
-rw-r--r--mingling_core/src/asset/chain.rs5
-rw-r--r--mingling_core/src/asset/dispatcher.rs20
-rw-r--r--mingling_core/src/asset/node.rs5
-rw-r--r--mingling_core/src/asset/renderer.rs4
-rw-r--r--mingling_core/src/lib.rs43
-rw-r--r--mingling_core/src/markers.rs1
-rw-r--r--mingling_core/src/program.rs19
-rw-r--r--mingling_core/src/program/config.rs2
-rw-r--r--mingling_core/src/program/exec.rs1
-rw-r--r--mingling_core/src/program/flag.rs41
-rw-r--r--mingling_core/src/renderer/render_result.rs41
14 files changed, 205 insertions, 14 deletions
diff --git a/mingling_core/src/any.rs b/mingling_core/src/any.rs
index dac0d44..d550ec7 100644
--- a/mingling_core/src/any.rs
+++ b/mingling_core/src/any.rs
@@ -6,8 +6,18 @@ use serde::Serialize;
use crate::Groupped;
use crate::error::ChainProcessError;
+#[doc(hidden)]
pub mod group;
+/// Any type output
+///
+/// Accepts any type that implements `Send + Groupped<G>`
+/// After being passed into AnyOutput, it will be converted to `Box<dyn Any + Send + 'static>`
+///
+/// Note:
+/// - If an enum value that does not belong to this type is incorrectly specified, it will be **unsafely** unwrapped by the scheduler
+/// - Under the `general_renderer` feature, the passed value must ensure it implements `serde::Serialize`
+/// - It is recommended to use the `pack!` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to create types that can be converted to `AnyOutput`, which guarantees runtime safety
#[derive(Debug)]
pub struct AnyOutput<G>
where
@@ -22,6 +32,7 @@ impl<G> AnyOutput<G>
where
G: Display,
{
+ /// Create an AnyOutput from a `Send + Groupped<G> + Serialize` type
#[cfg(feature = "general_renderer")]
pub fn new<T>(value: T) -> Self
where
@@ -34,6 +45,7 @@ where
}
}
+ /// Create an AnyOutput from a `Send + Groupped<G>` type
#[cfg(not(feature = "general_renderer"))]
pub fn new<T>(value: T) -> Self
where
@@ -46,6 +58,7 @@ where
}
}
+ /// Downcast the AnyOutput to a concrete type T
pub fn downcast<T: 'static>(self) -> Result<T, Self> {
if self.type_id == std::any::TypeId::of::<T>() {
Ok(*self.inner.downcast::<T>().unwrap())
@@ -54,6 +67,7 @@ where
}
}
+ /// Check if the inner value is of type T
pub fn is<T: 'static>(&self) -> bool {
self.type_id == std::any::TypeId::of::<T>()
}
@@ -89,6 +103,12 @@ where
}
}
+/// 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)`, `[`Next::Chain`](./enum.Next.html)`))` to continue execution with this type next
+/// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`Next::Renderer`](./enum.Next.html)`))` to render this type next and output to the terminal
+/// - Returns `Err(`[`ChainProcessError`](./error/enum.ChainProcessError.html)`]` to terminate the program directly
pub enum ChainProcess<G>
where
G: Display,
@@ -97,6 +117,10 @@ where
Err(ChainProcessError),
}
+/// Indicates the next step after processing
+///
+/// - `Chain`: Continue execution to the next chain
+/// - `Renderer`: Send output to renderer and end execution
pub enum Next {
Chain,
Renderer,
@@ -106,14 +130,17 @@ impl<G> ChainProcess<G>
where
G: Display,
{
+ /// Returns true if the result is Ok (has a next step)
pub fn is_next(&self) -> bool {
matches!(self, Self::Ok(_))
}
+ /// Returns true if the result is an error
pub fn is_err(&self) -> bool {
matches!(self, Self::Err(_))
}
+ /// Returns the next step if the result is Ok
pub fn next(&self) -> Option<&Next> {
match self {
Self::Ok((_, next)) => Some(next),
@@ -121,6 +148,7 @@ where
}
}
+ /// Returns the error if the result is Err
pub fn err(&self) -> Option<&ChainProcessError> {
match self {
Self::Ok(_) => None,
@@ -128,6 +156,7 @@ where
}
}
+ /// Unwraps the result, panics if it's an error
pub fn unwrap(self) -> (AnyOutput<G>, Next) {
match self {
Self::Ok(tuple) => tuple,
@@ -135,6 +164,7 @@ where
}
}
+ /// Returns the Ok value or a provided default
pub fn unwrap_or(self, default: (AnyOutput<G>, Next)) -> (AnyOutput<G>, Next) {
match self {
Self::Ok(tuple) => tuple,
@@ -142,6 +172,7 @@ where
}
}
+ /// Returns the Ok value or computes it from the error
pub fn unwrap_or_else<F>(self, f: F) -> (AnyOutput<G>, Next)
where
F: FnOnce(ChainProcessError) -> (AnyOutput<G>, Next),
diff --git a/mingling_core/src/any/group.rs b/mingling_core/src/any/group.rs
index 29d6a48..04701f2 100644
--- a/mingling_core/src/any/group.rs
+++ b/mingling_core/src/any/group.rs
@@ -1,3 +1,5 @@
+/// Used to mark a type with a unique enum ID, assisting dynamic dispatch
pub trait Groupped<Group> {
+ /// Returns the specific enum value representing its ID within that enum
fn member_id() -> Group;
}
diff --git a/mingling_core/src/asset.rs b/mingling_core/src/asset.rs
index c2adf4e..81aa3a6 100644
--- a/mingling_core/src/asset.rs
+++ b/mingling_core/src/asset.rs
@@ -1,4 +1,8 @@
+#[doc(hidden)]
pub mod chain;
+#[doc(hidden)]
pub mod dispatcher;
+#[doc(hidden)]
pub mod node;
+#[doc(hidden)]
pub mod renderer;
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs
index 9f62228..70eda8c 100644
--- a/mingling_core/src/asset/chain.rs
+++ b/mingling_core/src/asset/chain.rs
@@ -2,12 +2,17 @@ use std::fmt::Display;
use crate::ChainProcess;
+#[doc(hidden)]
pub mod error;
+/// Takes over a type (G: Previous) and converts it to another [AnyOutput](./struct.AnyOutput.html)
pub trait Chain<G>
where
G: Display,
{
+ /// The previous type in the chain
type Previous;
+
+ /// Process the previous value and return a future that resolves to a [`ChainProcess<G>`](./enum.ChainProcess.html)
fn proc(p: Self::Previous) -> impl Future<Output = ChainProcess<G>> + Send;
}
diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs
index 0863f16..86dfe7c 100644
--- a/mingling_core/src/asset/dispatcher.rs
+++ b/mingling_core/src/asset/dispatcher.rs
@@ -2,12 +2,22 @@ use std::fmt::Display;
use crate::{ChainProcess, Program, asset::node::Node};
+/// Dispatches user input commands to specific [ChainProcess](./enum.ChainProcess.html)
+///
+/// Note: If you are using [mingling_macros](https://crates.io/crates/mingling_macros),
+/// you can use the `dispatcher!("node.subnode", CommandType => Entry)` macro to declare a `Dispatcher`
pub trait Dispatcher<G>
where
G: Display,
{
+ /// Returns a command node for matching user input
fn node(&self) -> Node;
+
+ /// Returns a [ChainProcess](./enum.ChainProcess.html) based on user input arguments,
+ /// to be sent to the specific invocation
fn begin(&self, args: Vec<String>) -> ChainProcess<G>;
+
+ /// Clones the current dispatcher for implementing the `Clone` trait
fn clone_dispatcher(&self) -> Box<dyn Dispatcher<G>>;
}
@@ -39,6 +49,16 @@ impl<C: crate::program::ProgramCollect, G: Display> Program<C, G> {
}
}
+/// A collection of dispatchers.
+///
+/// This struct holds a vector of boxed `Dispatcher` trait objects,
+/// allowing multiple dispatchers to be grouped together and passed
+/// to the program via `Program::with_dispatchers`.
+/// A collection of dispatchers.
+///
+/// This struct holds a vector of boxed `Dispatcher` trait objects,
+/// allowing multiple dispatchers to be grouped together and passed
+/// to the program via `Program::with_dispatchers`.
pub struct Dispatchers<G> {
dispatcher: Vec<Box<dyn Dispatcher<G> + 'static>>,
}
diff --git a/mingling_core/src/asset/node.rs b/mingling_core/src/asset/node.rs
index c8b7600..035d227 100644
--- a/mingling_core/src/asset/node.rs
+++ b/mingling_core/src/asset/node.rs
@@ -1,11 +1,16 @@
use just_fmt::kebab_case;
+/// Represents a command node, used to match user-input command paths.
+///
+/// The node consists of multiple parts, each separated by a dot (`.`), and automatically converted to kebab-case.
+/// For example, the input string `"node.subnode"` will be converted to a node representation of `["node", "subnode"]`.
#[derive(Debug, Default)]
pub struct Node {
node: Vec<String>,
}
impl Node {
+ /// Append a new part to the node path.
pub fn join(self, node: impl Into<String>) -> Node {
let mut new_node = self.node;
new_node.push(node.into());
diff --git a/mingling_core/src/asset/renderer.rs b/mingling_core/src/asset/renderer.rs
index 3852b55..de417a2 100644
--- a/mingling_core/src/asset/renderer.rs
+++ b/mingling_core/src/asset/renderer.rs
@@ -1,6 +1,10 @@
use crate::RenderResult;
+/// Takes over a type (Self::Previous) and converts it to a [`RenderResult`](./struct.RenderResult.html)
pub trait Renderer {
+ /// The previous type in the chain
type Previous;
+
+ /// Process the previous value and write the result into the provided [`RenderResult`](./struct.RenderResult.html)
fn render(p: Self::Previous, r: &mut RenderResult);
}
diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs
index 3d2be11..bcad91d 100644
--- a/mingling_core/src/lib.rs
+++ b/mingling_core/src/lib.rs
@@ -1,28 +1,43 @@
+//! Mingling Core
+//!
+//! # Intro
+//! This crate is the core implementation of `mingling`, containing the complete logic for command dispatching, execution, and rendering.
+//!
+//! # Note
+//! It is not recommended to use [mingling_core](https://crates.io/crates/mingling_core) directly, as this will lose the code generation functionality of [mingling_macros](https://crates.io/crates/mingling_macros).
+//!
+//! Recommended to import [mingling](https://crates.io/crates/mingling) to use its features.
+
mod any;
+mod asset;
+mod markers;
+mod program;
+mod renderer;
+
pub use crate::any::group::*;
pub use crate::any::*;
-mod markers;
-pub mod marker {
- pub use crate::markers::group_process::*;
-}
+pub use crate::asset::chain::*;
+pub use crate::asset::dispatcher::*;
+pub use crate::asset::node::*;
+pub use crate::asset::renderer::*;
+/// All error types of `Mingling`
pub mod error {
pub use crate::asset::chain::error::*;
pub use crate::exec::error::*;
}
-mod program;
pub use crate::program::*;
+
+pub use crate::renderer::render_result::*;
+
+/// All marker types of `Mingling` that serve no practical purpose
+pub mod marker {
+ pub use crate::markers::group_process::*;
+}
+
+/// `Mingling`'s Program initialization system
pub mod setup {
pub use crate::program::setup::*;
}
-
-mod renderer;
-
-mod asset;
-pub use crate::asset::chain::*;
-pub use crate::asset::dispatcher::*;
-pub use crate::asset::node::*;
-pub use crate::asset::renderer::*;
-pub use crate::renderer::render_result::*;
diff --git a/mingling_core/src/markers.rs b/mingling_core/src/markers.rs
index e95fb8a..151a0d4 100644
--- a/mingling_core/src/markers.rs
+++ b/mingling_core/src/markers.rs
@@ -1 +1,2 @@
+#[doc(hidden)]
pub mod group_process;
diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs
index ffba17e..5d81234 100644
--- a/mingling_core/src/program.rs
+++ b/mingling_core/src/program.rs
@@ -4,7 +4,9 @@ use crate::{
};
use std::{env, fmt::Display, pin::Pin};
+#[doc(hidden)]
pub mod exec;
+#[doc(hidden)]
pub mod setup;
mod config;
@@ -14,6 +16,7 @@ mod flag;
pub use flag::*;
use tokio::io::AsyncWriteExt;
+/// Program, used to define the behavior of the entire command-line program
#[derive(Default)]
pub struct Program<C, G>
where
@@ -86,15 +89,31 @@ where
}
}
+/// Collected program context
+///
+/// Note: It is recommended to use the `gen_program!()` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to automatically create this type
pub trait ProgramCollect {
+ /// Enum type representing internal IDs for the program
type Enum: Display;
+
+ /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a renderer was not found
fn build_renderer_not_found(member_id: Self::Enum) -> AnyOutput<Self::Enum>;
+
+ /// Build an [AnyOutput](./struct.AnyOutput.html) to indicate that a dispatcher was not found
fn build_dispatcher_not_found(args: Vec<String>) -> AnyOutput<Self::Enum>;
+
+ /// Render the input [AnyOutput](./struct.AnyOutput.html)
fn render(any: AnyOutput<Self::Enum>, r: &mut RenderResult);
+
+ /// Find a matching chain to continue execution based on the input [AnyOutput](./struct.AnyOutput.html), returning a new [AnyOutput](./struct.AnyOutput.html)
fn do_chain(
any: AnyOutput<Self::Enum>,
) -> Pin<Box<dyn Future<Output = ChainProcess<Self::Enum>> + Send>>;
+
+ /// Whether the program has a renderer that can handle the current [AnyOutput](./struct.AnyOutput.html)
fn has_renderer(any: &AnyOutput<Self::Enum>) -> bool;
+
+ /// Whether the program has a chain that can handle the current [AnyOutput](./struct.AnyOutput.html)
fn has_chain(any: &AnyOutput<Self::Enum>) -> bool;
}
diff --git a/mingling_core/src/program/config.rs b/mingling_core/src/program/config.rs
index 386b112..6ad0a38 100644
--- a/mingling_core/src/program/config.rs
+++ b/mingling_core/src/program/config.rs
@@ -1,3 +1,4 @@
+/// Program stdout settings
#[derive(Debug, Clone)]
pub struct ProgramStdoutSetting {
/// Output error messages
@@ -16,6 +17,7 @@ impl Default for ProgramStdoutSetting {
}
}
+/// Program stdout settings
#[derive(Debug, Clone, Default)]
pub struct ProgramUserContext {
/// View help information instead of running the command
diff --git a/mingling_core/src/program/exec.rs b/mingling_core/src/program/exec.rs
index 7de3723..9c80c4a 100644
--- a/mingling_core/src/program/exec.rs
+++ b/mingling_core/src/program/exec.rs
@@ -7,6 +7,7 @@ use crate::{
error::ProgramInternalExecuteError,
};
+#[doc(hidden)]
pub mod error;
pub async fn exec<C, G>(program: Program<C, G>) -> Result<RenderResult, ProgramInternalExecuteError>
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 84fae01..a520495 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -2,6 +2,45 @@ use std::fmt::Display;
use crate::{Program, ProgramCollect};
+/// A wrapper for a collection of static string slices representing command-line flags or arguments.
+///
+/// `Flag` is used to store one or more static string slices (e.g., `["-h", "--help"]`) that
+/// represent command-line flags or arguments. It provides conversions from various input types
+/// (like a single `&'static str`, a slice, or an array) and dereferences to a slice of strings
+/// for easy iteration and access.
+///
+/// # Examples
+///
+/// ```
+/// use mingling_core::Flag;
+///
+/// // Create a Flag from a single string slice
+/// let flag1 = Flag::from("-h");
+/// assert_eq!(flag1.as_ref(), &["-h"]);
+///
+/// // Create a Flag from a slice of string slices
+/// let flag2 = Flag::from(&["-h", "--help"][..]);
+/// assert_eq!(flag2.as_ref(), &["-h", "--help"]);
+///
+/// // Create a Flag from an array
+/// let flag3 = Flag::from(["-v", "--verbose"]);
+/// assert_eq!(flag3.as_ref(), &["-v", "--verbose"]);
+///
+/// // Create a Flag from a reference to an array
+/// let arr = &["-f", "--file"];
+/// let flag4 = Flag::from(arr);
+/// assert_eq!(flag4.as_ref(), &["-f", "--file"]);
+///
+/// // Create an empty Flag from unit type
+/// let flag5 = Flag::from(());
+/// assert_eq!(flag5.as_ref(), &[] as &[&str]);
+///
+/// // Dereference to slice for iteration
+/// let flag = Flag::from(["-a", "-b"]);
+/// for arg in flag.iter() {
+/// println!("Flag: {}", arg);
+/// }
+/// ```
pub struct Flag {
vec: Vec<&'static str>,
}
@@ -57,6 +96,7 @@ impl std::ops::Deref for Flag {
}
#[macro_export]
+#[doc(hidden)]
macro_rules! special_flag {
($args:expr, $flag:expr) => {{
let flag = $flag;
@@ -67,6 +107,7 @@ macro_rules! special_flag {
}
#[macro_export]
+#[doc(hidden)]
macro_rules! special_argument {
($args:expr, $flag:expr) => {{
let flag = $flag;
diff --git a/mingling_core/src/renderer/render_result.rs b/mingling_core/src/renderer/render_result.rs
index 73c38e7..d9da7b7 100644
--- a/mingling_core/src/renderer/render_result.rs
+++ b/mingling_core/src/renderer/render_result.rs
@@ -3,6 +3,7 @@ use std::{
ops::Deref,
};
+/// Render result, containing the rendered text content.
#[derive(Default, Debug, PartialEq)]
pub struct RenderResult {
render_text: String,
@@ -23,15 +24,55 @@ impl Deref for RenderResult {
}
impl RenderResult {
+ /// Appends the given text to the rendered content.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling_core::RenderResult;
+ /// use std::ops::Deref;
+ ///
+ /// let mut result = RenderResult::default();
+ /// result.print("Hello");
+ /// result.print(", world!");
+ /// assert_eq!(result.deref(), "Hello, world!");
+ /// ```
pub fn print(&mut self, text: &str) {
self.render_text.push_str(text);
}
+ /// Appends the given text followed by a newline to the rendered content.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling_core::RenderResult;
+ /// use std::ops::Deref;
+ ///
+ /// let mut result = RenderResult::default();
+ /// result.println("First line");
+ /// result.println("Second line");
+ /// assert_eq!(result.deref(), "First line\nSecond line\n");
+ /// ```
pub fn println(&mut self, text: &str) {
self.render_text.push_str(text);
self.render_text.push('\n');
}
+ /// Clears all rendered content.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use mingling_core::RenderResult;
+ /// use std::ops::Deref;
+ ///
+ /// let mut result = RenderResult::default();
+ /// result.print("Some content");
+ /// assert!(!result.is_empty());
+ /// result.clear();
+ /// assert!(result.is_empty());
+ /// ```
pub fn clear(&mut self) {
self.render_text.clear();
}