diff options
Diffstat (limited to 'crates/service')
| -rw-r--r-- | crates/service/Cargo.toml | 11 | ||||
| -rw-r--r-- | crates/service/service_macros/Cargo.toml | 15 | ||||
| -rw-r--r-- | crates/service/service_macros/src/lib.rs | 149 | ||||
| -rw-r--r-- | crates/service/src/action.rs | 38 | ||||
| -rw-r--r-- | crates/service/src/action_pool.rs | 108 | ||||
| -rw-r--r-- | crates/service/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/service/todo.txt | 36 |
7 files changed, 0 insertions, 362 deletions
diff --git a/crates/service/Cargo.toml b/crates/service/Cargo.toml deleted file mode 100644 index 8059255..0000000 --- a/crates/service/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "vcs_service" -edition = "2024" -version.workspace = true - -[dependencies] -tcp_connection = { path = "../utils/tcp_connection" } -vcs_service_macros = { path = "service_macros" } - -# Serialization -serde = { version = "1.0.219", features = ["derive"] } diff --git a/crates/service/service_macros/Cargo.toml b/crates/service/service_macros/Cargo.toml deleted file mode 100644 index 5cc1ac5..0000000 --- a/crates/service/service_macros/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "vcs_service_macros" -edition = "2024" -version.workspace = true - -[lib] -proc-macro = true - -[dependencies] -tcp_connection = { path = "../../utils/tcp_connection" } -string_proc = { path = "../../utils/string_proc" } - -syn = { version = "2.0", features = ["full", "extra-traits"] } -quote = "1.0" -proc-macro2 = "1.0" diff --git a/crates/service/service_macros/src/lib.rs b/crates/service/service_macros/src/lib.rs deleted file mode 100644 index 2c2e4a8..0000000 --- a/crates/service/service_macros/src/lib.rs +++ /dev/null @@ -1,149 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{ItemFn, parse_macro_input}; - -/// A procedural macro for generating structs that implement the Action trait -/// -/// Usage: -/// #[action_gen] or #[action_gen(local)] -/// pub fn my_action(ctx: ActionContext, arg: MyArg) -> Result<MyReturn, TcpTargetError> { -/// todo!() -/// } -#[proc_macro_attribute] -pub fn action_gen(attr: TokenStream, item: TokenStream) -> TokenStream { - let input_fn = parse_macro_input!(item as ItemFn); - let is_local = if attr.is_empty() { - false - } else { - let attr_str = attr.to_string(); - attr_str == "local" || attr_str.contains("local") - }; - - generate_action_struct(input_fn, is_local).into() -} - -fn generate_action_struct(input_fn: ItemFn, _is_local: bool) -> proc_macro2::TokenStream { - let fn_vis = &input_fn.vis; - let fn_sig = &input_fn.sig; - let fn_name = &fn_sig.ident; - let fn_block = &input_fn.block; - - validate_function_signature(fn_sig); - - let (arg_type, return_type) = extract_types(fn_sig); - - let struct_name = quote::format_ident!("{}", convert_to_pascal_case(&fn_name.to_string())); - - let action_name_ident = &fn_name; - - quote! { - #[derive(Debug, Clone, Default)] - #fn_vis struct #struct_name; - - impl vcs_service::action::Action<#arg_type, #return_type> for #struct_name { - fn action_name() -> &'static str { - Box::leak(string_proc::snake_case!(stringify!(#action_name_ident)).into_boxed_str()) - } - - fn is_remote_action() -> bool { - !#_is_local - } - - async fn process(context: vcs_service::action::ActionContext, args: #arg_type) -> Result<#return_type, tcp_connection::error::TcpTargetError> { - #fn_block - } - } - - #[deprecated = "This function is used by #[action_gen] as a template."] - #[doc = " This function is used by #[action_gen] as a template to generate the struct. "] - #[doc = " It is forbidden to call it anywhere."] - #[doc = " You should use the generated struct to register this function in `ActionPool`"] - #[doc = " and call it using the function name."] - #fn_vis #fn_sig #fn_block - } -} - -fn validate_function_signature(fn_sig: &syn::Signature) { - if !fn_sig.asyncness.is_some() { - panic!("Expected async function for Action, but found synchronous function"); - } - - if fn_sig.inputs.len() != 2 { - panic!( - "Expected exactly 2 arguments for Action function: ctx: ActionContext and arg: T, but found {} arguments", - fn_sig.inputs.len() - ); - } - - let return_type = match &fn_sig.output { - syn::ReturnType::Type(_, ty) => ty, - _ => panic!( - "Expected Action function to return Result<T, TcpTargetError>, but found no return type" - ), - }; - - if let syn::Type::Path(type_path) = return_type.as_ref() { - if let Some(segment) = type_path.path.segments.last() { - if segment.ident != "Result" { - panic!( - "Expected Action function to return Result<T, TcpTargetError>, but found different return type" - ); - } - } - } else { - panic!( - "Expected Action function to return Result<T, TcpTargetError>, but found no return type" - ); - } -} - -fn convert_to_pascal_case(s: &str) -> String { - s.split('_') - .map(|word| { - let mut chars = word.chars(); - match chars.next() { - None => String::new(), - Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(), - } - }) - .collect() -} - -fn extract_types(fn_sig: &syn::Signature) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { - let mut inputs = fn_sig.inputs.iter(); - - let _ = inputs.next(); - - let arg_type = match inputs.next() { - Some(syn::FnArg::Typed(pat_type)) => { - let ty = &pat_type.ty; - quote::quote!(#ty) - } - _ => { - panic!("Expected the second argument to be a typed parameter, but found something else") - } - }; - - let return_type = match &fn_sig.output { - syn::ReturnType::Type(_, ty) => { - if let syn::Type::Path(type_path) = ty.as_ref() { - if let syn::PathArguments::AngleBracketed(args) = - &type_path.path.segments.last().unwrap().arguments - { - if let Some(syn::GenericArgument::Type(ty)) = args.args.first() { - quote::quote!(#ty) - } else { - panic!("Expected to extract the success type of Result, but failed"); - } - } else { - panic!("Expected Result type to have generic parameters, but found none"); - } - } else { - panic!("Expected return type to be Result, but found different type"); - } - } - _ => panic!("Expected function to have return type, but found none"), - }; - - (arg_type, return_type) -} diff --git a/crates/service/src/action.rs b/crates/service/src/action.rs deleted file mode 100644 index 14f1148..0000000 --- a/crates/service/src/action.rs +++ /dev/null @@ -1,38 +0,0 @@ -use tcp_connection::{error::TcpTargetError, instance::ConnectionInstance}; - -pub trait Action<Args, Return> { - fn action_name() -> &'static str; - - fn is_remote_action() -> bool; - - fn process( - context: ActionContext, - args: Args, - ) -> impl std::future::Future<Output = Result<Return, TcpTargetError>> + Send; -} - -pub struct ActionContext { - // Whether the action is executed locally or remotely - local: bool, - - /// The connection instance in the current context, - /// used to interact with the machine on the other end - instance: ConnectionInstance, -} - -impl ActionContext { - /// Whether the action is executed locally - pub fn is_local(&self) -> bool { - self.local - } - - /// Whether the action is executed remotely - pub fn is_remote(&self) -> bool { - !self.local - } - - /// Get the connection instance in the current context - pub fn instance(&self) -> &ConnectionInstance { - &self.instance - } -} diff --git a/crates/service/src/action_pool.rs b/crates/service/src/action_pool.rs deleted file mode 100644 index 0a1a6c7..0000000 --- a/crates/service/src/action_pool.rs +++ /dev/null @@ -1,108 +0,0 @@ -use tcp_connection::error::TcpTargetError; - -use crate::action::{Action, ActionContext}; - -/// A pool of registered actions that can be processed by name -pub struct ActionPool { - /// HashMap storing action name to action implementation mapping - actions: std::collections::HashMap<&'static str, Box<dyn ActionErased>>, -} - -impl ActionPool { - /// Creates a new empty ActionPool - pub fn new() -> Self { - Self { - actions: std::collections::HashMap::new(), - } - } - - /// Registers an action type with the pool - /// - /// Usage: - /// ``` - /// action_pool.register::<MyAction, MyArgs, MyReturn>(); - /// ``` - pub fn register<A, Args, Return>(&mut self) - where - A: Action<Args, Return> + Send + Sync + 'static, - Args: serde::de::DeserializeOwned + Send + Sync + 'static, - Return: serde::Serialize + Send + Sync + 'static, - { - let action_name = A::action_name(); - self.actions.insert( - action_name, - Box::new(ActionWrapper::<A, Args, Return>(std::marker::PhantomData)), - ); - } - - /// Processes an action by name with given context and arguments - /// - /// Usage: - /// ``` - /// let result = action_pool.process::<MyArgs, MyReturn>("my_action", context, args).await?; - /// ``` - pub async fn process<'a, Args, Return>( - &'a self, - action_name: &'a str, - context: ActionContext, - args: Args, - ) -> Result<Return, TcpTargetError> - where - Args: serde::de::DeserializeOwned + Send + 'static, - Return: serde::Serialize + Send + 'static, - { - if let Some(action) = self.actions.get(action_name) { - let result = action.process_erased(context, Box::new(args)).await?; - let result = *result - .downcast::<Return>() - .map_err(|_| TcpTargetError::Unsupported("InvalidArguments".to_string()))?; - Ok(result) - } else { - Err(TcpTargetError::Unsupported("InvalidAction".to_string())) - } - } -} - -/// Trait for type-erased actions that can be stored in ActionPool -trait ActionErased: Send + Sync { - /// Processes the action with type-erased arguments and returns type-erased result - fn process_erased( - &self, - context: ActionContext, - args: Box<dyn std::any::Any + Send>, - ) -> std::pin::Pin< - Box< - dyn std::future::Future<Output = Result<Box<dyn std::any::Any + Send>, TcpTargetError>> - + Send, - >, - >; -} - -/// Wrapper struct that implements ActionErased for concrete Action types -struct ActionWrapper<A, Args, Return>(std::marker::PhantomData<(A, Args, Return)>); - -impl<A, Args, Return> ActionErased for ActionWrapper<A, Args, Return> -where - A: Action<Args, Return> + Send + Sync, - Args: serde::de::DeserializeOwned + Send + Sync + 'static, - Return: serde::Serialize + Send + Sync + 'static, -{ - fn process_erased( - &self, - context: ActionContext, - args: Box<dyn std::any::Any + Send>, - ) -> std::pin::Pin< - Box< - dyn std::future::Future<Output = Result<Box<dyn std::any::Any + Send>, TcpTargetError>> - + Send, - >, - > { - Box::pin(async move { - let args = *args - .downcast::<Args>() - .map_err(|_| TcpTargetError::Unsupported("InvalidArguments".to_string()))?; - let result = A::process(context, args).await?; - Ok(Box::new(result) as Box<dyn std::any::Any + Send>) - }) - } -} diff --git a/crates/service/src/lib.rs b/crates/service/src/lib.rs deleted file mode 100644 index fe2c34e..0000000 --- a/crates/service/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use vcs_service_macros::*; -pub extern crate vcs_service_macros as macros; - -pub mod action; -pub mod action_pool; diff --git a/crates/service/todo.txt b/crates/service/todo.txt deleted file mode 100644 index 65c94ef..0000000 --- a/crates/service/todo.txt +++ /dev/null @@ -1,36 +0,0 @@ -本地文件操作 -设置上游服务器(仅设置,不会连接和修改染色标识) -验证连接、权限,并为当前工作区染色(若已染色,则无法连接不同标识的服务器) -进入表 (否则无法做任何操作) -退出表 (文件将会从当前目录移出,等待下次进入时还原) -去色 - 断开与上游服务器的关联 -跟踪本地文件的移动、重命名,立刻同步至表 -扫描本地文件结构,标记变化 -通过本地暂存的表索引搜索文件 -查询本地某个文件的状态 -查询当前目录的状态 -查询工作区状态 -将本地所有文件更新到最新状态 -提交所有产生变化的自身所属文件 - - -表操作(必须指定成员和表) -表查看 - 指定表并查看结构 -从参照表拉入文件项目 -将文件项目(或多个)导出到指定表 -查看导入请求 -在某个本地地址同意并导入文件 -拒绝某个、某些或所有导入请求 -删除表中的映射,但要确保实际文件已被移除 (忽略文件) -放弃表,所有者消失,下一个切换至表的人获得(放弃需要确保表中没有任何文件是所有者持有的)(替代目前的安全删除) - - -虚拟文件操作 -跟踪本地某些文件,并将其创建为虚拟文件,然后添加到自己的表 -根据本地文件的目录查找虚拟文件,并为自己获得所有权(需要确保版本和上游同步才可) -根据本地文件的目录查找虚拟文件,并放弃所有权(需要确保和上游同步才可) -根据本地文件的目录查找虚拟文件,并定向到指定的存在的老版本 - - -?为什么虚拟文件不能删除:虚拟文件的唯一删除方式就是,没有人再用他 -?为什么没有删除表:同理,表权限可以转移,但是删除只能等待定期清除无主人的表 |
