Skip to content

Commit 822d35e

Browse files
committed
Define a type for blob IDs.
1 parent ae0d944 commit 822d35e

6 files changed

Lines changed: 132 additions & 41 deletions

File tree

lib/asimov-kb/src/blob_id.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// This is free and unencumbered software released into the public domain.
2+
3+
use crate::{Id, IdClass, IdError};
4+
use core::{str::FromStr, ops::RangeInclusive};
5+
use derive_more::Display;
6+
7+
#[derive(Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
8+
pub struct BlobId(pub(crate) Id<32>);
9+
10+
impl BlobId {
11+
pub const ID_LEN_MIN: usize = 1 + 16; // TODO
12+
pub const ID_LEN_MAX: usize = 1 + 22; // TODO
13+
pub const ID_LEN: RangeInclusive<usize> = Self::ID_LEN_MIN..=Self::ID_LEN_MAX;
14+
15+
pub fn as_id(&self) -> &Id<32> {
16+
&self.0
17+
}
18+
19+
pub fn into_id(self) -> Id<32> {
20+
self.0
21+
}
22+
}
23+
24+
impl From<[u8; 32]> for BlobId {
25+
fn from(bytes: [u8; 32]) -> Self {
26+
Self(Id::from((IdClass::Blob, bytes)))
27+
}
28+
}
29+
30+
impl From<&Vec<u8>> for BlobId {
31+
fn from(bytes: &Vec<u8>) -> Self {
32+
Self(Id::from((IdClass::Blob, bytes)))
33+
}
34+
}
35+
36+
impl FromStr for BlobId {
37+
type Err = IdError;
38+
39+
fn from_str(input: &str) -> Result<Self, Self::Err> {
40+
let id = Id::from_str(input)?;
41+
if id.class() != IdClass::Blob {
42+
return Err(IdError::UnknownClass);
43+
}
44+
Ok(Self(id))
45+
}
46+
}
47+
48+
#[cfg(feature = "eloquent")]
49+
impl eloquent::ToSql for BlobId {
50+
fn to_sql(&self) -> Result<String, eloquent::error::EloquentError> {
51+
self.as_id().to_sql()
52+
}
53+
}
54+
55+
#[cfg(feature = "libsql")]
56+
impl libsql::params::IntoValue for BlobId {
57+
fn into_value(self) -> libsql::Result<libsql::Value> {
58+
self.into_id().into_value()
59+
}
60+
}
61+
62+
#[cfg(feature = "rocket")]
63+
impl<'r> rocket::request::FromParam<'r> for BlobId {
64+
type Error = IdError;
65+
66+
fn from_param(input: &'r str) -> Result<Self, Self::Error> {
67+
Self::from_str(input)
68+
}
69+
}
70+
71+
#[cfg(feature = "turso")]
72+
impl turso::IntoValue for BlobId {
73+
fn into_value(self) -> turso::Result<turso::Value> {
74+
self.into_id().into_value()
75+
}
76+
}

lib/asimov-kb/src/event_id.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
// This is free and unencumbered software released into the public domain.
22

33
use crate::{Id, IdClass, IdError};
4-
use core::str::FromStr;
4+
use core::{str::FromStr, ops::RangeInclusive};
55
use derive_more::Display;
66

77
#[derive(Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
8-
pub struct EventId(pub(crate) Id);
8+
pub struct EventId(pub(crate) Id<16>);
99

1010
impl EventId {
11+
pub const ID_LEN_MIN: usize = 1 + 16;
12+
pub const ID_LEN_MAX: usize = 1 + 22;
13+
pub const ID_LEN: RangeInclusive<usize> = Self::ID_LEN_MIN..=Self::ID_LEN_MAX;
14+
1115
pub fn new() -> Self {
12-
Self(Id::new(IdClass::Event))
16+
Self(Id::new_uuid(IdClass::Event))
1317
}
1418

1519
pub fn as_id(&self) -> &Id {

lib/asimov-kb/src/id.rs

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
11
// This is free and unencumbered software released into the public domain.
22

33
use crate::{IdClass, IdError};
4-
use core::{ops::RangeInclusive, str::FromStr};
4+
use core::{str::FromStr};
55
use derive_more::Display;
66

7-
pub const ID_LENGTH_MIN: usize = 1 + 16;
8-
pub const ID_LENGTH_MAX: usize = 1 + 22;
9-
pub const ID_LENGTH: RangeInclusive<usize> = ID_LENGTH_MIN..=ID_LENGTH_MAX;
10-
117
#[derive(Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
128
#[display("{class}{}", bs58::encode(bytes).into_string())]
13-
pub struct Id {
9+
pub struct Id<const N: usize = 16> {
1410
pub(crate) class: IdClass,
15-
pub(crate) bytes: [u8; 16],
11+
pub(crate) bytes: [u8; N],
1612
}
1713

18-
impl Id {
14+
impl<const N: usize> Id<N> {
1915
pub fn zero(class: IdClass) -> Self {
2016
Self {
2117
class,
22-
bytes: [0u8; 16],
23-
}
24-
}
25-
26-
pub fn new(class: IdClass) -> Self {
27-
Self {
28-
class,
29-
bytes: uuid::Uuid::now_v7().into_bytes(),
18+
bytes: [0u8; N],
3019
}
3120
}
3221

@@ -38,18 +27,10 @@ impl Id {
3827
self.bytes.as_slice()
3928
}
4029

41-
pub fn as_uuid(&self) -> uuid::Uuid {
42-
uuid::Uuid::from_bytes(self.bytes)
43-
}
44-
45-
pub fn into_bytes(self) -> [u8; 16] {
30+
pub fn into_bytes(self) -> [u8; N] {
4631
self.bytes
4732
}
4833

49-
pub fn into_uuid(self) -> uuid::Uuid {
50-
uuid::Uuid::from_bytes(self.bytes)
51-
}
52-
5334
#[cfg(feature = "std")]
5435
pub fn yaml_path(&self) -> std::path::PathBuf {
5536
self.dir_path().join(self.class().yaml_path())
@@ -73,21 +54,39 @@ impl Id {
7354
}
7455
}
7556

76-
impl From<(IdClass, [u8; 16])> for Id {
77-
fn from((class, bytes): (IdClass, [u8; 16])) -> Self {
57+
impl Id<16> {
58+
pub fn new_uuid(class: IdClass) -> Self {
59+
Self {
60+
class,
61+
bytes: uuid::Uuid::now_v7().into_bytes(),
62+
}
63+
}
64+
65+
pub fn as_uuid(&self) -> uuid::Uuid {
66+
uuid::Uuid::from_bytes(self.bytes)
67+
}
68+
69+
pub fn into_uuid(self) -> uuid::Uuid {
70+
uuid::Uuid::from_bytes(self.bytes)
71+
}
72+
}
73+
74+
impl<const N: usize> From<(IdClass, [u8; N])> for Id<N> {
75+
fn from((class, bytes): (IdClass, [u8; N])) -> Self {
7876
Self { class, bytes }
7977
}
8078
}
8179

82-
impl From<(IdClass, &Vec<u8>)> for Id {
80+
impl<const N: usize> From<(IdClass, &Vec<u8>)> for Id<N> {
8381
fn from((class, bytes_vec): (IdClass, &Vec<u8>)) -> Self {
84-
let mut bytes = [0u8; 16];
85-
bytes[0..].copy_from_slice(&bytes_vec[0..16]);
82+
let mut bytes = [0u8; N];
83+
let len = N.min(bytes_vec.len());
84+
bytes[..len].copy_from_slice(&bytes_vec[..len]);
8685
Self { class, bytes }
8786
}
8887
}
8988

90-
impl FromStr for Id {
89+
impl<const N: usize> FromStr for Id<N> {
9190
type Err = IdError;
9291

9392
fn from_str(input: &str) -> Result<Self, Self::Err> {
@@ -112,22 +111,22 @@ impl FromStr for Id {
112111
}
113112

114113
#[cfg(feature = "eloquent")]
115-
impl eloquent::ToSql for Id {
114+
impl<const N: usize> eloquent::ToSql for Id<N> {
116115
fn to_sql(&self) -> Result<String, eloquent::error::EloquentError> {
117116
let hex: String = self.bytes.iter().map(|b| format!("{b:02X}")).collect();
118117
Ok(format!("X'{hex}'"))
119118
}
120119
}
121120

122121
#[cfg(feature = "libsql")]
123-
impl libsql::params::IntoValue for Id {
122+
impl<const N: usize> libsql::params::IntoValue for Id<N> {
124123
fn into_value(self) -> libsql::Result<libsql::Value> {
125124
Ok(libsql::Value::Blob(self.bytes.to_vec()))
126125
}
127126
}
128127

129128
#[cfg(feature = "rocket")]
130-
impl<'r> rocket::request::FromParam<'r> for Id {
129+
impl<'r, const N: usize> rocket::request::FromParam<'r> for Id<N> {
131130
type Error = IdError;
132131

133132
fn from_param(input: &'r str) -> Result<Self, Self::Error> {
@@ -136,7 +135,7 @@ impl<'r> rocket::request::FromParam<'r> for Id {
136135
}
137136

138137
#[cfg(feature = "turso")]
139-
impl turso::IntoValue for Id {
138+
impl<const N: usize> turso::IntoValue for Id<N> {
140139
fn into_value(self) -> turso::Result<turso::Value> {
141140
Ok(turso::Value::Blob(self.bytes.to_vec()))
142141
}

lib/asimov-kb/src/id_class.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use derive_more::Display;
77
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
88
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
99
pub enum IdClass {
10+
#[display("B")]
11+
Blob,
1012
#[display("E")]
1113
Event,
1214
#[display("P")]
@@ -17,6 +19,7 @@ impl IdClass {
1719
#[cfg(feature = "std")]
1820
pub fn yaml_path(&self) -> std::path::PathBuf {
1921
match self {
22+
Self::Blob => "blob.yaml",
2023
Self::Event => "event.yaml",
2124
Self::Person => "person.yaml",
2225
}
@@ -26,6 +29,7 @@ impl IdClass {
2629
#[cfg(feature = "std")]
2730
pub fn dir_path(&self) -> std::path::PathBuf {
2831
match self {
32+
Self::Blob => "blobs",
2933
Self::Event => "events",
3034
Self::Person => "people",
3135
}
@@ -38,6 +42,7 @@ impl FromStr for IdClass {
3842

3943
fn from_str(input: &str) -> Result<Self, Self::Err> {
4044
Ok(match input.chars().next().unwrap_or_default() {
45+
'B' => Self::Blob,
4146
'E' => Self::Event,
4247
'P' => Self::Person,
4348
_ => return Err(IdError::UnknownClass),

lib/asimov-kb/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// This is free and unencumbered software released into the public domain.
22

3+
mod blob_id;
4+
pub use blob_id::*;
5+
36
mod event_id;
47
pub use event_id::*;
58

lib/asimov-kb/src/person_id.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
// This is free and unencumbered software released into the public domain.
22

33
use crate::{Id, IdClass, IdError};
4-
use core::str::FromStr;
4+
use core::{str::FromStr, ops::RangeInclusive};
55
use derive_more::Display;
66

77
#[derive(Clone, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
8-
pub struct PersonId(pub(crate) Id);
8+
pub struct PersonId(pub(crate) Id<16>);
99

1010
impl PersonId {
11+
pub const ID_LEN_MIN: usize = 1 + 16;
12+
pub const ID_LEN_MAX: usize = 1 + 22;
13+
pub const ID_LEN: RangeInclusive<usize> = Self::ID_LEN_MIN..=Self::ID_LEN_MAX;
14+
1115
pub fn new() -> Self {
12-
Self(Id::new(IdClass::Person))
16+
Self(Id::new_uuid(IdClass::Person))
1317
}
1418

1519
pub fn as_id(&self) -> &Id {

0 commit comments

Comments
 (0)