use colored::Colorize; use mingling::{ Groupped, LazyRes, macros::{chain, dispatcher_clap, pack, pack_err, r_println, renderer}, res::ResExitCode, }; use rorolala::bucket::{self, bind::BucketBind}; use serde::Serialize; use shared_functions::info; use crate::{ Next, bucket_mgr::ResultEnumBucketOperation::Set, error::{ErrorRequireOverwrite, ErrorSpace}, locale::I18nBucketManager, output::display::markdown, res::{bucket::ResBucketWithoutProtocol, overwrite::ResOverwrite}, }; pub const EC_BUCKET_BIND_INDEX_NOT_PROVIDED: i32 = 251; pub const EC_BUCKET_BIND_INDEX_NOT_FOUND: i32 = 252; #[derive(Default, clap::Parser, Serialize, Groupped)] #[dispatcher_clap("bucket.bind", CMDBucketBind)] pub struct EntryBucketBind { index: Option, #[arg(long, conflicts_with_all = ["set"])] remove: bool, #[arg(long, conflicts_with_all = ["remove"])] set: Option, } pack!(StateBucketBindListAll = ()); pack!(StateBucketBindListSpecified = u8); pack!(StateBucketBindRemove = u8); pack!(StateBucketBindSet = (u8, String)); // idx, url #[derive(Default, Serialize, Groupped)] pub struct ResultAllBucketBind { bind_list: Vec, } #[derive(Default, Serialize, Groupped)] pub struct ResultOneBucketBind { index: u8, url: String, } #[derive(Default, Serialize, Groupped)] pub struct ResultBucketOperated { pub operation: ResultEnumBucketOperation, pub index: u8, pub url: Option, } #[derive(Default, Serialize)] pub enum ResultEnumBucketOperation { #[serde(rename = "none")] #[default] None, #[serde(rename = "set_bind")] Set, #[serde(rename = "remove_bind")] Remove, } pack_err!(ErrorBucketBindIndexNotProvided); pack_err!(ErrorBucketBindIndexNotFound = u8); // Chains #[chain] pub fn handle_bucket_bind(args: EntryBucketBind) -> Next { match args.index { Some(op_idx) => { if let Some(url) = args.set { // bind idx --set url return StateBucketBindSet::new((op_idx, url)).to_chain(); } else if args.remove { // bind idx --remove return StateBucketBindRemove::new(op_idx).to_chain(); } else { // bind idx return StateBucketBindListSpecified::new(op_idx).to_chain(); } } None => { if !args.remove && args.set.is_none() { // bind return StateBucketBindListAll::new(()).to_chain(); } else { // index not provided // bind --set url // or // bind --remove return ErrorBucketBindIndexNotProvided::default().to_render(); } } } } #[chain] pub fn handle_state_bucket_bind_set( set: StateBucketBindSet, bucket: &mut LazyRes, overwrite: &ResOverwrite, ) -> Next { let (index, url) = set.inner; let bucket_space_ref = &bucket.get_ref().space; let exist = match bucket::bind::check_bucket_bind_exists(bucket_space_ref, index) { Ok(exists) => exists, Err(e) => return ErrorSpace::from(e).to_chain(), }; // When the bind already exists but overwrite is not specified // Dispatch to ErrorRequireOverwrite if exist && !overwrite.overwrite { return ErrorRequireOverwrite::default().to_render(); } if let Err(space_error) = bucket::bind::write_bucket_bind(bucket_space_ref, index, url.clone().as_str()) { ErrorSpace::from(space_error).to_chain(); } ResultBucketOperated { operation: Set, index, url: Some(url), } .to_render() } #[chain] pub fn handle_state_bucket_bind_remove( remove: StateBucketBindRemove, bucket: &mut LazyRes, ) -> Next { let index = remove.inner; let bucket_space_ref = &bucket.get_ref().space; if let Err(space_error) = bucket::bind::remove_bucket_bind(bucket_space_ref, index) { return ErrorSpace::from(space_error).to_chain(); } ResultBucketOperated { operation: ResultEnumBucketOperation::Remove, index, url: None, } .to_render() } #[chain] pub fn handle_state_bucket_bind_list_specified( index: StateBucketBindListSpecified, bucket: &mut LazyRes, ) -> Next { let index = index.inner; let bucket_space_ref = &bucket.get_ref().space; let bind_info = match bucket::bind::read_bucket_bind(bucket_space_ref, index) { Err(e) => return ErrorSpace::from(e).to_chain(), Ok(None) => return ErrorBucketBindIndexNotFound::new(index).to_render(), Ok(Some(r)) => r, }; ResultOneBucketBind { index: bind_info.get_index(), url: bind_info.get_url().to_string(), } .to_render() } #[chain] pub fn handle_state_bucket_bind_list_all( _p: StateBucketBindListAll, bucket: &mut LazyRes, ) -> Next { let bucket_space_ref = &bucket.get_ref().space; info!("Reading all bucket binds from space"); let bind_list = match bucket::bind::read_bucket_binds(bucket_space_ref) { Err(e) => return ErrorSpace::from(e).to_chain(), Ok(r) => r, }; info!("Read {} bucket binds", bind_list.len()); ResultAllBucketBind { bind_list }.to_render() } // Result Renderers #[renderer] pub fn render_result_one_bucket_bind(result: ResultOneBucketBind) { r_println!("BIND_{} => \"{}\"", result.index, result.url.trim().bold()); } #[renderer] pub fn render_result_all_bucket_bind(result: ResultAllBucketBind) { let list: Vec = result .bind_list .iter() .map(|b| { format!( "BIND_{} => \"{}\"", b.get_index(), b.get_url().trim().bold() ) }) .collect(); for item in list { r_println!("{}", item); } } #[renderer] pub fn render_result_bucket_operated(result: ResultBucketOperated) { let index = result.index; let url = result.url.unwrap_or("".to_string()); match result.operation { ResultEnumBucketOperation::Set => { r_println!( "{} BIND_{} => \"{}\"", "+++".bold().bright_green(), index, url.bold().trim() ) } ResultEnumBucketOperation::Remove => { r_println!("{} BIND_{}", "---".bold().bright_red(), index) } _ => {} } } // Error Renderers #[renderer] pub fn render_error_bucket_bind_index_not_provided( _err: ErrorBucketBindIndexNotProvided, ec: &mut ResExitCode, ) { r_println!( "{}", markdown(I18nBucketManager::error_bind_index_not_provided().trim()) ); ec.exit_code = EC_BUCKET_BIND_INDEX_NOT_PROVIDED; } #[renderer] pub fn render_error_bucket_bind_index_not_found( err: ErrorBucketBindIndexNotFound, ec: &mut ResExitCode, ) { r_println!( "{}", markdown(I18nBucketManager::error_bind_index_not_found(err.info.to_string()).trim()) ); ec.exit_code = EC_BUCKET_BIND_INDEX_NOT_FOUND; }