aboutsummaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-04-16 21:31:57 +0800
committer魏曹先生 <1992414357@qq.com>2026-04-16 21:31:57 +0800
commit363fbc6e98f832471a17a10ec18e8823df6a2ed5 (patch)
tree98f71ab1796c1a9c1df411eee5174dd92001ef94 /src/cli
Initialize Rust project with billing calculation functionality
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/calc_cmd.rs9
-rw-r--r--src/cli/consts.rs1
-rw-r--r--src/cli/dispatchers.rs12
-rw-r--r--src/cli/entry.rs21
-rw-r--r--src/cli/io_error.rs42
-rw-r--r--src/cli/ops_cmd.rs80
6 files changed, 165 insertions, 0 deletions
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<ThisProgram, ThisProgram>) {
+ 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<std::io::Error> for IOError {
+ fn from(error: std::io::Error) -> Self {
+ Self::new(error)
+ }
+}
+
+impl Serialize for IOError {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ 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<String>) -> PathBuf {
+ let path = Picker::<()>::new(args)
+ .pick::<String>(())
+ .unpack_directly()
+ .0;
+ PathBuf::from(path)
+}