Skip to content

Commit b937f0a

Browse files
committed
quickly sketch up a client API proposal
1 parent b62004c commit b937f0a

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ edition = "2024"
55

66
[dependencies]
77
proto-codegen = { path = "../proto-codegen" }
8+
uuid = { version = "1.17.0", features = ["v4"] }

client/src/lib.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,101 @@
66
//!
77
//! The Client SDK is built primarily as a Rust library,
88
//! and can be exposed to Python through PyO3 as well.
9+
10+
use std::collections::HashMap;
11+
use std::sync::{Arc, LazyLock, Mutex};
12+
13+
use uuid::Uuid;
14+
15+
#[derive(Clone, PartialEq, Eq, Hash)]
16+
pub struct StorageScope {
17+
org_id: u64,
18+
}
19+
impl StorageScope {
20+
pub fn for_organization(org_id: u64) -> Self {
21+
Self { org_id }
22+
}
23+
}
24+
25+
pub struct StorageService {
26+
usecase: &'static str,
27+
}
28+
impl StorageService {
29+
pub const fn for_usecase(usecase: &'static str) -> Self {
30+
Self { usecase }
31+
}
32+
pub fn with_scope(&self, scope: StorageScope) -> StorageClient {
33+
StorageClient {
34+
usecase: self.usecase,
35+
scope,
36+
}
37+
}
38+
}
39+
40+
#[derive(Clone, PartialEq, Eq, Hash)]
41+
pub struct StorageId {
42+
id: String,
43+
}
44+
impl StorageId {
45+
pub fn from_str(id: &str) -> Self {
46+
Self { id: id.into() }
47+
}
48+
}
49+
50+
static MOCK_STORAGE: LazyLock<Mutex<HashMap<(&'static str, StorageScope, StorageId), Arc<[u8]>>>> =
51+
LazyLock::new(Default::default);
52+
53+
pub struct StorageClient {
54+
usecase: &'static str,
55+
scope: StorageScope,
56+
}
57+
// TODO: all this should be async
58+
impl StorageClient {
59+
pub fn put_blob(&self, id: Option<StorageId>, contents: &[u8]) -> StorageId {
60+
let id = id.unwrap_or_else(|| StorageId {
61+
id: Uuid::new_v4().to_string(),
62+
});
63+
let key = (self.usecase, self.scope.clone(), id.clone());
64+
let contents = contents.into();
65+
66+
let mut storage = MOCK_STORAGE.lock().unwrap();
67+
storage.insert(key, contents);
68+
id
69+
}
70+
71+
pub fn get_blob(&self, id: StorageId) -> Option<Arc<[u8]>> {
72+
let key = (self.usecase, self.scope.clone(), id.clone());
73+
74+
let storage = MOCK_STORAGE.lock().unwrap();
75+
storage.get(&key).cloned()
76+
}
77+
78+
pub fn delete_blob(&self, id: StorageId) {
79+
let key = (self.usecase, self.scope.clone(), id.clone());
80+
81+
let mut storage = MOCK_STORAGE.lock().unwrap();
82+
storage.remove(&key);
83+
}
84+
}
85+
86+
#[cfg(test)]
87+
mod tests {
88+
use super::*;
89+
90+
#[test]
91+
fn api_is_usable() {
92+
static ATTACHMENTS: StorageService = StorageService::for_usecase("attachments");
93+
94+
let scope = StorageScope::for_organization(12345);
95+
let client = ATTACHMENTS.with_scope(scope);
96+
97+
let blob: &[u8] = b"oh hai!";
98+
let allocated_id = client.put_blob(None, blob);
99+
100+
let stored_blob = client.get_blob(allocated_id.clone());
101+
assert_eq!(stored_blob.unwrap().as_ref(), blob);
102+
103+
client.delete_blob(allocated_id.clone());
104+
assert!(client.get_blob(allocated_id).is_none());
105+
}
106+
}

0 commit comments

Comments
 (0)