Bootstrap UCXL Core: Implementation of UCXLAddress and TemporalAxis in Rust

This commit is contained in:
anthonyrawlins
2026-03-03 14:18:16 +11:00
parent 68a489b64d
commit 68df0bddc7
38 changed files with 238 additions and 0 deletions

7
UCXL/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ucxl"
version = "0.1.0"

6
UCXL/Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[package]
name = "ucxl"
version = "0.1.0"
edition = "2024"
[dependencies]

192
UCXL/src/lib.rs Normal file
View File

@@ -0,0 +1,192 @@
// UCXL Core Data Structures
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
/// Represents the temporal axis in a UCXL address.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TemporalAxis {
/// Present ("#")
Present,
/// Past ("~~")
Past,
/// Future ("^^")
Future,
}
impl FromStr for TemporalAxis {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"#" => Ok(TemporalAxis::Present),
"~~" => Ok(TemporalAxis::Past),
"^^" => Ok(TemporalAxis::Future),
_ => Err(format!("Invalid temporal axis: {}", s)),
}
}
}
impl fmt::Display for TemporalAxis {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
TemporalAxis::Present => "#",
TemporalAxis::Past => "~~",
TemporalAxis::Future => "^^",
};
write!(f, "{}", s)
}
}
/// Represents a parsed UCXL address.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct UCXLAddress {
pub agent: String,
pub role: Option<String>,
pub project: String,
pub task: String,
pub temporal: TemporalAxis,
pub path: String,
}
impl FromStr for UCXLAddress {
type Err = String;
fn from_str(address: &str) -> Result<Self, Self::Err> {
// Ensure the scheme is correct
let scheme_split: Vec<&str> = address.splitn(2, "://").collect();
if scheme_split.len() != 2 || scheme_split[0] != "ucxl" {
return Err("Address must start with 'ucxl://'".into());
}
let remainder = scheme_split[1];
// Split at the first '@' to separate agent/role from project/task
let parts: Vec<&str> = remainder.splitn(2, '@').collect();
if parts.len() != 2 {
return Err("Missing '@' separating agent and project".into());
}
// Agent and optional role
let agent_part = parts[0];
let mut agent_iter = agent_part.splitn(2, ':');
let agent = agent_iter.next().unwrap().to_string();
let role = agent_iter.next().map(|s| s.to_string());
// Project and task
let project_task_part = parts[1];
// Find the first '/' that starts the temporal segment and path
let slash_idx = project_task_part
.find('/')
.ok_or("Missing '/' before temporal segment and path")?;
let (proj_task, after_slash) = project_task_part.split_at(slash_idx);
let mut proj_task_iter = proj_task.splitn(2, ':');
let project = proj_task_iter.next().ok_or("Missing project")?.to_string();
let task = proj_task_iter.next().ok_or("Missing task")?.to_string();
// after_slash starts with '/', remove it
let after = &after_slash[1..];
// Temporal segment is up to the next '/' if present
let temporal_end = after
.find('/')
.ok_or("Missing '/' after temporal segment")?;
let temporal_str = &after[..temporal_end];
let temporal = TemporalAxis::from_str(temporal_str)?;
// The rest is the resource path
let path = after[temporal_end + 1..].to_string();
Ok(UCXLAddress {
agent,
role,
project,
task,
temporal,
path,
})
}
}
impl fmt::Display for UCXLAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let role_part = if let Some(r) = &self.role {
format!(":{}", r)
} else {
"".to_string()
};
write!(
f,
"ucxl://{}{}@{}:{}/{}{}",
self.agent,
role_part,
self.project,
self.task,
self.temporal,
if self.path.is_empty() {
"".to_string()
} else {
format!("/{}", self.path)
}
)
}
}
/// Simple inmemory metadata store mapping a file path to a metadata string.
pub trait MetadataStore {
fn get(&self, path: &str) -> Option<&String>;
fn set(&mut self, path: &str, metadata: String);
fn remove(&mut self, path: &str) -> Option<String> {
None
}
}
/// A concrete inmemory implementation using a HashMap.
pub struct InMemoryMetadataStore {
map: HashMap<String, String>,
}
impl InMemoryMetadataStore {
pub fn new() -> Self {
InMemoryMetadataStore {
map: HashMap::new(),
}
}
}
impl MetadataStore for InMemoryMetadataStore {
fn get(&self, path: &str) -> Option<&String> {
self.map.get(path)
}
fn set(&mut self, path: &str, metadata: String) {
self.map.insert(path.to_string(), metadata);
}
fn remove(&mut self, path: &str) -> Option<String> {
self.map.remove(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temporal_from_str() {
assert_eq!(TemporalAxis::from_str("#").unwrap(), TemporalAxis::Present);
assert_eq!(TemporalAxis::from_str("~~").unwrap(), TemporalAxis::Past);
assert_eq!(TemporalAxis::from_str("^^").unwrap(), TemporalAxis::Future);
}
#[test]
fn test_ucxl_address_parsing() {
let addr_str = "ucxl://alice:admin@myproj:task1/#/docs/readme.md";
let addr = UCXLAddress::from_str(addr_str).unwrap();
assert_eq!(addr.agent, "alice");
assert_eq!(addr.role, Some("admin".to_string()));
assert_eq!(addr.project, "myproj");
assert_eq!(addr.task, "task1");
assert_eq!(addr.temporal, TemporalAxis::Present);
assert_eq!(addr.path, "docs/readme.md");
assert_eq!(addr.to_string(), addr_str);
}
#[test]
fn test_metadata_store() {
let mut store = InMemoryMetadataStore::new();
store.set("/foo.txt", "meta".into());
assert_eq!(store.get("/foo.txt"), Some(&"meta".to_string()));
store.remove("/foo.txt");
assert!(store.get("/foo.txt").is_none());
}
}

View File

@@ -0,0 +1 @@
{"rustc_fingerprint":15256376128064635560,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/tony/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.87.0 (17067e9ac 2025-05-09)\nbinary: rustc\ncommit-hash: 17067e9ac6d7ecb70e50f92c1944e545188d2359\ncommit-date: 2025-05-09\nhost: x86_64-unknown-linux-gnu\nrelease: 1.87.0\nLLVM version: 20.1.1\n","stderr":""}},"successes":{}}

3
UCXL/target/CACHEDIR.TAG Normal file
View File

@@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/

View File

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
bb01722b2cbeb242

View File

@@ -0,0 +1 @@
{"rustc":15597765236515928571,"features":"[]","declared_features":"[]","target":6072219573135885997,"profile":17672942494452627365,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/UCXL-5635639e87e07c73/dep-lib-UCXL","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@@ -0,0 +1,3 @@
{"$message_type":"diagnostic","message":"unused variable: `path`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":4183,"byte_end":4187,"line_start":130,"line_end":130,"column_start":26,"column_end":30,"is_primary":true,"text":[{"text":" fn remove(&mut self, path: &str) -> Option<String> {","highlight_start":26,"highlight_end":30}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_variables)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":4183,"byte_end":4187,"line_start":130,"line_end":130,"column_start":26,"column_end":30,"is_primary":true,"text":[{"text":" fn remove(&mut self, path: &str) -> Option<String> {","highlight_start":26,"highlight_end":30}],"label":null,"suggested_replacement":"_path","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `path`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:130:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m130\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m fn remove(&mut self, path: &str) -> Option<String> {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_path`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\n\n"}
{"$message_type":"diagnostic","message":"crate `UCXL` should have a snake case name","code":{"code":"non_snake_case","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":0,"byte_end":0,"line_start":1,"line_end":1,"column_start":1,"column_end":1,"is_primary":true,"text":[],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"convert the identifier to snake case: `ucxl`","code":null,"level":"help","spans":[],"children":[],"rendered":null},{"message":"`#[warn(non_snake_case)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: crate `UCXL` should have a snake case name\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mhelp\u001b[0m\u001b[0m: convert the identifier to snake case: `ucxl`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(non_snake_case)]` on by default\u001b[0m\n\n"}
{"$message_type":"diagnostic","message":"2 warnings emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 2 warnings emitted\u001b[0m\n\n"}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
6a2348a550665fb3

View File

@@ -0,0 +1 @@
{"rustc":15597765236515928571,"features":"[]","declared_features":"[]","target":6072219573135885997,"profile":3316208278650011218,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/UCXL-56588304154f99b1/dep-test-lib-UCXL","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
d1d9a9e987f425e2

View File

@@ -0,0 +1 @@
{"rustc":15597765236515928571,"features":"[]","declared_features":"[]","target":11178672385271275666,"profile":17672942494452627365,"path":10763286916239946207,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/ucxl-20fd26e825d12f34/dep-lib-ucxl","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@@ -0,0 +1,2 @@
{"$message_type":"diagnostic","message":"unused variable: `path`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":4183,"byte_end":4187,"line_start":130,"line_end":130,"column_start":26,"column_end":30,"is_primary":true,"text":[{"text":" fn remove(&mut self, path: &str) -> Option<String> {","highlight_start":26,"highlight_end":30}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_variables)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":4183,"byte_end":4187,"line_start":130,"line_end":130,"column_start":26,"column_end":30,"is_primary":true,"text":[{"text":" fn remove(&mut self, path: &str) -> Option<String> {","highlight_start":26,"highlight_end":30}],"label":null,"suggested_replacement":"_path","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `path`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:130:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m130\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m fn remove(&mut self, path: &str) -> Option<String> {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_path`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\n\n"}
{"$message_type":"diagnostic","message":"1 warning emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 1 warning emitted\u001b[0m\n\n"}

View File

@@ -0,0 +1,5 @@
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/libUCXL-5635639e87e07c73.rmeta: src/lib.rs
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/UCXL-5635639e87e07c73.d: src/lib.rs
src/lib.rs:

View File

@@ -0,0 +1,5 @@
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/libUCXL-56588304154f99b1.rmeta: src/lib.rs
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/UCXL-56588304154f99b1.d: src/lib.rs
src/lib.rs:

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/libucxl-20fd26e825d12f34.rmeta: src/lib.rs
/home/tony/rust/projects/reset/CHORUS/UCXL/target/debug/deps/ucxl-20fd26e825d12f34.d: src/lib.rs
src/lib.rs: