mirror of https://github.com/pcleavelin/a_ci.git
use facet to do pointer war crimes
honestly should probably switch gears to do a separate build pass to generate the `field()` and `set_field()` functions so there isn't so much pointer magic happening at runtime.db-work
parent
5dd8d1f5cd
commit
f1e2299347
|
|
@ -10,6 +10,7 @@ dependencies = [
|
|||
"axum",
|
||||
"bincode",
|
||||
"dotenv",
|
||||
"facet",
|
||||
"field_types",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
|
@ -139,6 +140,12 @@ version = "3.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
|
|
@ -248,6 +255,69 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49184c644e74dd0e899d21144285e92598d682845c8ef0d13da05f1c67c2bcc5"
|
||||
dependencies = [
|
||||
"facet-core",
|
||||
"facet-macros",
|
||||
"facet-reflect",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet-core"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e09a4180d0a78a9e444cb57ea57b2c1d65ab140e53e35535b76713454a8e158e"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"impls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet-macros"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bfcaf327d2f5d94bc208e81d625acfdab871f47f9915a34929335762d4d312"
|
||||
dependencies = [
|
||||
"facet-core",
|
||||
"facet-macros-emit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet-macros-emit"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "084b862aa27ccade8c0d249b57960975611d1f9dfcd3c8c42a25aaa7d14cd1c2"
|
||||
dependencies = [
|
||||
"facet-macros-parse",
|
||||
"quote 1.0.41",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet-macros-parse"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85c198c8252d559dff4ef2846662b8ac63496ec4cbe734651a5ffedeefaa8652"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.103",
|
||||
"quote 1.0.41",
|
||||
"unsynn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "facet-reflect"
|
||||
version = "0.31.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91debebf849ca4acf73f14c74dc5642f8b8e1aa823c86cabfa5d0b4825c66323"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"facet-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
|
|
@ -340,6 +410,15 @@ dependencies = [
|
|||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
|
|
@ -599,6 +678,12 @@ dependencies = [
|
|||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impls"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
|
|
@ -684,6 +769,12 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutants"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
|
|
@ -1029,6 +1120,12 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shadow_counted"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65da48d447333cebe1aadbdd3662f3ba56e76e67f53bc46f3dd5f67c74629d6b"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -1082,6 +1179,12 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
|
|
@ -1331,6 +1434,18 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
||||
[[package]]
|
||||
name = "unsynn"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7940603a9e25cf11211cc43b81f4fcad2b8ab4df291ca855f32c40e1ac22d5bc"
|
||||
dependencies = [
|
||||
"fxhash",
|
||||
"mutants",
|
||||
"proc-macro2 1.0.103",
|
||||
"shadow_counted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unty"
|
||||
version = "0.0.4"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ anyhow = "1.0.100"
|
|||
axum = { version = "0.6.9", features = ["headers"] }
|
||||
bincode = "2.0.1"
|
||||
dotenv = "0.15.0"
|
||||
facet = { version = "0.31.3", features = ["reflect"] }
|
||||
field_types = "1.1.0"
|
||||
reqwest = "0.11.14"
|
||||
serde = "1.0.152"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use facet::Facet;
|
||||
|
||||
use crate::outbound::db_custom::write_set::{Diffable, Storable, Write, WriteOperation, WriteSet};
|
||||
|
||||
#[derive(
|
||||
|
|
@ -20,7 +22,7 @@ impl From<u64> for TransactionId {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, facet::Facet)]
|
||||
pub struct Id<T> {
|
||||
id: u64,
|
||||
_type: PhantomData<T>,
|
||||
|
|
@ -392,7 +394,7 @@ impl<'a> PendingTransaction<'a> {
|
|||
self.accessor.get(id, self.timestamp)
|
||||
}
|
||||
|
||||
pub fn get_field<T: Storable + Clone + 'static>(
|
||||
pub fn get_field<T: Storable + for<'facet> Facet<'facet> + Clone + 'static>(
|
||||
&mut self,
|
||||
id: Id<T>,
|
||||
field: &'static str,
|
||||
|
|
@ -464,6 +466,16 @@ pub enum Value {
|
|||
}
|
||||
|
||||
impl Value {
|
||||
fn size(&self) -> Option<usize> {
|
||||
match self {
|
||||
Value::String(_) | Value::Array(_) => None,
|
||||
|
||||
Value::Int(_) => Some(size_of::<i64>()),
|
||||
Value::Float(_) => Some(size_of::<f64>()),
|
||||
Value::Bool(_) => Some(size_of::<bool>()),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_string(&self) -> Option<&String> {
|
||||
match self {
|
||||
Value::String(s) => Some(s),
|
||||
|
|
@ -501,42 +513,30 @@ impl Value {
|
|||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{db_type, outbound::db_custom::write_set::FieldDiff};
|
||||
use bincode::{Decode, Encode};
|
||||
use facet::Facet;
|
||||
|
||||
use super::*;
|
||||
|
||||
db_type! {
|
||||
#[derive(Debug, Clone, Encode, Decode, Facet)]
|
||||
struct MyTestData {
|
||||
name: Name,
|
||||
age: i64,
|
||||
contacts: (HashSet<Id<Self>>),
|
||||
ff: String,
|
||||
//contacts: HashSet<Id<Self>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Encode, Decode, Facet)]
|
||||
struct Name {
|
||||
nest: SuperNested,
|
||||
first: String,
|
||||
last: String,
|
||||
nest: SuperNested,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Encode, Decode, Facet)]
|
||||
struct SuperNested {
|
||||
i_am_a_really_nested_field: i64,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(bincode::Encode)]
|
||||
struct A<T> {
|
||||
name: String,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T> A<T> {
|
||||
fn with(name: impl ToString, data: T) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
|
@ -552,6 +552,7 @@ mod tests {
|
|||
let mut accessor = DatabaseAccessor::new(db);
|
||||
|
||||
let data = MyTestData {
|
||||
ff: "FIRST".to_string(),
|
||||
name: Name {
|
||||
first: "John".to_string(),
|
||||
last: "Doe".to_string(),
|
||||
|
|
@ -559,8 +560,8 @@ mod tests {
|
|||
i_am_a_really_nested_field: 67,
|
||||
},
|
||||
},
|
||||
age: 42,
|
||||
contacts: vec![Id::new(1)].into_iter().collect(),
|
||||
age: 1337,
|
||||
//contacts: vec![Id::new(1)].into_iter().collect(),
|
||||
};
|
||||
|
||||
println!("initial state: {:#?}", accessor.db.lock().unwrap());
|
||||
|
|
@ -570,6 +571,7 @@ mod tests {
|
|||
t.insert(
|
||||
Id::new(2),
|
||||
MyTestData {
|
||||
ff: "FIRST".to_string(),
|
||||
name: Name {
|
||||
first: "Oldy".to_string(),
|
||||
last: "McOlderton".to_string(),
|
||||
|
|
@ -577,8 +579,8 @@ mod tests {
|
|||
i_am_a_really_nested_field: 32,
|
||||
},
|
||||
},
|
||||
age: 69,
|
||||
contacts: vec![Id::new(1)].into_iter().collect(),
|
||||
age: 3000,
|
||||
//contacts: vec![Id::new(1)].into_iter().collect(),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -586,16 +588,16 @@ mod tests {
|
|||
let cloned_accessor = accessor.clone();
|
||||
let handle = std::thread::spawn(move || {
|
||||
cloned_accessor.transact(|t| {
|
||||
let Some(Value::String(last_name_of_oldy)) =
|
||||
t.get_field(Id::<MyTestData>::new(2), "name.last")
|
||||
else {
|
||||
panic!("expected a string")
|
||||
};
|
||||
|
||||
//let Some(Value::String(last_name_of_oldy)) =
|
||||
// t.get_field(Id::<MyTestData>::new(2), "name.last")
|
||||
//else {
|
||||
// panic!("expected a string")
|
||||
//};
|
||||
//
|
||||
t.modify(Id::<MyTestData>::new(1), |mut data| {
|
||||
data.age = 9;
|
||||
data.name.nest.i_am_a_really_nested_field = 9;
|
||||
data.name.last = format!("Gearbox {}", last_name_of_oldy);
|
||||
data.name.nest.i_am_a_really_nested_field = 99999;
|
||||
//data.name.last = format!("Gearbox {}", last_name_of_oldy);
|
||||
data
|
||||
});
|
||||
});
|
||||
|
|
@ -604,7 +606,9 @@ mod tests {
|
|||
let id = Id::<MyTestData>::new(1);
|
||||
accessor.transact(|t| {
|
||||
t.modify(id.clone(), |mut data| {
|
||||
data.age = 9;
|
||||
data.ff = "hello".to_string();
|
||||
data.age = 8;
|
||||
data.name.first = "JingleJeimer".to_string();
|
||||
data.name.last = "Not McOlderton Gearbox".to_string();
|
||||
|
||||
data
|
||||
|
|
@ -614,11 +618,13 @@ mod tests {
|
|||
let oldys_contacts = accessor.transact(|t| {
|
||||
let oldy = t.get(Id::<MyTestData>::new(2)).unwrap();
|
||||
|
||||
oldy.contacts
|
||||
.into_iter()
|
||||
.filter_map(|id| t.get(id))
|
||||
.map(|data| format!("{} {}", data.name.first, data.name.last))
|
||||
.collect::<Vec<_>>()
|
||||
vec![0]
|
||||
|
||||
//oldy.contacts
|
||||
// .into_iter()
|
||||
// .filter_map(|id| t.get(id))
|
||||
// .map(|data| format!("{} {}", data.name.first, data.name.last))
|
||||
// .collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
handle.join().unwrap();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,32 @@
|
|||
use bincode::{Decode, Encode};
|
||||
use facet::{Facet, Field, PtrConst, Shape};
|
||||
|
||||
use crate::outbound::db_custom::{TypelessId, Value};
|
||||
|
||||
#[derive(bincode::Encode)]
|
||||
struct TransactionEntity<T> {
|
||||
name: String,
|
||||
data: T,
|
||||
}
|
||||
impl<T> TransactionEntity<T> {
|
||||
fn with(name: impl ToString, data: T) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Storable: Diffable + std::fmt::Debug + std::any::Any {
|
||||
fn as_full_write_op(&self) -> WriteOperation
|
||||
where
|
||||
Self: Sized;
|
||||
fn as_partial_write_op(&self, other: &Self) -> WriteOperation
|
||||
where
|
||||
Self: Sized;
|
||||
Self: Sized,
|
||||
{
|
||||
WriteOperation::Partial(self.diff(other).into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
fn apply_partial_write(&mut self, op: &PartialWrite);
|
||||
|
||||
|
|
@ -27,6 +45,266 @@ pub trait Diffable {
|
|||
Self: Sized;
|
||||
}
|
||||
|
||||
struct ShapeVal<'a, 's>(&'a Shape, PtrConst<'a>, &'s str);
|
||||
|
||||
impl<'a, 's> Diffable for ShapeVal<'a, 's> {
|
||||
fn diff(&self, other: &Self) -> Vec<FieldDiff>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut modifications = vec![];
|
||||
|
||||
let facet::Type::User(user) = self.0.ty else {
|
||||
panic!("invalid type");
|
||||
};
|
||||
|
||||
match user {
|
||||
facet::UserType::Struct(struct_type) => {
|
||||
//println!("{:#?}", struct_type.fields);
|
||||
|
||||
for field in struct_type.fields {
|
||||
let field_shape = field.shape();
|
||||
//println!("{:#?}", field_shape);
|
||||
|
||||
let (self_field_ptr, other_field_ptr) = unsafe {
|
||||
let offset = field.offset.try_into().unwrap();
|
||||
|
||||
let self_ptr: *const u8 = self.1.as_ptr();
|
||||
let me_field_ptr = self_ptr.offset(offset);
|
||||
|
||||
let other_ptr: *const u8 = other.1.as_ptr();
|
||||
let other_field_ptr = other_ptr.offset(offset);
|
||||
|
||||
let me = PtrConst::new(std::ptr::NonNull::new_unchecked(
|
||||
me_field_ptr as *mut u8,
|
||||
));
|
||||
let other = PtrConst::new(std::ptr::NonNull::new_unchecked(
|
||||
other_field_ptr as *mut u8,
|
||||
));
|
||||
|
||||
(me, other)
|
||||
};
|
||||
|
||||
match field_shape.def {
|
||||
facet::Def::Undefined => {
|
||||
let me = ShapeVal(
|
||||
field_shape,
|
||||
self_field_ptr,
|
||||
&format!("{}{}.", self.2, field.name),
|
||||
);
|
||||
let other = ShapeVal(
|
||||
field_shape,
|
||||
other_field_ptr,
|
||||
&format!("{}{}.", self.2, field.name),
|
||||
);
|
||||
|
||||
modifications.extend(me.diff(&other).into_iter());
|
||||
}
|
||||
facet::Def::Scalar => {
|
||||
let partial_eq = field_shape.vtable.partial_eq.unwrap();
|
||||
|
||||
let are_partially_eq =
|
||||
unsafe { partial_eq(self_field_ptr, other_field_ptr) };
|
||||
|
||||
if !are_partially_eq {
|
||||
//println!("{:#?}", field);
|
||||
//println!("{:#?}", field_shape);
|
||||
modifications.push(FieldDiff::of(
|
||||
format!("{}{}", self.2, field.name),
|
||||
Value::from_field_value(other, field),
|
||||
));
|
||||
}
|
||||
}
|
||||
facet::Def::Map(map_def) => todo!(),
|
||||
facet::Def::Set(set_def) => {}
|
||||
facet::Def::List(list_def) => todo!(),
|
||||
facet::Def::Array(array_def) => todo!(),
|
||||
facet::Def::NdArray(nd_array_def) => todo!(),
|
||||
facet::Def::Slice(slice_def) => todo!(),
|
||||
facet::Def::Option(option_def) => todo!(),
|
||||
facet::Def::Pointer(pointer_def) => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
facet::UserType::Enum(enum_type) => todo!(),
|
||||
facet::UserType::Union(union_type) => panic!("union types are unsupported"),
|
||||
facet::UserType::Opaque => panic!("opaque types are invalid"),
|
||||
}
|
||||
|
||||
modifications
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 's> ShapeVal<'a, 's> {
|
||||
fn set_field(&self, field_ident: &str, value: Value) {
|
||||
let mut idents = field_ident.split(".");
|
||||
let next = idents.next().unwrap();
|
||||
|
||||
let facet::Type::User(user) = self.0.ty else {
|
||||
panic!("invalid type");
|
||||
};
|
||||
|
||||
println!("next: {next}");
|
||||
|
||||
match user {
|
||||
facet::UserType::Struct(struct_type) => {
|
||||
if let Some(field) = struct_type.fields.iter().find(|f| f.name == next) {
|
||||
let field_shape = field.shape();
|
||||
match field_shape.ty {
|
||||
facet::Type::Primitive(primitive_type) => {
|
||||
let layout = field_shape
|
||||
.layout
|
||||
.sized_layout()
|
||||
.expect("primitive type is sized");
|
||||
let primitive_size = layout.size();
|
||||
if Some(primitive_size) != value.size() {
|
||||
panic!("invalid primitive size");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let self_ptr = self.1.as_byte_ptr() as *mut u8;
|
||||
let field_ptr = self_ptr.offset(field.offset.try_into().unwrap());
|
||||
|
||||
match value {
|
||||
Value::String(_) | Value::Array(_) => {
|
||||
panic!("String & Array are not primitive types")
|
||||
}
|
||||
|
||||
Value::Int(value) => {
|
||||
let r = (field_ptr as *mut i64).as_mut().unwrap();
|
||||
println!("value: {value}, r before: {r}");
|
||||
*r = value;
|
||||
println!("r after: {r}");
|
||||
}
|
||||
Value::Float(value) => *(field_ptr as *mut f64) = value,
|
||||
Value::Bool(value) => *(field_ptr as *mut bool) = value,
|
||||
}
|
||||
}
|
||||
}
|
||||
facet::Type::Sequence(sequence_type) => todo!(),
|
||||
facet::Type::User(user_type) => match user_type {
|
||||
facet::UserType::Struct(struct_type) => {
|
||||
let field_ptr = unsafe {
|
||||
let self_ptr = self.1.as_byte_ptr() as *mut u8;
|
||||
let field_ptr =
|
||||
self_ptr.offset(field.offset.try_into().unwrap());
|
||||
|
||||
PtrConst::new(std::ptr::NonNull::new_unchecked(field_ptr))
|
||||
};
|
||||
let field_val = ShapeVal(field_shape, field_ptr, "");
|
||||
|
||||
field_val.set_field(&field_ident[(field.name.len() + 1)..], value);
|
||||
}
|
||||
facet::UserType::Enum(enum_type) => todo!(),
|
||||
facet::UserType::Union(union_type) => todo!(),
|
||||
facet::UserType::Opaque if field_shape.type_identifier == "String" => {
|
||||
let Value::String(value) = value else {
|
||||
panic!("got non-string value");
|
||||
};
|
||||
|
||||
let field_ref = unsafe {
|
||||
let self_ptr = self.1.as_byte_ptr() as *mut u8;
|
||||
let field_ptr = self_ptr
|
||||
.offset(field.offset.try_into().unwrap())
|
||||
as *mut String;
|
||||
|
||||
field_ptr.as_mut().unwrap()
|
||||
};
|
||||
|
||||
*field_ref = value;
|
||||
}
|
||||
facet::UserType::Opaque => panic!("unsupported type"),
|
||||
},
|
||||
facet::Type::Pointer(pointer_type) => todo!(),
|
||||
}
|
||||
} else {
|
||||
panic!("couldn't find field: {next}");
|
||||
}
|
||||
}
|
||||
facet::UserType::Enum(enum_type) => todo!(),
|
||||
facet::UserType::Union(union_type) => panic!("union types are unsupported"),
|
||||
facet::UserType::Opaque => panic!("opaque types are invalid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: for<'a> Facet<'a> + for<'a> FieldValueRef<'a>> Diffable for T {
|
||||
fn diff(&self, other: &Self) -> Vec<FieldDiff>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let (me, other) = unsafe {
|
||||
let me = ShapeVal(
|
||||
Self::SHAPE,
|
||||
PtrConst::new(std::ptr::NonNull::new_unchecked(
|
||||
self as *const _ as *mut u8,
|
||||
)),
|
||||
"",
|
||||
);
|
||||
|
||||
let other = ShapeVal(
|
||||
Self::SHAPE,
|
||||
PtrConst::new(std::ptr::NonNull::new_unchecked(
|
||||
other as *const _ as *mut u8,
|
||||
)),
|
||||
"",
|
||||
);
|
||||
|
||||
(me, other)
|
||||
};
|
||||
|
||||
me.diff(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: for<'a> Facet<'a> + Diffable + Encode + std::fmt::Debug> Storable for T {
|
||||
fn as_full_write_op(&self) -> WriteOperation
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let data = bincode::encode_to_vec(
|
||||
TransactionEntity::with(Self::SHAPE.type_identifier, self),
|
||||
bincode::config::standard(),
|
||||
)
|
||||
.expect("encode MUST succeed");
|
||||
|
||||
WriteOperation::Full(data)
|
||||
}
|
||||
|
||||
fn apply_partial_write(&mut self, op: &PartialWrite) {
|
||||
println!("{:#?}", op);
|
||||
println!("before: {:#?}", self);
|
||||
let value = Value::from_bytes(&op.data);
|
||||
self.set_field(&op.field_ident, value);
|
||||
println!("after: {:#?}", self);
|
||||
}
|
||||
|
||||
fn set_field(&mut self, field_ident: &str, value: Value)
|
||||
where
|
||||
Self: for<'a> Facet<'a> + Sized,
|
||||
{
|
||||
let me = unsafe {
|
||||
ShapeVal(
|
||||
Self::SHAPE,
|
||||
PtrConst::new(std::ptr::NonNull::new_unchecked(
|
||||
self as *const _ as *mut u8,
|
||||
)),
|
||||
"",
|
||||
)
|
||||
};
|
||||
|
||||
me.set_field(field_ident, value);
|
||||
}
|
||||
|
||||
fn field(&self, field_ident: &str) -> Option<Value>
|
||||
where
|
||||
Self: for<'a> Facet<'a> + Sized,
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FieldDiff {
|
||||
pub(super) field_ident: String,
|
||||
pub(super) value: Value,
|
||||
|
|
@ -52,6 +330,79 @@ impl From<FieldDiff> for PartialWrite {
|
|||
}
|
||||
}
|
||||
|
||||
trait FieldValueRef<'a> {
|
||||
fn get_field_value_as_ref<F>(&'a self, field: &Field) -> &'a F {
|
||||
unsafe {
|
||||
let container_ptr = self as *const _ as *const u8;
|
||||
let field_ptr = container_ptr.offset(field.offset.try_into().unwrap()) as *const F;
|
||||
|
||||
field_ptr.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Facet<'a>> FieldValueRef<'a> for T {}
|
||||
|
||||
impl<'a, 's> FieldValueRef<'a> for ShapeVal<'a, 's> {
|
||||
fn get_field_value_as_ref<F>(&'a self, field: &Field) -> &'a F {
|
||||
unsafe {
|
||||
let container_ptr: *const u8 = self.1.as_ptr();
|
||||
let field_ptr = container_ptr.offset(field.offset.try_into().unwrap()) as *const F;
|
||||
|
||||
field_ptr.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
fn from_field_value<'a, T: FieldValueRef<'a>>(container: &'a T, field: &Field) -> Self {
|
||||
let shape = field.shape();
|
||||
|
||||
match shape.ty {
|
||||
facet::Type::Primitive(primitive_type) => match primitive_type {
|
||||
facet::PrimitiveType::Boolean => {
|
||||
Self::Bool(*container.get_field_value_as_ref(field))
|
||||
}
|
||||
facet::PrimitiveType::Numeric(numeric_type) => {
|
||||
let field_layout = field.shape().layout.sized_layout().unwrap();
|
||||
|
||||
const I32_SIZE: usize = size_of::<i32>();
|
||||
const I64_SIZE: usize = size_of::<i64>();
|
||||
|
||||
match (field_layout.size(), numeric_type) {
|
||||
(I32_SIZE, facet::NumericType::Integer { signed }) if signed => {
|
||||
Value::Int(*container.get_field_value_as_ref::<i32>(field) as i64)
|
||||
}
|
||||
(I32_SIZE, facet::NumericType::Integer { .. }) => {
|
||||
Value::Int(*container.get_field_value_as_ref::<u32>(field) as i64)
|
||||
}
|
||||
|
||||
(I64_SIZE, facet::NumericType::Integer { signed }) if signed => {
|
||||
Value::Int(*container.get_field_value_as_ref::<u64>(field) as i64)
|
||||
}
|
||||
(I64_SIZE, facet::NumericType::Integer { .. }) => {
|
||||
Value::Int(*container.get_field_value_as_ref::<i64>(field) as i64)
|
||||
}
|
||||
_ => panic!("invalid numeric size"),
|
||||
}
|
||||
}
|
||||
facet::PrimitiveType::Textual(textual_type) => todo!(),
|
||||
facet::PrimitiveType::Never => todo!(),
|
||||
},
|
||||
|
||||
facet::Type::Sequence(sequence_type) => todo!(),
|
||||
facet::Type::User(user_type) => {
|
||||
if shape.type_identifier == "String" {
|
||||
Value::String(container.get_field_value_as_ref::<String>(field).clone())
|
||||
} else {
|
||||
todo!("asdkjhfadsfkdasjhfdssklsadkljfkljasdhfkjlhsdfklsdkfhsd");
|
||||
}
|
||||
}
|
||||
facet::Type::Pointer(pointer_type) => todo!("alkjhdfklajdhs"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Encode, Decode)]
|
||||
pub struct WriteSet {
|
||||
// TODO: make an actual set
|
||||
|
|
@ -75,212 +426,3 @@ pub struct PartialWrite {
|
|||
pub(super) field_ident: String,
|
||||
pub(super) data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! primitive_assign {
|
||||
($self:ident, $field_name:ident, $value:ident, $ty:ty, $variant:ident) => {{
|
||||
let Value::$variant(value) = $value else {
|
||||
panic!(
|
||||
"expected '{}' to be a '{}'",
|
||||
stringify!($field_name),
|
||||
stringify!($ty)
|
||||
);
|
||||
};
|
||||
|
||||
$self.$field_name = value;
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! primitive_return {
|
||||
($self:ident, $field_name:ident, $variant:ident) => {
|
||||
Some(Value::$variant($self.$field_name.clone()))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! db_type {
|
||||
($self:ident, $field_name:ident, i64, $value:ident, $field_ident:ident) => {
|
||||
$crate::primitive_assign!($self, $field_name, $value, i64, Int)
|
||||
};
|
||||
($self:ident, $field_name:ident, String, $value:ident, $field_ident:ident) => {
|
||||
$crate::primitive_assign!($self, $field_name, $value, String, String)
|
||||
};
|
||||
|
||||
($self:ident, $field_name:ident, (HashSet<Id<$ty:ty>>), $value:ident, $field_ident:ident) => {
|
||||
{
|
||||
let Value::Array(value) = $value else {
|
||||
panic!(
|
||||
"expected '{}' to be a 'Vec<Id<{}>>'",
|
||||
stringify!($field_name),
|
||||
stringify!($ty)
|
||||
);
|
||||
};
|
||||
|
||||
$self.$field_name = value.into_iter().map(|id| Id::<_>::new(id.0)).collect();
|
||||
}
|
||||
};
|
||||
|
||||
($self:ident, $field_name:ident, $field_type:ty, $value:ident, $field_ident:ident) => {
|
||||
{
|
||||
let field_name_len = stringify!($field_name).len();
|
||||
if field_name_len == $field_ident.len() {
|
||||
panic!("attempt to assign non-primitive");
|
||||
}
|
||||
|
||||
$self.$field_name.set_field(&$field_ident[(field_name_len+1)..], $value);
|
||||
}
|
||||
};
|
||||
|
||||
($self:ident, $field_name:ident, i64, $field_ident:ident) => {{
|
||||
Some(Value::Int($self.$field_name.clone()))
|
||||
}};
|
||||
($self:ident, $field_name:ident, String, $field_ident:ident) => {{
|
||||
Some(Value::String($self.$field_name.clone()))
|
||||
}};
|
||||
($self:ident, $field_name:ident, (HashSet<Id<$ty:ty>>), $field_ident:ident) => {
|
||||
Some(Value::Array($self.$field_name.iter().map(|id| id.into()).collect()))
|
||||
};
|
||||
($self:ident, $field_name:ident, $field_type:ty, $field_ident:ident) => {{
|
||||
let field_name_len = stringify!($field_name).len();
|
||||
if field_name_len == $field_ident.len() {
|
||||
panic!("attempt to assign non-primitive");
|
||||
}
|
||||
|
||||
$self.$field_name.field(&$field_ident[(field_name_len+1)..])
|
||||
}};
|
||||
|
||||
(@diff $self:ident, $other:ident, $modifications:ident, $field_name:ident, i64) => {
|
||||
if $self.$field_name != $other.$field_name {
|
||||
$modifications.push(FieldDiff::of(
|
||||
stringify!($field_name),
|
||||
Value::Int($other.$field_name.clone()),
|
||||
));
|
||||
}
|
||||
};
|
||||
(@diff $self:ident, $other:ident, $modifications:ident, $field_name:ident, String) => {
|
||||
if $self.$field_name != $other.$field_name {
|
||||
$modifications.push(FieldDiff::of(
|
||||
stringify!($field_name),
|
||||
Value::String($other.$field_name.clone()),
|
||||
));
|
||||
}
|
||||
};
|
||||
(@diff $self:ident, $other:ident, $modifications:ident, $field_name:ident, (HashSet<Id<$ty:ty>>)) => {
|
||||
let diff = $self.$field_name.difference(&$other.$field_name);
|
||||
if diff.count() > 0 {
|
||||
$modifications.push(FieldDiff::of(
|
||||
stringify!($field_name),
|
||||
Value::Array($other.$field_name.iter().map(|id| id.into()).collect())
|
||||
));
|
||||
}
|
||||
};
|
||||
(@diff $self:ident, $other:ident, $modifications:ident, $field_name:ident, $field_type:ty) => {
|
||||
$modifications.extend(
|
||||
$self.$field_name
|
||||
.diff(&$other.$field_name)
|
||||
.into_iter()
|
||||
.map(|diff| {
|
||||
// NOTE(pcleavelin): we have to recreate the `field_ident` path via recursive
|
||||
// string building lol
|
||||
FieldDiff::of(
|
||||
format!("{}.{}", stringify!($field_name), &diff.field_ident),
|
||||
diff.value
|
||||
)
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
{
|
||||
$(
|
||||
struct $db_type_name:ident {
|
||||
$(
|
||||
$field_name:ident: $field_type:tt,
|
||||
)*
|
||||
}
|
||||
)*
|
||||
} => {
|
||||
$(
|
||||
#[derive(Debug, Clone, ::bincode::Encode, ::bincode::Decode)]
|
||||
pub struct $db_type_name {
|
||||
$(
|
||||
$field_name: $field_type,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Storable for $db_type_name {
|
||||
fn as_full_write_op(&self) -> WriteOperation
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let data =
|
||||
bincode::encode_to_vec(A::with(stringify!($db_type_name), self), bincode::config::standard())
|
||||
.expect("encode MUST succeed");
|
||||
|
||||
WriteOperation::Full(data)
|
||||
}
|
||||
fn as_partial_write_op(&self, other: &Self) -> WriteOperation
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
WriteOperation::Partial(self.diff(other).into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
fn apply_partial_write(&mut self, op: &write_set::PartialWrite) {
|
||||
let value = Value::from_bytes(&op.data);
|
||||
self.set_field(&op.field_ident, value);
|
||||
}
|
||||
|
||||
fn set_field(&mut self, field_ident: &str, value: Value)
|
||||
where Self: Sized,
|
||||
{
|
||||
let mut idents = field_ident.split(".");
|
||||
let next = idents.next().unwrap();
|
||||
|
||||
match next {
|
||||
$(
|
||||
stringify!($field_name) => {
|
||||
db_type!(self, $field_name, $field_type, value, field_ident)
|
||||
}
|
||||
),*
|
||||
|
||||
_ => panic!("set_field: invalid field '{next}'"),
|
||||
}
|
||||
}
|
||||
|
||||
fn field(&self, field_ident: &str) -> Option<Value>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut idents = field_ident.split(".");
|
||||
let next = idents.next().unwrap();
|
||||
|
||||
match next {
|
||||
$(
|
||||
stringify!($field_name) => {
|
||||
db_type!(self, $field_name, $field_type, field_ident)
|
||||
}
|
||||
),*
|
||||
|
||||
_ => panic!("field: invalid field '{next}'"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Diffable for $db_type_name {
|
||||
fn diff(&self, other: &Self) -> Vec<FieldDiff>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut modifications = vec![];
|
||||
|
||||
$(
|
||||
db_type!(@diff self, other, modifications, $field_name, $field_type);
|
||||
)*
|
||||
|
||||
modifications
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue