summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock57
-rw-r--r--Cargo.toml11
-rw-r--r--src/lib.rs4
-rw-r--r--systems/_constants/Cargo.toml7
-rw-r--r--systems/_constants/macros/Cargo.toml15
-rw-r--r--systems/_constants/macros/src/lib.rs189
-rw-r--r--systems/_constants/src/lib.rs147
7 files changed, 429 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index adaca97..8c69537 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -51,6 +51,15 @@ dependencies = [
]
[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -281,6 +290,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
+name = "constants"
+version = "0.1.0"
+dependencies = [
+ "macros",
+]
+
+[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -671,6 +687,7 @@ dependencies = [
"action_system",
"cfg_file",
"chrono",
+ "constants",
"data_struct",
"sha1_hash",
"string_proc",
@@ -729,6 +746,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
+name = "macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "regex",
+ "string_proc",
+ "syn",
+]
+
+[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1034,6 +1062,35 @@ dependencies = [
]
[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
+
+[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index f0274c1..e8854a2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,12 +38,15 @@ members = [
"systems/action",
"systems/action/action_macros",
+ "systems/_constants",
+ "systems/_constants/macros",
+
"legacy_data",
"legacy_data/tests",
"legacy_actions",
- "docs",
+ "docs", "systems/_constants",
]
[workspace.package]
@@ -73,14 +76,20 @@ chrono = "0.4"
toml = "0.9"
[dependencies]
+# Utils
cfg_file = { path = "utils/cfg_file" }
data_struct = { path = "utils/data_struct" }
sha1_hash = { path = "utils/sha1_hash" }
tcp_connection = { path = "utils/tcp_connection" }
string_proc = { path = "utils/string_proc" }
+# Systems
action_system = { path = "systems/action" }
+constants = { path = "systems/_constants" }
+# Documents
vcs_docs = { path = "docs" }
+
+# Legacy
vcs_data = { path = "legacy_data" }
vcs_actions = { path = "legacy_actions" }
diff --git a/src/lib.rs b/src/lib.rs
index 4574d92..0abe27f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,10 @@ pub mod system {
pub mod action_system {
pub use action_system::*;
}
+
+ pub mod constants {
+ pub use constants::*;
+ }
}
pub mod utils {
diff --git a/systems/_constants/Cargo.toml b/systems/_constants/Cargo.toml
new file mode 100644
index 0000000..c57049c
--- /dev/null
+++ b/systems/_constants/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "constants"
+edition = "2024"
+version.workspace = true
+
+[dependencies]
+macros = { path = "macros" }
diff --git a/systems/_constants/macros/Cargo.toml b/systems/_constants/macros/Cargo.toml
new file mode 100644
index 0000000..04ad4fc
--- /dev/null
+++ b/systems/_constants/macros/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "macros"
+version = "0.1.0"
+edition = "2024"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "2.0", features = ["full", "extra-traits"] }
+quote = "1.0"
+proc-macro2 = "1.0"
+regex = "1.12"
+
+string_proc = { path = "../../../utils/string_proc" }
diff --git a/systems/_constants/macros/src/lib.rs b/systems/_constants/macros/src/lib.rs
new file mode 100644
index 0000000..ebcc31f
--- /dev/null
+++ b/systems/_constants/macros/src/lib.rs
@@ -0,0 +1,189 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{Expr, ExprLit, Ident, Item, ItemMod, Lit, LitStr, parse_macro_input};
+
+#[proc_macro_attribute]
+pub fn constants(attr: TokenStream, item: TokenStream) -> TokenStream {
+ let prefix = parse_macro_input!(attr as LitStr).value();
+ let mut input_mod = parse_macro_input!(item as ItemMod);
+
+ if let Some((_, items)) = &mut input_mod.content {
+ // Collect functions to generate
+ let mut generated_functions = Vec::new();
+
+ for item in items.iter_mut() {
+ if let Item::Macro(macro_item) = item {
+ // Check if it's a c! macro call
+ if macro_item.mac.path.is_ident("c") {
+ // Parse macro content
+ let macro_tokens = macro_item.mac.tokens.clone();
+ let parsed: syn::Result<Expr> = syn::parse2(macro_tokens);
+
+ if let Ok(Expr::Assign(assign)) = parsed {
+ // Get constant name and value
+ if let Some((const_name, const_value)) =
+ extract_const_name_and_value(&assign)
+ {
+ // Process constant and generate function
+ if let Some(func) = process_constant(&prefix, const_name, const_value) {
+ generated_functions.push(func);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for func in generated_functions {
+ items.push(Item::Verbatim(func));
+ }
+ }
+
+ TokenStream::from(quote! { #input_mod })
+}
+
+fn extract_const_name_and_value(assign: &syn::ExprAssign) -> Option<(Ident, Box<Expr>)> {
+ if let Expr::Path(path) = &*assign.left {
+ if let Some(ident) = path.path.get_ident() {
+ let const_name = ident.clone();
+ let const_value = assign.right.clone();
+ return Some((const_name, const_value));
+ }
+ }
+ None
+}
+
+fn process_constant(
+ prefix: &str,
+ const_name: Ident,
+ const_value: Box<Expr>,
+) -> Option<proc_macro2::TokenStream> {
+ if let Expr::Lit(ExprLit {
+ lit: Lit::Str(lit_str),
+ ..
+ }) = *const_value
+ {
+ let value_str = lit_str.value();
+ let value_span = lit_str.span();
+
+ if value_str.contains('{') && value_str.contains('}') {
+ let params = extract_placeholder_params(&value_str);
+
+ if !params.is_empty() {
+ // With parameters
+ Some(generate_function_with_params(
+ prefix,
+ &const_name,
+ &value_str,
+ &params,
+ value_span,
+ ))
+ } else {
+ // Without parameters
+ Some(generate_function_without_params(
+ prefix,
+ &const_name,
+ &value_str,
+ ))
+ }
+ } else {
+ // Without parameters
+ Some(generate_function_without_params(
+ prefix,
+ &const_name,
+ &value_str,
+ ))
+ }
+ } else {
+ None
+ }
+}
+
+fn extract_placeholder_params(value_str: &str) -> Vec<String> {
+ let mut params = Vec::new();
+ let chars = value_str.chars().collect::<Vec<_>>();
+ let mut i = 0;
+
+ while i < chars.len() {
+ if chars[i] == '{' {
+ let start = i;
+ i += 1;
+
+ while i < chars.len() && chars[i] != '}' {
+ i += 1;
+ }
+
+ if i < chars.len() && chars[i] == '}' {
+ let param: String = chars[start + 1..i].iter().collect();
+ if !param.is_empty() {
+ params.push(param);
+ }
+ }
+ }
+ i += 1;
+ }
+
+ params
+}
+
+fn generate_function_with_params(
+ prefix: &str,
+ const_name: &Ident,
+ value_str: &str,
+ params: &[String],
+ value_span: proc_macro2::Span,
+) -> proc_macro2::TokenStream {
+ let fn_name = format!("{}_{}", prefix, const_name.to_string().to_lowercase());
+ let fn_ident = Ident::new(&fn_name, const_name.span());
+
+ // Generate parameter list
+ let param_idents: Vec<Ident> = params.iter().map(|p| Ident::new(p, value_span)).collect();
+ let format_args = param_idents.iter().map(|ident| quote! { #ident });
+
+ // Generate format! code
+ let doc_format_vaule = params.join(", ");
+
+ let mut doc_format_template = value_str.to_string();
+ for param in params {
+ let placeholder = format!("{{{}}}", param);
+ doc_format_template = doc_format_template.replace(&placeholder, "{}");
+ }
+
+ let doc_format_code = format!(
+ "format!(\"{}\", {});",
+ doc_format_template, doc_format_vaule
+ );
+
+ // Replace {xxx} with {} format
+ let mut format_str = value_str.to_string();
+ for param in params {
+ format_str = format_str.replace(&format!("{{{}}}", param), "{}");
+ }
+
+ quote! {
+ #[doc = "```ignore"]
+ #[doc = #doc_format_code]
+ #[doc = "```"]
+ pub fn #fn_ident(#(#param_idents: &str),*) -> String {
+ format!(#format_str, #(#format_args),*)
+ }
+ }
+}
+
+fn generate_function_without_params(
+ prefix: &str,
+ const_name: &Ident,
+ value_str: &str,
+) -> proc_macro2::TokenStream {
+ let fn_name = format!("{}_{}", prefix, const_name.to_string().to_lowercase());
+ let fn_ident = Ident::new(&fn_name, const_name.span());
+
+ quote! {
+ #[doc = "`"]
+ #[doc = #value_str]
+ #[doc = "`"]
+ pub fn #fn_ident() -> &'static str {
+ #value_str
+ }
+ }
+}
diff --git a/systems/_constants/src/lib.rs b/systems/_constants/src/lib.rs
new file mode 100644
index 0000000..e73f81f
--- /dev/null
+++ b/systems/_constants/src/lib.rs
@@ -0,0 +1,147 @@
+#[macro_export]
+macro_rules! c {
+ ($name:ident = $value:expr) => {
+ const $name: &str = $value;
+ };
+}
+
+/// File and directory path constants for the server root
+#[allow(unused)]
+pub mod server {
+ /// File constants
+ #[macros::constants("server_file")]
+ pub mod files {
+ c! { CONFIG = "./config.toml" }
+ c! { JOIN_REQUEST_KEY = "./.temp/join_requests/{member_name}.pem" }
+ c! { KEY = "./keys/{member_name}.pem" }
+ c! { MEMBER_METADATA = "./meta/{member_name}.toml" }
+ }
+
+ /// Directory path constants
+ #[macros::constants("server_dir")]
+ pub mod dirs {
+ c! { KEYS = "./key/" }
+ c! { JOIN_REQUEST_KEYS = "./.temp/join_requests/" }
+ c! { MEMBER_METADATAS = "./meta/" }
+ c! { VAULTS = "./v/" }
+ }
+}
+
+/// File and directory path constants for the vault root
+#[allow(unused)]
+pub mod vault {
+ /// File path constants
+ #[macros::constants("vault_file")]
+ pub mod files {
+ // ### Configs ###
+ c! { CONFIG = "./vault.toml" }
+
+ // ### Sheets ###
+
+ // Reference sheet
+ c! { REFSHEET = "./ref/{ref_sheet_name}.sheet" }
+
+ // Editable instance of a reference sheet, maintained by one of the vault's Hosts, written back to the sheet file after editing
+ c! { REFSHEET_EDIT = "./ref/~{ref_sheet_name}.sheet" }
+
+ // Member sheet backup, only used to temporarily store a member's local workspace file structure, fully controlled by the corresponding member
+ c! { MEMBER_SHEET_BACKUP = "./_member/{member_name}/backups/{sheet_name}.sheet" }
+
+ // Share sent to a reference sheet, selectively merged into that reference sheet by a Host
+ c! { SHARE_TO_REF = "./req/{ref_sheet_name}/{share_id}.sheet" }
+
+ // Share sent to a specific member, fully controlled by that member, who can optionally merge it into any of their own sheets
+ c! { SHARE_TO_MEMBER = "./_member/{member_name}/shares/{share_id}.sheet" }
+
+ // ### Storages ###
+ c! { CHANGE_FILE = "./changes/CURRENT" }
+ c! { CHANGE_FILE_BACKUP = "./changes/backup.{index}" }
+
+ // Whether an index is locked; if the file exists, the member name written inside is the current holder
+ c! { INDEX_LOCK = "./index/{index_slice_1}/{index_slice_2}/{index_name}/LOCK" }
+
+ // Editable instance of an index's version sequence, maintained by the holder, written back to the ver file after editing
+ c! { INDEX_VER_EDIT = "./index/{index_slice_1}/{index_slice_2}/{index_name}/~ver" }
+
+ // Index version sequence
+ c! { INDEX_VER = "./index/{index_slice_1}/{index_slice_2}/{index_name}/ver" }
+
+ // Index
+ c! { INDEX = "./index/{index_slice_1}/{index_slice_2}/{index_name}/{version}.index" }
+
+ // Object, file chunk
+ c! { OBJ = "./obj/{obj_hash_slice_1}/{obj_hash_slice_2}/{obj_hash}" }
+ }
+
+ /// Directory path constants
+ #[macros::constants("vault_dir")]
+ pub mod dirs {
+ c! { REFSHEETS = "./ref/" }
+ c! { SHARES_TO_REF = "./req/{ref_sheet_name}/" }
+ c! { MEMBER = "./_member/{member_name}/" }
+ c! { MEMBER_SHEET_BACKUPS = "./_member/{member_name}/backups/" }
+ c! { MEMBER_SHARES = "./_member/{member_name}/shares/" }
+ c! { CHANGES = "./changes/" }
+ }
+}
+
+/// File and directory path constants for the workspace root
+#[allow(unused)]
+pub mod workspace {
+ /// File path constants
+ #[macros::constants("workspace_file")]
+ pub mod files {
+ // ### Config ###
+ c! { CONFIG = "./.jv/workspace.toml" }
+
+ // ### Sheets ###
+ // Records the latest state of local physical files, used to calculate deviations
+ c! { LOCAL_STATUS = "./.jv/sheets/{account}/{sheet}.local" }
+
+ // Personal sheet, represents the desired file structure, held only by the member, can be backed up to the vault
+ c! { SHEET = "./.jv/sheets/{account}/{sheet}.sheet" }
+
+ // Draft file, when switching to another sheet, fully records modified but untracked files
+ c! { DRAFTED_FILE = "./.jv/drafts/{account}_{sheet}/{mapping}" }
+
+ // Working file
+ c! { WORKING_FILE = "./{mapping}" }
+ }
+
+ /// Directory path constants
+ #[macros::constants("workspace_dir")]
+ pub mod dirs {
+ c! { WORKSPACE = "./.jv" }
+ c! { VAULT_MIRROR = "./.jv/UPSTREAM/" }
+ c! { LOCAL_SHEETS = "./.jv/sheets/{account}/" }
+ c! { DRAFT_AREA = "./.jv/drafts/{account}_{sheet}/" }
+ c! { WORKING_AREA = "./" }
+ }
+}
+
+/// File and directory path constants for the user root
+#[allow(unused)]
+pub mod user {
+ /// File path constants
+ #[macros::constants("user_file")]
+ pub mod files {
+ // Upstream public key, stored after initial login, used to verify the trustworthiness of that upstream
+ c! { UPSTREAM_PUB = "./upstreams/{upstream_addr}.pem" }
+
+ // Account private key, stored only locally, used for login authentication
+ c! { PRIVATE_KEY = "./private/{account}.pem" }
+
+ // Account public key, automatically generated from the private key and stored,
+ // will be placed into the server's "join request list" upon initial login (if server.toml permits this action)
+ // The server administrator can optionally approve this request
+ c! { PUBLIC_KEY = "./public/{account}.pem" }
+ }
+
+ /// Directory path constants
+ #[macros::constants("user_dir")]
+ pub mod dirs {
+ c! { UPSTREAM_PUBS = "./upstreams/" }
+ c! { PRIVATE_KEYS = "./private/" }
+ c! { PUBLIC_KEYS = "./public/" }
+ }
+}