From 363fbc6e98f832471a17a10ec18e8823df6a2ed5 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 16 Apr 2026 21:31:57 +0800 Subject: Initialize Rust project with billing calculation functionality --- src/cli/calc_cmd.rs | 9 ++++++ src/cli/consts.rs | 1 + src/cli/dispatchers.rs | 12 ++++++++ src/cli/entry.rs | 21 +++++++++++++ src/cli/io_error.rs | 42 ++++++++++++++++++++++++++ src/cli/ops_cmd.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+) create mode 100644 src/cli/calc_cmd.rs create mode 100644 src/cli/consts.rs create mode 100644 src/cli/dispatchers.rs create mode 100644 src/cli/entry.rs create mode 100644 src/cli/io_error.rs create mode 100644 src/cli/ops_cmd.rs (limited to 'src/cli') diff --git a/src/cli/calc_cmd.rs b/src/cli/calc_cmd.rs new file mode 100644 index 0000000..09f4e03 --- /dev/null +++ b/src/cli/calc_cmd.rs @@ -0,0 +1,9 @@ +// use mingling::macros::dispatcher; +// use mingling::{macros::chain, marker::NextProcess}; + +// use crate::cli::entry::*; + +// dispatcher!("calc", CalculateCommand => CalculateEntry); + +// #[chain] +// pub async fn parse_calc_entry(prev: CalculateEntry) -> NextProcess {} diff --git a/src/cli/consts.rs b/src/cli/consts.rs new file mode 100644 index 0000000..254b611 --- /dev/null +++ b/src/cli/consts.rs @@ -0,0 +1 @@ +pub const BILL_WORKSPACE_CONFIG_FILE: &str = "cobill.yml"; diff --git a/src/cli/dispatchers.rs b/src/cli/dispatchers.rs new file mode 100644 index 0000000..27b7747 --- /dev/null +++ b/src/cli/dispatchers.rs @@ -0,0 +1,12 @@ +use mingling::{Program, macros::program_setup}; + +use crate::ThisProgram; +use crate::cli::ops_cmd::{CreateCommand, InitHereCommand}; + +#[program_setup] +pub fn chaos_billing_setup(program: &mut Program) { + program.with_dispatcher(InitHereCommand); + program.with_dispatcher(CreateCommand); + + // program.with_dispatcher(CalculateCommand); +} diff --git a/src/cli/entry.rs b/src/cli/entry.rs new file mode 100644 index 0000000..e68b7b4 --- /dev/null +++ b/src/cli/entry.rs @@ -0,0 +1,21 @@ +use mingling::setup::GeneralRendererSetup; + +use crate::__completion_gen::CompletionDispatcher; +use crate::ThisProgram; +use crate::cli::dispatchers::*; + +pub async fn entry() { + let mut program = ThisProgram::new(); + + // Add Completion + program.with_dispatcher(CompletionDispatcher); + + // Add General Renderer + program.with_setup(GeneralRendererSetup); + + // Setup `cobill` + program.with_setup(ChaosBillingSetup); + + // Execute + program.exec().await; +} diff --git a/src/cli/io_error.rs b/src/cli/io_error.rs new file mode 100644 index 0000000..49b9939 --- /dev/null +++ b/src/cli/io_error.rs @@ -0,0 +1,42 @@ +use mingling::{ + Groupped, + macros::{r_println, renderer}, +}; +use serde::Serialize; + +use crate::ThisProgram; + +#[derive(Groupped)] +pub struct IOError { + inner: std::io::Error, +} + +impl IOError { + pub fn new(error: std::io::Error) -> Self { + Self { inner: error } + } +} + +impl From for IOError { + fn from(error: std::io::Error) -> Self { + Self::new(error) + } +} + +impl Serialize for IOError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut state = serializer.serialize_struct("IOError", 2)?; + state.serialize_field("kind", &self.inner.kind().to_string())?; + state.serialize_field("info", &self.inner.to_string())?; + state.end() + } +} + +#[renderer] +pub fn render_io_error(prev: IOError) { + r_println!("{}: {}", prev.inner.kind(), prev.inner.to_string()) +} diff --git a/src/cli/ops_cmd.rs b/src/cli/ops_cmd.rs new file mode 100644 index 0000000..4b0eea7 --- /dev/null +++ b/src/cli/ops_cmd.rs @@ -0,0 +1,80 @@ +use std::{ + env::current_dir, + fs::{self, create_dir_all}, + path::PathBuf, +}; + +use mingling::{ + AnyOutput, + macros::{chain, dispatcher, pack, r_println, renderer}, + marker::NextProcess, + parser::Picker, +}; + +use crate::{ + ThisProgram, + cli::{consts::BILL_WORKSPACE_CONFIG_FILE, io_error::IOError}, +}; + +dispatcher!("init", InitHereCommand => InitEntry); +dispatcher!("create", CreateCommand => CreateEntry); + +pack!(StateCreateWorkspace = PathBuf); + +#[chain] +pub async fn handle_init_command(_prev: InitEntry) -> NextProcess { + let current_dir = match current_dir() { + Ok(d) => d, + Err(e) => return AnyOutput::new(IOError::from(e)).route_renderer(), + }; + StateCreateWorkspace::new(current_dir).to_chain() +} + +#[chain] +pub async fn handle_create_command(prev: CreateEntry) -> NextProcess { + let path = pick_path(prev.inner); + StateCreateWorkspace::new(path).to_chain() +} + +#[chain] +pub async fn handle_state_create_workspace(prev: StateCreateWorkspace) -> NextProcess { + let dir = prev.inner; + let file = dir.join(BILL_WORKSPACE_CONFIG_FILE); + + match create_dir_all(&dir) { + Ok(d) => d, + Err(e) => return AnyOutput::new(IOError::from(e)).route_renderer(), + }; + + if file.exists() { + return AnyOutput::new(WorkspaceConfigAlreadyExists::new(dir)).route_renderer(); + } + + if let Err(e) = fs::write(file, "") { + return AnyOutput::new(IOError::from(e)).route_renderer(); + } + + StateWorkspaceCreated::new(dir).to_render() +} + +pack!(StateWorkspaceCreated = PathBuf); + +#[renderer] +pub fn render_workspace_created(prev: StateWorkspaceCreated) { + r_println!("Workspace created at: {:?}", prev.inner); +} + +pack!(WorkspaceConfigAlreadyExists = PathBuf); + +#[renderer] +pub fn render_workspace_config_already_exists(prev: WorkspaceConfigAlreadyExists) { + r_println!("Workspace config already exists: {:?}", prev.inner); +} + +fn pick_path(args: Vec) -> PathBuf { + let path = Picker::<()>::new(args) + .pick::(()) + .unpack_directly() + .0; + PathBuf::from(path) +} -- cgit