diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-04-11 22:28:26 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-04-11 22:28:26 +0800 |
| commit | dc501290f7b25b72edf8c67555b5604ac5fc8a59 (patch) | |
| tree | b6c4064e46ddef919a561a846c9770f432f821d2 | |
| parent | d6f0e3f8f97a27a464d9610500c29fb817a2ef88 (diff) | |
Add dev tools to generate example documentation
| -rw-r--r-- | dev_tools/Cargo.lock | 26 | ||||
| -rw-r--r-- | dev_tools/Cargo.toml | 8 | ||||
| -rw-r--r-- | dev_tools/src/bin/refresh-examples.rs | 139 | ||||
| -rw-r--r-- | dev_tools/src/lib.rs | 1 | ||||
| -rw-r--r-- | mingling/src/example_docs.rs | 382 | ||||
| -rw-r--r-- | mingling/src/example_docs.rs.tmpl | 18 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 162 | ||||
| -rw-r--r-- | mingling_core/src/lib.rs | 3 | ||||
| -rwxr-xr-x | run-tools.sh | 27 |
9 files changed, 608 insertions, 158 deletions
diff --git a/dev_tools/Cargo.lock b/dev_tools/Cargo.lock new file mode 100644 index 0000000..39a1521 --- /dev/null +++ b/dev_tools/Cargo.lock @@ -0,0 +1,26 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "just_fmt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5454cda0d57db59778608d7a47bff5b16c6705598265869fb052b657f66cf05e" + +[[package]] +name = "just_template" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3edb658c34b10b69c4b3b58f7ba989cd09c82c0621dee1eef51843c2327225" +dependencies = [ + "just_fmt", +] + +[[package]] +name = "tools" +version = "0.1.0" +dependencies = [ + "just_fmt", + "just_template", +] diff --git a/dev_tools/Cargo.toml b/dev_tools/Cargo.toml new file mode 100644 index 0000000..a28b156 --- /dev/null +++ b/dev_tools/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tools" +version = "0.1.0" +edition = "2024" + +[dependencies] +just_template = "0.1.3" +just_fmt = "0.1.2" diff --git a/dev_tools/src/bin/refresh-examples.rs b/dev_tools/src/bin/refresh-examples.rs new file mode 100644 index 0000000..9fdc7b5 --- /dev/null +++ b/dev_tools/src/bin/refresh-examples.rs @@ -0,0 +1,139 @@ +use std::path::PathBuf; + +use just_fmt::snake_case; +use just_template::{Template, tmpl}; + +const EXAMPLE_ROOT: &str = "./examples/"; +const OUTPUT_PATH: &str = "./mingling/src/example_docs.rs"; + +const TEMPLATE_CONTENT: &str = include_str!("../../../mingling/src/example_docs.rs.tmpl"); + +struct ExampleContent { + name: String, + header: String, + code: String, + cargo_toml: String, +} + +impl ExampleContent { + pub fn read(name: &str) -> Self { + let repo = find_git_repo().unwrap(); + let cargo_toml = Self::read_cargo_toml(&repo, name); + let (header, code) = Self::read_header_and_code(&repo, name); + + let cargo_toml = cargo_toml + .lines() + .map(|line| format!("/// {}", line)) + .collect::<Vec<_>>() + .join("\n"); + + let header = header + .lines() + .map(|line| format!("/// {}", line)) + .collect::<Vec<_>>() + .join("\n"); + + let code = code + .lines() + .map(|line| format!("/// {}", line)) + .collect::<Vec<_>>() + .join("\n"); + + ExampleContent { + name: name.to_string(), + header, + code, + cargo_toml, + } + } + + fn read_header_and_code(repo: &PathBuf, name: &str) -> (String, String) { + let file_path = repo + .join(EXAMPLE_ROOT) + .join(name) + .join("src") + .join("main.rs"); + let content = std::fs::read_to_string(&file_path).unwrap_or_default(); + let mut lines = content.lines(); + let mut header = String::new(); + let mut code = String::new(); + + // Collect header lines (starting with //!) + while let Some(line) = lines.next() { + if line.trim_start().starts_with("//!") { + let trimmed = line.trim_start_matches("//!").trim(); + header.push_str(trimmed); + header.push('\n'); + } else { + // First non-header line found, start collecting code + code.push_str(line); + code.push('\n'); + break; + } + } + + // Collect remaining code lines + for line in lines { + code.push_str(line); + code.push('\n'); + } + + (header.trim().to_string(), code.trim().to_string()) + } + + fn read_cargo_toml(repo: &PathBuf, name: &str) -> String { + let file_path = repo.join(EXAMPLE_ROOT).join(name).join("Cargo.toml"); + let content = std::fs::read_to_string(&file_path).unwrap_or_default(); + content + } +} + +fn main() { + let mut template = Template::from(TEMPLATE_CONTENT); + let repo_root = find_git_repo().unwrap(); + let example_root = repo_root.join(EXAMPLE_ROOT); + let mut examples = Vec::new(); + if let Ok(entries) = std::fs::read_dir(&example_root) { + for entry in entries.flatten() { + if let Ok(file_type) = entry.file_type() { + if file_type.is_dir() { + let example_name = entry.file_name().to_string_lossy().to_string(); + let example_content = ExampleContent::read(&example_name); + examples.push(example_content); + } + } + } + } + + for example in examples { + tmpl!(template += { + examples { + ( + example_header = example.header, + example_import = example.cargo_toml, + example_code = example.code, + example_name = snake_case!(example.name) + ) + } + }); + } + + std::fs::write(repo_root.join(OUTPUT_PATH), template.to_string()).unwrap(); +} + +fn find_git_repo() -> Option<std::path::PathBuf> { + let mut current_dir = std::env::current_dir().ok()?; + + loop { + let git_dir = current_dir.join(".git"); + if git_dir.exists() && git_dir.is_dir() { + return Some(current_dir); + } + + if !current_dir.pop() { + break; + } + } + + None +} diff --git a/dev_tools/src/lib.rs b/dev_tools/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dev_tools/src/lib.rs @@ -0,0 +1 @@ + diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs new file mode 100644 index 0000000..ec8162e --- /dev/null +++ b/mingling/src/example_docs.rs @@ -0,0 +1,382 @@ +// Auto generated + +/// `Mingling` Example - Basic +/// +/// # How to Run +/// ```bash +/// cargo run --manifest-path ./examples/example-basic/Cargo.toml -- hello World +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-basic" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling" } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, +/// marker::NextProcess, +/// }; +/// +/// // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry` +/// dispatcher!("hello", HelloCommand => HelloEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// // Create program +/// let mut program = ThisProgram::new(); +/// +/// // Add dispatcher `HelloCommand` +/// program.with_dispatcher(HelloCommand); +/// +/// // Run program +/// program.exec().await; +/// } +/// +/// // Register wrapper type `Hello`, setting inner to `String` +/// pack!(Hello = String); +/// +/// // Register chain to `ThisProgram`, handling logic from `HelloEntry` +/// #[chain] +/// async fn parse_name(prev: HelloEntry) -> NextProcess { +/// // Extract string from `HelloEntry` as argument +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// +/// // Build `Hello` type and route to renderer +/// Hello::new(name).to_render() +/// } +/// +/// // Register renderer to `ThisProgram`, handling rendering of `Hello` +/// #[renderer] +/// fn render_hello_who(prev: Hello) { +/// // Print message +/// r_println!("Hello, {}!", *prev); +/// +/// // Program ends here +/// } +/// +/// // Generate program, default is `ThisProgram` +/// gen_program!(); +/// ``` +pub mod example_basic {} +/// `Mingling` Example - Completion +/// +/// # How to Deploy +/// 1. Enable the `comp` feature +/// ```toml +/// mingling = { version = "0.1.5", features = [ +/// "comp", // Enable this feature +/// "parser" +/// ] } +/// ``` +/// +/// 2. Write `build.rs` to generate completion scripts at compile time +/// ```rust +/// use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name}; +/// fn main() { +/// // Generate completion scripts for the current program +/// build_comp_scripts().unwrap(); +/// +/// // Or specify a specific name +/// // build_comp_scripts_with_bin_name("your_bin").unwrap(); +/// } +/// ``` +/// +/// 3. Write `main.rs`, adding completion logic for your command entry point +/// 4. Execute `cargo install --path ./`, then run the corresponding completion script in your shell +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-completion" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = ["comp", "parser"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, Groupped, ShellContext, Suggest, +/// macros::{chain, completion, dispatcher, gen_program, r_println, renderer, suggest}, +/// marker::NextProcess, +/// parser::{Pickable, Picker}, +/// }; +/// +/// // Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry` +/// dispatcher!("fruit", FruitCommand => FruitEntry); +/// +/// #[completion(FruitEntry)] +/// fn comp_fruit_command(ctx: &ShellContext) -> Suggest { +/// if ctx.current_word.starts_with("-") { +/// return suggest! { +/// "--name": "Fruit name", +/// "--type": "Fruit type" +/// }; +/// } +/// if ctx.previous_word == "--name" { +/// return suggest!(); +/// } +/// if ctx.previous_word == "--type" { +/// return suggest! { +/// "apple", "banana" +/// }; +/// } +/// return suggest!(); +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// program.with_dispatcher(CompletionDispatcher); // Add completion dispatcher +/// program.with_dispatcher(FruitCommand); +/// program.exec().await; +/// } +/// +/// #[derive(Groupped)] +/// struct FruitInfo { +/// name: String, +/// fruit_type: FruitType, +/// } +/// +/// #[derive(Default, Debug)] +/// enum FruitType { +/// #[default] +/// Apple, +/// Banana, +/// Other(String), +/// } +/// +/// impl Pickable for FruitType { +/// type Output = FruitType; +/// +/// fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> { +/// let name = args.pick_argument(flag); +/// match name { +/// Some(name) => match name.as_str() { +/// "apple" => Some(FruitType::Apple), +/// "banana" => Some(FruitType::Banana), +/// other => Some(FruitType::Other(other.to_string())), +/// }, +/// None => None, +/// } +/// } +/// } +/// +/// #[chain] +/// async fn parse_fruit_info(prev: FruitEntry) -> NextProcess { +/// let picker = Picker::<ThisProgram>::from(prev.inner); +/// let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack_directly(); +/// let info = FruitInfo { +/// name: fruit_name, +/// fruit_type, +/// }; +/// AnyOutput::new(info).route_renderer() +/// } +/// +/// #[renderer] +/// fn render_fruit(prev: FruitInfo) { +/// if let FruitType::Other(other) = prev.fruit_type { +/// r_println!("Fruit name: {}, Type: {:?} (Unknown)", prev.name, other); +/// } else { +/// r_println!("Fruit name: {}, Type: {:?}", prev.name, prev.fruit_type); +/// } +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_completion {} +/// `Mingling` Example - General Renderer +/// +/// ## Step1 - Enable Feature +/// Enable the `general_renderer` feature for mingling in `Cargo.toml` +/// ```toml +/// [dependencies] +/// mingling = { version = "...", features = ["general_renderer", "parser"] } +/// ``` +/// +/// ## Step2 - Add Dependencies +/// Add `serde` dependency to `Cargo.toml` for serialization support +/// ```toml +/// [dependencies] +/// serde = { version = "1", features = ["derive"] } +/// ``` +/// +/// ## Step3 - Write Code +/// Write the following content into `main.rs` +/// +/// ## Step3 - Build and Run +/// ```bash +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --json +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --yaml +/// ``` +/// +/// Will print: +/// ```plain +/// Bob is 22 years old +/// {"member_name":"Bob","member_age":22} +/// member_name: Bob +/// member_age: 22 +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-general-renderer" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = [ +/// "parser", +/// "general_renderer", +/// ] } +/// serde = { version = "1", features = ["derive"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, Groupped, +/// macros::{chain, dispatcher, gen_program, r_println, renderer}, +/// marker::NextProcess, +/// parser::Picker, +/// setup::GeneralRendererSetup, +/// }; +/// use serde::Serialize; +/// +/// dispatcher!("render", RenderCommand => RenderCommandEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// // Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters +/// program.with_setup(GeneralRendererSetup); +/// program.with_dispatcher(RenderCommand); +/// program.exec().await; +/// } +/// +/// // Manually implement Info struct +/// #[derive(Serialize, Groupped)] +/// struct Info { +/// #[serde(rename = "member_name")] +/// name: String, +/// #[serde(rename = "member_age")] +/// age: i32, +/// } +/// +/// #[chain] +/// async fn parse_render(prev: RenderCommandEntry) -> NextProcess { +/// let (name, age) = Picker::<AnyOutput<ThisProgram>>::new(prev.inner) +/// .pick::<String>(()) +/// .pick::<i32>(()) +/// .unpack_directly(); +/// AnyOutput::new(Info { name, age }).route_renderer() +/// } +/// +/// // Implement default renderer for when general_renderer is not specified +/// #[renderer] +/// fn render_info(prev: Info) { +/// r_println!("{} is {} years old", prev.name, prev.age); +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_general_renderer {} +/// `Mingling` Example - Picker +/// +/// ## Step1 - Enable Feature +/// Enable the `parser` feature for mingling in `Cargo.toml` +/// ```toml +/// [dependencies] +/// mingling = { version = "...", features = ["parser"] } +/// ``` +/// +/// ## Step2 - Write Code +/// Write the following content into `main.rs` +/// +/// ## Step3 - Build and Run +/// ```bash +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob --age -15 +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick --age 99 +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-picker" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = ["parser"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, +/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, +/// marker::NextProcess, +/// parser::Picker, +/// }; +/// +/// dispatcher!("pick", PickCommand => PickEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// program.with_dispatcher(PickCommand); +/// program.exec().await; +/// } +/// +/// pack!(NoNameProvided = ()); +/// pack!(ParsedPickInput = (i32, String)); +/// +/// #[chain] +/// async fn parse(prev: PickEntry) -> NextProcess { +/// // Extract arguments from `PickEntry`'s inner and create a `Picker` +/// let picker = Picker::new(prev.inner); +/// let picked = picker +/// // First extract the named argument +/// .pick_or("--age", 20) +/// .after(|n: i32| n.clamp(0, 100)) +/// // Then sequentially extract the remaining arguments +/// .pick_or_route((), AnyOutput::new(NoNameProvided::default())) +/// .unpack(); +/// +/// match picked { +/// Ok(value) => ParsedPickInput::new(value).to_render(), +/// Err(e) => e.route_renderer(), +/// } +/// } +/// +/// #[renderer] +/// fn render_parsed_pick_input(prev: ParsedPickInput) { +/// let (age, name) = prev.inner; +/// r_println!("Picked: name = {}, age = {}", name, age); +/// } +/// +/// #[renderer] +/// fn render_no_name_input(_prev: NoNameProvided) { +/// r_println!("No name provided."); +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_picker {}
\ No newline at end of file diff --git a/mingling/src/example_docs.rs.tmpl b/mingling/src/example_docs.rs.tmpl new file mode 100644 index 0000000..5a32126 --- /dev/null +++ b/mingling/src/example_docs.rs.tmpl @@ -0,0 +1,18 @@ +// Auto generated + +>>>>>>>>>> examples + +@@@ >>> examples +<<<example_header>>> +/// +/// Cargo.toml +/// ``` +<<<example_import>>> +/// ``` +/// +/// main.rs +/// ```rust +<<<example_code>>> +/// ``` +pub mod <<<example_name>>> {} +@@@ <<< diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 8d0a8c2..2bd6b58 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -101,163 +101,9 @@ pub mod macros { /// derive macro Groupped pub use mingling_macros::Groupped; -pub mod docs { - pub mod basic { - //! # Basic Usage - //! - //! This module demonstrates basic usage of Mingling with a simple "hello" command. - //! - //! ## Example - //! - //! ```rust - //! use mingling::{ - //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, - //! marker::NextProcess, - //! }; - //! - //! // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry` - //! dispatcher!("hello", HelloCommand => HelloEntry); - //! - //! #[tokio::main] - //! async fn main() { - //! // Create program - //! let mut program = ThisProgram::new(); - //! - //! // Add dispatcher `HelloCommand` - //! program.with_dispatcher(HelloCommand); - //! - //! // Run program - //! program.exec().await; - //! } - //! - //! // Register wrapper type `Hello`, setting inner to `String` - //! pack!(Hello = String); - //! - //! // Register chain to `ThisProgram`, handling logic from `HelloEntry` - //! #[chain] - //! async fn parse_name(prev: HelloEntry) -> NextProcess { - //! // Extract string from `HelloEntry` as argument - //! let name = prev.get(0).cloned().unwrap_or_else(|| "World".to_string()); - //! - //! // Build `Hello` type and route to renderer - //! Hello::new(name).to_render() - //! } - //! - //! // Register renderer to `ThisProgram`, handling rendering of `Hello` - //! #[renderer] - //! fn render_hello_who(prev: Hello) { - //! // Print message - //! r_println!("Hello, {}!", *prev); - //! - //! // Program ends here - //! } - //! - //! // Generate program, default is `ThisProgram` - //! gen_program!(); - //! ``` - //! - //! ## Output - //! - //! ```text - //! > mycmd hello - //! Hello, World! - //! > mycmd hello Alice - //! Hello, Alice! - //! ``` - } +mod example_docs; - pub mod parser { - //! # Feature `parser` Usage - //! - //! This module demonstrates advanced usage of Mingling with the `Picker` utility for argument parsing. - //! - //! ## Example - //! - //! ```rust - //! use mingling::{ - //! AnyOutput, - //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, - //! marker::NextProcess, - //! parser::Picker, - //! }; - //! - //! // Define dispatcher `RepeatCommand`, directing subcommand "repeat" to `RepeatEntry` - //! dispatcher!("repeat", RepeatCommand => RepeatEntry); - //! - //! #[tokio::main] - //! async fn main() { - //! // Create program - //! let mut program = ThisProgram::new(); - //! - //! // Add dispatcher `RepeatCommand` - //! program.with_dispatcher(RepeatCommand); - //! - //! // Run program - //! program.exec().await; - //! } - //! - //! // Register wrapper type `RepeatArgument`, setting inner to `(i32, String)` - //! pack!(RepeatArgument = (i32, String)); - //! - //! // Register error type - //! pack!(ErrorContentRequired = ()); - //! - //! // Register chain to `ThisProgram`, handling logic for `RepeatEntry` - //! #[chain] - //! async fn parse_repeat_args(prev: RepeatEntry) -> NextProcess { - //! let picker = Picker::new(prev.inner); // Create Picker from user arguments - //! let picked = picker - //! .pick_or::<i32>("--time", 1) // Extract argument `--time` - //! .after(|n| n.clamp(1, 20)) // Clamp extracted number between 1 - 20 - //! // Extract first remaining argument as content, route to type `ErrorContentRequired` if not found - //! .pick_or_route((), AnyOutput::new(ErrorContentRequired::default())) - //! .unpack(); // Unpack - //! - //! match picked { - //! Ok(args) => { - //! // Build `RepeatArgument` type and route to renderer - //! RepeatArgument::new(args).to_render() - //! } - //! Err(e) => { - //! // Extraction failed, route to error type - //! e.route_renderer() - //! } - //! } - //! } - //! - //! // Render `RepeatArgument` - //! #[renderer] - //! fn render_repeat(prev: RepeatArgument) { - //! let (times, content) = prev.inner; - //! for _ in 0..times { - //! r_println!("{}", content); - //! } - //! } - //! - //! // Render `ErrorContentRequired` - //! #[renderer] - //! fn render_error_content_required(_prev: ErrorContentRequired) { - //! r_println!("Error: content is required"); - //! } - //! - //! // Generate program, default is `ThisProgram` - //! gen_program!(); - //! ``` - //! - //! ## Output - //! - //! ```text - //! > mycmd repeat --time 3 Hello - //! Hello - //! Hello - //! Hello - //! > mycmd repeat --time 25 Hello - //! Hello - //! Hello - //! Hello - //! ... (repeated 20 times, clamped from 25) - //! > mycmd repeat --time 3 - //! Error: content is required - //! ``` - } +/// Example projects for `Mingling`, for learning how to use `Mingling` +pub mod _mingling_examples { + pub use crate::example_docs::*; } diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs index b4124a9..2c5cf55 100644 --- a/mingling_core/src/lib.rs +++ b/mingling_core/src/lib.rs @@ -51,9 +51,12 @@ pub mod setup { #[doc(hidden)] pub mod builds; + +/// Provides build scripts for users pub mod build { #[cfg(feature = "comp")] pub use crate::builds::comp::*; } +/// Provided for framework developers pub mod debug; diff --git a/run-tools.sh b/run-tools.sh new file mode 100755 index 0000000..7bfcd29 --- /dev/null +++ b/run-tools.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +cd "$(dirname "$0")" || exit 1 + +if [ $# -eq 0 ]; then + echo "Available:" + if [ -d "dev_tools/src/bin" ]; then + for file in dev_tools/src/bin/*.rs; do + if [ -f "$file" ]; then + basename "$file" .rs + fi + done + else + echo "Warning: dev_tools/src/bin directory does not exist" + fi + exit 1 +fi + +target_bin="$1" +target_file="dev_tools/src/bin/${target_bin}.rs" + +if [ ! -f "$target_file" ]; then + echo "Error: target file '$target_file' does not exist" + exit 1 +fi + +cargo run --manifest-path dev_tools/Cargo.toml --bin "$1" |
