diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-02-27 20:56:20 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-02-27 20:56:20 +0100 |
| commit | 7930d543a2aa68d4ad2958605827d7eb1baa91f8 (patch) | |
| tree | fe59d1f549e303a96b78d3e925d75abb70b73af0 /common | |
| parent | c05bfcc2775f0e11db6e856bfcf06d0419c35d54 (diff) | |
| download | jellything-7930d543a2aa68d4ad2958605827d7eb1baa91f8.tar jellything-7930d543a2aa68d4ad2958605827d7eb1baa91f8.tar.bz2 jellything-7930d543a2aa68d4ad2958605827d7eb1baa91f8.tar.zst | |
reimplement Object as slice type
Diffstat (limited to 'common')
| -rw-r--r-- | common/object/src/buffer.rs | 76 | ||||
| -rw-r--r-- | common/object/src/debug.rs | 14 | ||||
| -rw-r--r-- | common/object/src/json.rs | 4 | ||||
| -rw-r--r-- | common/object/src/lib.rs | 276 | ||||
| -rw-r--r-- | common/object/src/path.rs | 18 | ||||
| -rw-r--r-- | common/object/src/registry.rs | 39 | ||||
| -rw-r--r-- | common/object/src/tag.rs | 73 | ||||
| -rw-r--r-- | common/object/src/tests.rs | 36 | ||||
| -rw-r--r-- | common/object/src/value.rs | 139 | ||||
| -rw-r--r-- | common/src/api.rs | 7 | ||||
| -rw-r--r-- | common/src/internal.rs | 2 | ||||
| -rw-r--r-- | common/src/lib.rs | 1 | ||||
| -rw-r--r-- | common/src/node.rs | 78 | ||||
| -rw-r--r-- | common/src/user.rs | 8 |
14 files changed, 354 insertions, 417 deletions
diff --git a/common/object/src/buffer.rs b/common/object/src/buffer.rs index 1f8cec6..4d3af34 100644 --- a/common/object/src/buffer.rs +++ b/common/object/src/buffer.rs @@ -4,56 +4,12 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, Tag, TypedTag, ValueStore}; +use crate::{Object, Tag, TypedTag, ValueMarker, value::ValueSer}; use bytemuck::{try_cast_slice, try_cast_vec}; +use core::marker::Sized; use log::trace; use std::borrow::Cow; -#[derive(PartialEq, Eq, Hash)] -pub struct ObjectBuffer(pub Vec<u32>); - -impl ObjectBuffer { - pub fn empty() -> Self { - Self(vec![0]) - } - pub fn as_object<'a>(&'a self) -> Object<'a> { - Object::load(&self.0).unwrap() - } - pub fn to_bytes(self) -> Vec<u8> { - self.0.into_iter().flat_map(u32::to_le_bytes).collect() - } - pub fn new(fields: &mut [(Tag, &dyn ValueStore)]) -> ObjectBuffer { - let mut tags = Vec::new(); - let mut offsets = Vec::new(); - let mut values = Vec::new(); - fields.sort_by_key(|(t, _)| t.0); - let mut temp = Vec::new(); - for (tag, val) in fields { - tags.push(tag.0); - let ty = val.get_type(); - let off = (values.len() as u32) << 5 | (ty as u32) << 2; - if ty.is_aligned() { - offsets.push(off); - val.store_aligned(&mut values); - } else { - temp.clear(); - val.store_unaligned(&mut temp); - let pad = pad_vec(&mut temp); - offsets.push(off | pad); - values.extend(&*slice_u8_to_u32(&temp)); - } - } - ObjectBuffer( - [tags.len() as u32] - .into_iter() - .chain(tags) - .chain(offsets) - .chain(values) - .collect(), - ) - } -} - pub type OBB = ObjectBufferBuilder; #[derive(Default)] @@ -62,10 +18,9 @@ impl ObjectBufferBuilder { pub fn new() -> Self { Self::default() } - pub fn push<T: ValueStore>(&mut self, tag: TypedTag<T>, value: T) { - let ty = value.get_type(); - let tyb = (ty as u32) << 2; - if ty.is_aligned() { + pub fn push<'a, T: ValueMarker<'a> + ?Sized>(&mut self, tag: TypedTag<T>, value: T::Inner) { + let tyb = (T::Inner::TYPE as u32) << 2; + if T::Inner::TYPE.is_aligned() { let mut buf = Vec::new(); value.store_aligned(&mut buf); self.0.push((tag.0, tyb, buf)); @@ -76,11 +31,15 @@ impl ObjectBufferBuilder { self.0.push((tag.0, tyb | pad, vec_u8_to_u32(buf))); } } - pub fn with<T: ValueStore>(mut self, tag: TypedTag<T>, value: T) -> Self { + pub fn with<'a, T: ValueMarker<'a> + ?Sized>( + mut self, + tag: TypedTag<T>, + value: T::Inner, + ) -> Self { self.push(tag, value); self } - pub fn finish(mut self) -> ObjectBuffer { + pub fn finish(mut self) -> Box<Object> { let mut tags = Vec::new(); let mut offsets = Vec::new(); let mut values = Vec::new(); @@ -91,7 +50,7 @@ impl ObjectBufferBuilder { offsets.push(off); values.extend(buf); } - ObjectBuffer( + vec_to_ob( [tags.len() as u32] .into_iter() .chain(tags) @@ -102,10 +61,13 @@ impl ObjectBufferBuilder { } } -impl From<Vec<u8>> for ObjectBuffer { - fn from(value: Vec<u8>) -> Self { - ObjectBuffer(vec_u8_to_u32(value)) - } +pub fn vec_to_ob(v: Vec<u32>) -> Box<Object> { + //? safe way to do this? + unsafe { std::mem::transmute(v.into_boxed_slice()) } +} +pub const fn slice_to_ob<'a>(v: &'a [u32]) -> &'a Object { + //? safe way to do this? + unsafe { std::mem::transmute(v) } } #[inline] diff --git a/common/object/src/debug.rs b/common/object/src/debug.rs index b45d5bf..196da20 100644 --- a/common/object/src/debug.rs +++ b/common/object/src/debug.rs @@ -4,16 +4,10 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, ObjectBuffer, Tag, TypedTag, ValueType}; +use crate::{Object, Tag, TypedTag, ValueType}; use std::{any::type_name, fmt::Debug}; -impl Debug for ObjectBuffer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.as_object().fmt(f) - } -} - -impl Debug for Object<'_> { +impl Debug for Object { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut s = f.debug_struct("Object"); let mut nonexhaustive = false; @@ -23,8 +17,8 @@ impl Debug for Object<'_> { let ty = self.offset_type(i); let sz = self.size(i); match (ty, sz) { - (ValueType::String, _) => s.field(k, &self.get_typed::<&str>(i).unwrap()), - (ValueType::Binary, _) => s.field(k, &self.get_typed::<&[u8]>(i).unwrap()), + (ValueType::String, _) => s.field(k, &self.get_typed::<str>(i).unwrap()), + (ValueType::Binary, _) => s.field(k, &self.get_typed::<[u8]>(i).unwrap()), (ValueType::Object, _) => s.field(k, &self.get_typed::<Object>(i).unwrap()), (ValueType::UInt, 4) => s.field(k, &self.get_typed::<u32>(i).unwrap()), (ValueType::UInt, 8) => s.field(k, &self.get_typed::<u64>(i).unwrap()), diff --git a/common/object/src/json.rs b/common/object/src/json.rs index a18ecf5..32b7009 100644 --- a/common/object/src/json.rs +++ b/common/object/src/json.rs @@ -7,7 +7,7 @@ use crate::{Object, Tag, ValueType}; use serde_json::{Map, Value}; -pub fn object_to_json(ob: Object<'_>) -> Value { +pub fn object_to_json(ob: &Object) -> Value { let mut o = Map::new(); let mut nonexhaustive = false; for (i, tag) in ob.keys().enumerate() { @@ -16,7 +16,7 @@ pub fn object_to_json(ob: Object<'_>) -> Value { let ty = ob.offset_type(i); let sz = ob.size(i); let val = match (ty, sz) { - (ValueType::String, _) => ob.get_typed::<&str>(i).unwrap().to_string().into(), + (ValueType::String, _) => ob.get_typed::<str>(i).unwrap().to_string().into(), (ValueType::Object, _) => object_to_json(ob.get_typed::<Object>(i).unwrap()), (ValueType::Tag, 4) => ob.get_typed::<Tag>(i).unwrap().to_string().into(), (ValueType::UInt, 4) => ob.get_typed::<u32>(i).unwrap().into(), diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs index ae8d1cd..783871b 100644 --- a/common/object/src/lib.rs +++ b/common/object/src/lib.rs @@ -3,102 +3,66 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin <metamuffin.org> */ -#![feature(iter_array_chunks, strip_circumfix)] +#![feature(iter_array_chunks, strip_circumfix, phantom_variance_markers)] mod buffer; pub mod debug; #[cfg(feature = "json")] pub mod json; mod path; -mod registry; +pub mod tag; #[cfg(test)] mod tests; mod value; pub use buffer::*; pub use path::*; -pub use registry::*; +pub use tag::*; pub use value::*; -use std::{collections::BTreeSet, fmt::Display, hash::Hash, marker::PhantomData}; +use core::{marker::Sized, todo}; +use std::{collections::BTreeSet, hash::Hash, marker::PhantomData}; +#[derive(Hash, PartialEq, Eq)] #[repr(transparent)] -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Tag(pub u32); +pub struct Object([u32]); -impl Default for Tag { - fn default() -> Self { - Self::new(b"1111") - } -} -impl Tag { - pub const fn new(fourcc: &[u8; 4]) -> Self { - Self(u32::from_le_bytes(*fourcc)) - } -} -impl Display for Tag { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(str::from_utf8(&self.0.to_le_bytes()).unwrap()) +impl ToOwned for Object { + type Owned = Box<Object>; + fn to_owned(&self) -> Self::Owned { + vec_to_ob(self.0.to_vec()) } } -#[derive(PartialEq, Eq, PartialOrd, Ord)] -pub struct TypedTag<T>(pub Tag, pub PhantomData<T>); -impl<T> Display for TypedTag<T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} +pub const EMPTY: &'static Object = slice_to_ob(&[0]); -impl<T> Clone for TypedTag<T> { - fn clone(&self) -> Self { - Self(self.0, PhantomData) +impl Object { + #[inline] + fn len(&self) -> usize { + self.0[0] as usize } -} -impl<T> Copy for TypedTag<T> {} - -#[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct Object<'a> { - tags: &'a [u32], - offsets: &'a [u32], - values: &'a [u32], -} - -impl<'a> Default for Object<'a> { - fn default() -> Self { - Self::EMPTY + #[inline] + fn tags(&self) -> &[u32] { + &self.0[1..1 + self.len()] } -} -impl<'a> Object<'a> { - pub const EMPTY: Object<'static> = Object { - offsets: &[], - tags: &[], - values: &[], - }; - - pub fn load(buf: &'a [u32]) -> Option<Self> { - let nf = *buf.get(0)? as usize; - if buf.len() < 1 + nf * 2 { - return None; - } - Some(Self { - tags: &buf[1..1 + nf], - offsets: &buf[1 + nf..1 + nf + nf], - values: &buf[1 + nf + nf..], - }) + #[inline] + fn offsets(&self) -> &[u32] { + let nf = self.len(); + &self.0[1 + nf..1 + nf + nf] } - pub fn dump(&self) -> ObjectBuffer { - let mut out = Vec::new(); - out.push(self.tags.len() as u32); - out.extend(self.tags); - out.extend(self.offsets); - out.extend(self.values); - ObjectBuffer(out) + #[inline] + fn values(&self) -> &[u32] { + let nf = self.len(); + &self.0[1 + nf + nf..] } + pub fn export(&self) -> &[u32] { + &self.0 + } + pub fn find_field(&self, tag: Tag) -> Option<usize> { // using partition as binary search for the first field (instead of regular binary_search that returns any) - let first = self.tags.partition_point(|&x| x < tag.0); - self.tags + let first = self.tags().partition_point(|&x| x < tag.0); + self.tags() .get(first) .is_some_and(|&x| x == tag.0) .then_some(first) @@ -108,94 +72,97 @@ impl<'a> Object<'a> { } #[inline] fn offset(&self, i: usize) -> usize { - self.offsets + self.offsets() .get(i) .map(|&v| v >> 5) - .unwrap_or(self.values.len() as u32) as usize + .unwrap_or(self.values().len() as u32) as usize } fn offset_type(&self, i: usize) -> ValueType { - let raw = (self.offsets[i] >> 2) & 0b111; + let raw = (self.offsets()[i] >> 2) & 0b111; ValueType::from_num(raw) } fn size(&self, i: usize) -> u32 { - let start_raw = self.offsets[i]; + let start_raw = self.offsets()[i]; let end_raw = self - .offsets + .offsets() .get(i + 1) .copied() - .unwrap_or((self.values.len() as u32) << 5); + .unwrap_or((self.values().len() as u32) << 5); let u32_len = (end_raw >> 5) - (start_raw >> 5); let padding = start_raw & 0b11; u32_len * 4 - padding } - fn get_aligned(&self, index: usize) -> Option<&'a [u32]> { - let start_raw = self.offsets[index]; + fn get_aligned<'a>(&'a self, index: usize) -> Option<&'a [u32]> { + let start_raw = self.offsets()[index]; let end_raw = self - .offsets + .offsets() .get(index + 1) .copied() - .unwrap_or((self.values.len() as u32) << 5); + .unwrap_or((self.values().len() as u32) << 5); let start = start_raw >> 5; let end = end_raw >> 5; - Some(&self.values[start as usize..end as usize]) + Some(&self.values()[start as usize..end as usize]) } - fn get_unaligned(&self, index: usize) -> Option<&'a [u8]> { - let start_raw = self.offsets[index]; + fn get_unaligned<'a>(&'a self, index: usize) -> Option<&'a [u8]> { + let start_raw = self.offsets()[index]; let end_raw = self - .offsets + .offsets() .get(index + 1) .copied() - .unwrap_or((self.values.len() as u32) << 5); + .unwrap_or((self.values().len() as u32) << 5); let start = (start_raw >> 5) * 4; let padding = start_raw & 0b11; let end = (end_raw >> 5) * 4 - padding; - let values_u8: &[u8] = bytemuck::cast_slice(self.values); + let values_u8: &[u8] = bytemuck::cast_slice(self.values()); Some(&values_u8[start as usize..end as usize]) } #[inline] - pub fn get_typed<T: ValueLoad<'a>>(&self, index: usize) -> Option<T> { - if T::TYPE.is_aligned() { - T::load_aligned(self.get_aligned(index)?) + pub fn get_typed<'a, T: ValueMarker<'a> + ?Sized>(&'a self, index: usize) -> Option<T::Inner> { + if T::Inner::TYPE.is_aligned() { + T::Inner::load_aligned(self.get_aligned(index)?) } else { - T::load_unaligned(self.get_unaligned(index)?) + T::Inner::load_unaligned(self.get_unaligned(index)?) } } - pub fn get<T: ValueLoad<'a>>(&self, tag: TypedTag<T>) -> Option<T> { - self.get_typed(self.find_field(tag.0)?) + pub fn get<'a, T: ValueMarker<'a> + ?Sized>(&'a self, tag: TypedTag<T>) -> Option<T::Inner> { + self.get_typed::<T>(self.find_field(tag.0)?) } - pub fn keys(&self) -> KeysIter<'a> { + pub fn keys<'a>(&'a self) -> KeysIter<'a> { KeysIter { - object: *self, + object: self, index: 0, } } - pub fn entries<T>(&self) -> EntriesIter<'a, T> { + pub fn entries<'a, T: ?Sized>(&'a self) -> EntriesIter<'a, T> { EntriesIter { - object: *self, + object: self, index: 0, ty: PhantomData, } } - pub fn iter<T>(&self, tag: TypedTag<T>) -> FieldIter<'a, T> { + pub fn iter<'a, T: ?Sized>(&'a self, tag: TypedTag<T>) -> FieldIter<'a, T> { FieldIter { - object: *self, - index: self.tags.partition_point(|&x| x < tag.0.0), + object: self, + index: self.tags().partition_point(|&x| x < tag.0.0), tag: tag.0.0, ty: PhantomData, } } #[must_use] - pub fn extend<T: ValueLoad<'a> + Eq + Ord>( - &self, + pub fn extend<'a, T: ValueMarker<'a> + ?Sized>( + &'a self, tag: TypedTag<T>, - values: impl IntoIterator<Item = T>, - ) -> ObjectBuffer { + values: impl IntoIterator<Item = T::Inner>, + ) -> Box<Object> + where + T::Inner: Eq + Ord, + { self.insert_multi( tag, &self @@ -209,71 +176,75 @@ impl<'a> Object<'a> { #[must_use] pub fn extend_object( &self, - tag: TypedTag<Object<'static>>, + tag: TypedTag<Object>, ident: Tag, - values: impl IntoIterator<Item = ObjectBuffer>, - ) -> ObjectBuffer { - let ident = TypedTag(ident, PhantomData::<&[u8]>); + values: &[Box<Object>], + ) -> Box<Object> { + let ident = TypedTag::<[u8]>::new(ident); let mut new_vals = Vec::new(); for ob in self.iter(tag) { - new_vals.push(ob.dump()); + new_vals.push(ob); } let mut any_new = false; for val in values { - if new_vals - .iter() - .all(|rhs| rhs.as_object().get(ident) != val.as_object().get(ident)) - { + if new_vals.iter().all(|rhs| rhs.get(ident) != val.get(ident)) { any_new = true; - new_vals.push(val); + new_vals.push(&val); } } if any_new { - self.insert_multi(TypedTag(tag.0, PhantomData), &new_vals) + self.insert_multi(TypedTag::<Object>::new(tag.0), &new_vals) } else { - self.dump() + todo!() } } #[must_use] #[inline] - pub fn insert<T: ValueStore>(&self, tag: TypedTag<T>, value: T) -> ObjectBuffer { + pub fn insert<'a, T: ValueMarker<'a> + ?Sized>( + &'a self, + tag: TypedTag<T>, + value: T::Inner, + ) -> Box<Object> { self.insert_multi(tag, &[value]) } #[must_use] #[inline] - pub fn remove<T: ValueStore>(&self, tag: TypedTag<T>) -> ObjectBuffer { + pub fn remove<'a, T: ValueMarker<'a>>(&'a self, tag: TypedTag<T>) -> Box<Object> { self.insert_multi(tag, &[]) } #[must_use] - pub fn insert_multi<T: ValueStore>(&self, tag: TypedTag<T>, values: &[T]) -> ObjectBuffer { - let prefix = self.tags.partition_point(|&x| x < tag.0.0); - let suffix = self.tags.partition_point(|&x| x <= tag.0.0); + pub fn insert_multi<'a, T: ValueMarker<'a> + ?Sized>( + &'a self, + tag: TypedTag<T>, + values: &[T::Inner], + ) -> Box<Object> { + let prefix = self.tags().partition_point(|&x| x < tag.0.0); + let suffix = self.tags().partition_point(|&x| x <= tag.0.0); let values_prefix = self.offset(prefix); let values_suffix = self.offset(suffix); let mut buf = Vec::new(); let cut_size = suffix - prefix; - buf.push((self.tags.len() - cut_size + values.len()) as u32); + buf.push((self.tags().len() - cut_size + values.len()) as u32); - buf.extend(&self.tags[..prefix]); + buf.extend(&self.tags()[..prefix]); buf.extend(values.iter().map(|_| tag.0.0)); - buf.extend(&self.tags[suffix..]); + buf.extend(&self.tags()[suffix..]); - buf.extend(&self.offsets[..prefix]); + buf.extend(&self.offsets()[..prefix]); let new_offs = buf.len(); buf.extend(values.iter().map(|_| 0)); // placeholder let suffix_offs = buf.len(); - buf.extend(&self.offsets[suffix..]); // need offsetting later + buf.extend(&self.offsets()[suffix..]); // need offsetting later let suffix_end = buf.len(); let values_start = buf.len() as u32; - buf.extend(&self.values[..values_prefix]); + buf.extend(&self.values()[..values_prefix]); let mut temp = Vec::new(); let values_new = buf.len(); for (i, val) in values.iter().enumerate() { - let ty = val.get_type(); - let off = (buf.len() as u32 - values_start) << 5 | (ty as u32) << 2; - if ty.is_aligned() { + let off = (buf.len() as u32 - values_start) << 5 | (T::Inner::TYPE as u32) << 2; + if T::Inner::TYPE.is_aligned() { buf[new_offs + i] = off; val.store_aligned(&mut buf); } else { @@ -292,77 +263,78 @@ impl<'a> Object<'a> { .iter_mut() .for_each(|e| *e = e.strict_add_signed(suffix_offset)); } - buf.extend(&self.values[values_suffix..]); + buf.extend(&self.values()[values_suffix..]); - ObjectBuffer(buf) + vec_to_ob(buf) } #[must_use] pub fn update( &self, - tag: TypedTag<Object<'static>>, - update: impl FnOnce(Object<'a>) -> ObjectBuffer, - ) -> ObjectBuffer { - self.insert(tag, update(self.get(tag).unwrap_or_default()).as_object()) + tag: TypedTag<Object>, + update: impl FnOnce(&Object) -> Box<Object>, + ) -> Box<Object> { + let ob = update(self.get(tag).unwrap_or(EMPTY)); + self.insert(tag, &ob) } } pub struct KeysIter<'a> { - object: Object<'a>, + object: &'a Object, index: usize, } impl Iterator for KeysIter<'_> { type Item = Tag; fn next(&mut self) -> Option<Self::Item> { - if self.index >= self.object.tags.len() { + if self.index >= self.object.tags().len() { return None; } else { self.index += 1; - Some(Tag(self.object.tags[self.index - 1])) + Some(Tag(self.object.tags()[self.index - 1])) } } } -pub struct EntriesIter<'a, T> { - object: Object<'a>, +pub struct EntriesIter<'a, T: ?Sized> { + object: &'a Object, index: usize, ty: PhantomData<T>, } -impl<'a, T: ValueLoad<'a>> Iterator for EntriesIter<'a, T> { - type Item = (Tag, T); +impl<'a, T: ValueMarker<'a> + ?Sized> Iterator for EntriesIter<'a, T> { + type Item = (Tag, T::Inner); fn next(&mut self) -> Option<Self::Item> { loop { - if self.index >= self.object.tags.len() { + if self.index >= self.object.tags().len() { return None; } - if T::TYPE != self.object.offset_type(self.index) { + if T::Inner::TYPE != self.object.offset_type(self.index) { self.index += 1; continue; } - let value = self.object.get_typed(self.index)?; - let tag = self.object.tags[self.index]; + let value = self.object.get_typed::<T>(self.index)?; + let tag = self.object.tags()[self.index]; self.index += 1; return Some((Tag(tag), value)); } } } -pub struct FieldIter<'a, T> { - object: Object<'a>, +pub struct FieldIter<'a, T: ?Sized> { + object: &'a Object, index: usize, tag: u32, ty: PhantomData<T>, } -impl<'a, T: ValueLoad<'a>> Iterator for FieldIter<'a, T> { - type Item = T; +impl<'a, T: ValueMarker<'a> + ?Sized> Iterator for FieldIter<'a, T> { + type Item = T::Inner; fn next(&mut self) -> Option<Self::Item> { - if self.index >= self.object.tags.len() { + if self.index >= self.object.tags().len() { return None; } - if self.object.tags[self.index] != self.tag { + if self.object.tags()[self.index] != self.tag { return None; } - let val = self.object.get_typed(self.index); + let val = self.object.get_typed::<T>(self.index); self.index += 1; val } diff --git a/common/object/src/path.rs b/common/object/src/path.rs index 793400c..83b2c43 100644 --- a/common/object/src/path.rs +++ b/common/object/src/path.rs @@ -5,30 +5,30 @@ */ use crate::{Object, Tag, TypedTag}; -use std::{fmt::Display, marker::PhantomData, str::FromStr}; +use std::{fmt::Display, str::FromStr}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Path(pub Vec<Tag>); impl Path { - pub fn get_matching_value<'a>(&self, ob: Object<'a>) -> Option<&'a [u8]> { - fn recurse<'a>(ob: Object<'a>, path: &[Tag]) -> Option<&'a [u8]> { + pub fn get_matching_value<'a>(&self, ob: &'a Object) -> Option<&'a [u8]> { + fn recurse<'a>(ob: &'a Object, path: &[Tag]) -> Option<&'a [u8]> { if path.len() > 1 { - recurse(ob.get(TypedTag(path[0], PhantomData))?, &path[1..]) + recurse(ob.get(TypedTag::<Object>::new(path[0]))?, &path[1..]) } else { - ob.get(TypedTag(path[0], PhantomData)) + ob.get(TypedTag::<[u8]>::new(path[0])) } } recurse(ob, &self.0) } - pub fn get_matching_values<'a>(&self, ob: Object<'a>) -> Vec<&'a [u8]> { - fn recurse<'a>(ob: Object<'a>, out: &mut Vec<&'a [u8]>, path: &[Tag]) { + pub fn get_matching_values<'a>(&self, ob: &'a Object) -> Vec<&'a [u8]> { + fn recurse<'a>(ob: &'a Object, out: &mut Vec<&'a [u8]>, path: &[Tag]) { if path.len() > 1 { - for nested in ob.iter(TypedTag(path[0], PhantomData::<Object>)) { + for nested in ob.iter(TypedTag::<Object>::new(path[0])) { recurse(nested, out, &path[1..]); } } else { - out.extend(ob.iter(TypedTag(path[0], PhantomData::<&[u8]>))); + out.extend(ob.iter(TypedTag::<[u8]>::new(path[0]))); } } let mut out = Vec::new(); diff --git a/common/object/src/registry.rs b/common/object/src/registry.rs deleted file mode 100644 index af028e8..0000000 --- a/common/object/src/registry.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - This file is part of jellything (https://codeberg.org/metamuffin/jellything) - which is licensed under the GNU Affero General Public License (version 3); see /COPYING. - Copyright (C) 2026 metamuffin <metamuffin.org> -*/ - -use std::any::TypeId; - -pub mod types { - use crate::Object; - use std::any::TypeId; - - pub const OBJECT: TypeId = TypeId::of::<Object>(); - pub const STR: TypeId = TypeId::of::<&str>(); - pub const BINARY: TypeId = TypeId::of::<&[u8]>(); - pub const U32: TypeId = TypeId::of::<u32>(); - pub const U64: TypeId = TypeId::of::<u64>(); - pub const I64: TypeId = TypeId::of::<i64>(); - pub const F64: TypeId = TypeId::of::<f64>(); -} - -#[derive(Clone)] -pub struct TagInfo { - pub name: &'static str, - pub r#type: Option<TypeId>, -} - -#[macro_export] -macro_rules! fields { - ($($id:ident: $type:ty = $tag:literal;)*) => { - $(pub const $id: $crate::TypedTag<$type> = $crate::TypedTag($crate::Tag::new($tag), std::marker::PhantomData);)* - }; -} -#[macro_export] -macro_rules! enums { - ($($id:ident = $tag:literal;)*) => { - $(pub const $id: $crate::Tag = $crate::Tag::new($tag);)* - }; -} diff --git a/common/object/src/tag.rs b/common/object/src/tag.rs new file mode 100644 index 0000000..c09af27 --- /dev/null +++ b/common/object/src/tag.rs @@ -0,0 +1,73 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2026 metamuffin <metamuffin.org> +*/ + +use core::marker::{PhantomData, Sized}; +use std::fmt::Display; + +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Tag(pub u32); + +impl Default for Tag { + fn default() -> Self { + Self::new(b"1111") + } +} +impl Tag { + pub const fn new(fourcc: &[u8; 4]) -> Self { + Self(u32::from_le_bytes(*fourcc)) + } +} +impl Display for Tag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(str::from_utf8(&self.0.to_le_bytes()).unwrap()) + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct TypedTag<T: ?Sized>(pub Tag, pub PhantomData<T>); +impl<T: ?Sized> Display for TypedTag<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} +impl<T: ?Sized> Clone for TypedTag<T> { + fn clone(&self) -> Self { + Self(self.0, PhantomData) + } +} +impl<T: ?Sized> Copy for TypedTag<T> {} +impl<T: ?Sized> TypedTag<T> { + pub const fn new(tag: Tag) -> Self { + Self(tag, PhantomData) + } +} + +pub mod types { + use crate::Object; + use std::any::TypeId; + + pub const OBJECT: TypeId = TypeId::of::<Object>(); + pub const STR: TypeId = TypeId::of::<&str>(); + pub const BINARY: TypeId = TypeId::of::<&[u8]>(); + pub const U32: TypeId = TypeId::of::<u32>(); + pub const U64: TypeId = TypeId::of::<u64>(); + pub const I64: TypeId = TypeId::of::<i64>(); + pub const F64: TypeId = TypeId::of::<f64>(); +} + +#[macro_export] +macro_rules! fields { + ($($id:ident: $type:ty = $tag:literal;)*) => { + $(pub const $id: $crate::TypedTag<$type> = $crate::TypedTag::new($crate::Tag::new($tag));)* + }; +} +#[macro_export] +macro_rules! enums { + ($($id:ident = $tag:literal;)*) => { + $(pub const $id: $crate::Tag = $crate::Tag::new($tag);)* + }; +} diff --git a/common/object/src/tests.rs b/common/object/src/tests.rs index 73f7b6f..b6455f5 100644 --- a/common/object/src/tests.rs +++ b/common/object/src/tests.rs @@ -4,28 +4,27 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, ObjectBuffer, fields}; +use crate::{EMPTY, OBB, Object, fields}; fields! { - NAME: &str = b"name"; + NAME: str = b"name"; AGE: u32 = b"age1"; - FRIEND: &str = b"frnd"; + FRIEND: str = b"frnd"; STUFF: Object = b"stff"; } -fn test_object() -> ObjectBuffer { - ObjectBuffer::new(&mut [ - (NAME.0, &"Bob"), - (AGE.0, &35_u32), - (FRIEND.0, &"Alice"), - (FRIEND.0, &"Charlie"), - ]) +fn test_object() -> Box<Object> { + OBB::new() + .with(NAME, "Bob") + .with(AGE, 35) + .with(FRIEND, "Alice") + .with(FRIEND, "Charlie") + .finish() } #[test] fn read_single_field() { let bob = test_object(); - let bob = bob.as_object(); assert_eq!(bob.get(NAME), Some("Bob")); assert_eq!(bob.get(AGE), Some(35)); } @@ -33,7 +32,6 @@ fn read_single_field() { #[test] fn read_multi_field() { let bob = test_object(); - let bob = bob.as_object(); let mut friends = bob.iter(FRIEND); assert_eq!(friends.next(), Some("Alice")); @@ -56,10 +54,10 @@ fn vec_align_test() { #[test] fn insert() { let bob = test_object(); - let edward = bob.as_object().insert(NAME, "Edward"); + let edward = bob.insert(NAME, "Edward"); - eprintln!("{:#?}", bob.as_object()); - let edward = edward.as_object(); + eprintln!("{:#?}", bob); + let edward = edward; eprintln!("{edward:#?}"); assert_eq!(edward.get(NAME), Some("Edward")); assert_eq!(edward.get(AGE), Some(35)); @@ -72,11 +70,11 @@ fn insert() { #[test] fn insert_empty() { - let ob = Object::EMPTY.insert(NAME, "Romeo"); - assert_eq!(ob.as_object().get(NAME), Some("Romeo")); + let ob = EMPTY.insert(NAME, "Romeo"); + assert_eq!(ob.get(NAME), Some("Romeo")); - let ob = Object::EMPTY.insert(STUFF, Object::EMPTY); - assert_eq!(ob.as_object().get(STUFF), Some(Object::EMPTY)); + let ob = EMPTY.insert(STUFF, EMPTY); + assert_eq!(ob.get(STUFF), Some(EMPTY)); } #[test] diff --git a/common/object/src/value.rs b/common/object/src/value.rs index 9b9a438..0a1ceb9 100644 --- a/common/object/src/value.rs +++ b/common/object/src/value.rs @@ -4,19 +4,58 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, ObjectBuffer, Tag}; +use crate::{Object, Tag, slice_to_ob}; +use core::{marker::Sized, unimplemented}; use std::{borrow::Cow, fmt::Display, str::FromStr}; -pub trait ValueLoad<'a>: ValueStore + Sized { +pub trait ValueMarker<'a> { + type Inner: ValueSer<'a>; +} + +impl<'a> ValueMarker<'a> for str { + type Inner = &'a str; +} +impl<'a> ValueMarker<'a> for Object { + type Inner = &'a Object; +} +impl<'a> ValueMarker<'a> for [u8] { + type Inner = &'a [u8]; +} + +impl<'a> ValueMarker<'a> for Tag { + type Inner = Tag; +} +impl<'a> ValueMarker<'a> for u32 { + type Inner = u32; +} +impl<'a> ValueMarker<'a> for i64 { + type Inner = i64; +} +impl<'a> ValueMarker<'a> for u64 { + type Inner = u64; +} +impl<'a> ValueMarker<'a> for () { + type Inner = (); +} +impl<'a> ValueMarker<'a> for f64 { + type Inner = f64; +} + +pub trait ValueSer<'a>: Sized { const TYPE: ValueType; - fn load_aligned(buf: &'a [u32]) -> Option<Self> { - let _ = buf; + fn load_aligned(_buf: &'a [u32]) -> Option<Self> { unimplemented!() } - fn load_unaligned(buf: &'a [u8]) -> Option<Self> { - let _ = buf; + fn load_unaligned(_buf: &'a [u8]) -> Option<Self> { + unimplemented!() + } + fn store_aligned(&self, _buf: &mut Vec<u32>) { unimplemented!() } + fn store_unaligned(&self, _buf: &mut Vec<u8>) { + unimplemented!() + } + fn size(&self) -> usize; } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -126,22 +165,11 @@ impl FromStr for Value<'static> { } } -pub trait ValueStore { - fn get_type(&self) -> ValueType; - fn store_aligned(&self, _buf: &mut Vec<u32>) {} - fn store_unaligned(&self, _buf: &mut Vec<u8>) {} - fn size(&self) -> usize; -} -impl<'a> ValueLoad<'a> for &'a str { +impl<'a> ValueSer<'a> for &'a str { const TYPE: ValueType = ValueType::String; fn load_unaligned(buf: &'a [u8]) -> Option<Self> { str::from_utf8(buf).ok() } -} -impl ValueStore for &str { - fn get_type(&self) -> ValueType { - ValueType::String - } fn store_unaligned(&self, buf: &mut Vec<u8>) { buf.extend(self.as_bytes()); } @@ -149,16 +177,11 @@ impl ValueStore for &str { self.len() } } -impl ValueLoad<'_> for u32 { +impl ValueSer<'_> for u32 { const TYPE: ValueType = ValueType::UInt; fn load_aligned(buf: &[u32]) -> Option<Self> { buf.get(0).copied().map(u32::from_be) } -} -impl ValueStore for u32 { - fn get_type(&self) -> ValueType { - ValueType::UInt - } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(self.to_be()); } @@ -166,16 +189,11 @@ impl ValueStore for u32 { 4 } } -impl ValueLoad<'_> for Tag { +impl ValueSer<'_> for Tag { const TYPE: ValueType = ValueType::Tag; fn load_aligned(buf: &[u32]) -> Option<Self> { buf.get(0).copied().map(u32::from_be).map(Tag) } -} -impl ValueStore for Tag { - fn get_type(&self) -> ValueType { - ValueType::Tag - } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(self.0.to_be()); } @@ -183,18 +201,13 @@ impl ValueStore for Tag { 4 } } -impl ValueLoad<'_> for u64 { +impl ValueSer<'_> for u64 { const TYPE: ValueType = ValueType::UInt; fn load_aligned(buf: &[u32]) -> Option<Self> { let hi = u32::from_be(*buf.get(0)?) as u64; let lo = u32::from_be(*buf.get(1)?) as u64; Some(hi << 32 | lo) } -} -impl ValueStore for u64 { - fn get_type(&self) -> ValueType { - ValueType::UInt - } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(((self >> 32) as u32).to_be()); buf.push((*self as u32).to_be()); @@ -203,7 +216,7 @@ impl ValueStore for u64 { 8 } } -impl ValueLoad<'_> for f64 { +impl ValueSer<'_> for f64 { const TYPE: ValueType = ValueType::Float; fn load_aligned(buf: &[u32]) -> Option<Self> { if buf.len() < 2 { @@ -215,11 +228,6 @@ impl ValueLoad<'_> for f64 { a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3], ])) } -} -impl ValueStore for f64 { - fn get_type(&self) -> ValueType { - ValueType::Float - } fn store_aligned(&self, buf: &mut Vec<u32>) { let b = self.to_be_bytes(); buf.push(u32::from_be_bytes([b[0], b[1], b[2], b[3]])); @@ -229,16 +237,11 @@ impl ValueStore for f64 { 8 } } -impl ValueLoad<'_> for i64 { +impl ValueSer<'_> for i64 { const TYPE: ValueType = ValueType::Int; fn load_aligned(buf: &[u32]) -> Option<Self> { u64::load_aligned(buf).map(|x| x as i64) } -} -impl ValueStore for i64 { - fn get_type(&self) -> ValueType { - ValueType::Int - } fn store_aligned(&self, buf: &mut Vec<u32>) { (*self as u64).store_aligned(buf); } @@ -246,47 +249,24 @@ impl ValueStore for i64 { 8 } } -impl<'a> ValueLoad<'a> for Object<'a> { +impl<'a> ValueSer<'a> for &'a Object { const TYPE: ValueType = ValueType::Object; fn load_aligned(buf: &'a [u32]) -> Option<Self> { - Self::load(buf) - } -} -impl ValueStore for Object<'_> { - fn get_type(&self) -> ValueType { - ValueType::Object - } - fn store_aligned(&self, buf: &mut Vec<u32>) { - buf.push(self.tags.len() as u32); - buf.extend(self.tags); - buf.extend(self.offsets); - buf.extend(self.values); - } - fn size(&self) -> usize { - (self.tags.len() + self.offsets.len() + self.values.len()) * size_of::<u32>() - } -} -impl ValueStore for ObjectBuffer { - fn get_type(&self) -> ValueType { - ValueType::Object + // TODO validate + Some(slice_to_ob(buf)) } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.extend(&self.0); } fn size(&self) -> usize { - self.0.len() * 4 + self.0.len() * size_of::<u32>() } } -impl<'a> ValueLoad<'a> for &'a [u8] { +impl<'a> ValueSer<'a> for &'a [u8] { const TYPE: ValueType = ValueType::Binary; fn load_unaligned(buf: &'a [u8]) -> Option<Self> { Some(buf) } -} -impl ValueStore for &[u8] { - fn get_type(&self) -> ValueType { - ValueType::Binary - } fn store_unaligned(&self, buf: &mut Vec<u8>) { buf.extend(*self); } @@ -294,16 +274,11 @@ impl ValueStore for &[u8] { self.len() } } -impl<'a> ValueLoad<'a> for () { +impl<'a> ValueSer<'a> for () { const TYPE: ValueType = ValueType::Int; fn load_aligned(_buf: &'a [u32]) -> Option<Self> { Some(()) } -} -impl ValueStore for () { - fn get_type(&self) -> ValueType { - ValueType::Binary - } fn store_aligned(&self, _buf: &mut Vec<u32>) {} fn size(&self) -> usize { 0 diff --git a/common/src/api.rs b/common/src/api.rs index d9f27c5..bf79cc0 100644 --- a/common/src/api.rs +++ b/common/src/api.rs @@ -5,12 +5,13 @@ */ use jellyobject::{Object, Tag}; +use std::borrow::Cow; use std::collections::BTreeMap; -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] pub struct Nku<'a> { - pub node: Object<'a>, - pub userdata: Object<'a>, + pub node: Cow<'a, Object>, + pub userdata: Cow<'a, Object>, pub role: Option<&'a str>, } diff --git a/common/src/internal.rs b/common/src/internal.rs index 5aedfc7..844468d 100644 --- a/common/src/internal.rs +++ b/common/src/internal.rs @@ -10,7 +10,7 @@ use jellyobject::fields; use serde::{Deserialize, Serialize}; fields! { - IM_PATH: &str = b"Ipth"; + IM_PATH: str = b"Ipth"; IM_MTIME: u64 = b"Imtm"; } diff --git a/common/src/lib.rs b/common/src/lib.rs index d94c72c..417acee 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -3,6 +3,7 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ +#![feature(phantom_variance_markers)] pub mod api; pub mod internal; pub mod node; diff --git a/common/src/node.rs b/common/src/node.rs index 92b5a5b..ff3289f 100644 --- a/common/src/node.rs +++ b/common/src/node.rs @@ -10,51 +10,51 @@ fields! { // Tag counter: 111 NO_KIND: Tag = b"kind"; - NO_TITLE: &str = b"titl"; + NO_TITLE: str = b"titl"; NO_PARENT: u64 = b"prnt"; // multi - NO_SUBTITLE: &str = b"sbtl"; - NO_TAGLINE: &str = b"tgln"; - NO_DESCRIPTION: &str = b"desc"; + NO_SUBTITLE: str = b"sbtl"; + NO_TAGLINE: str = b"tgln"; + NO_DESCRIPTION: str = b"desc"; NO_RELEASEDATE: i64 = b"rldt"; NO_DURATION: f64 = b"durn"; NO_INDEX: u64 = b"indx"; NO_SEASON_INDEX: u64 = b"sidx"; NO_TRACK: Object = b"trak"; // multi NO_CHAPTER: Object = b"chpt"; // multi - NO_TAG: &str = b"tag1"; // multi + NO_TAG: str = b"tag1"; // multi NO_RATINGS: Object = b"rtng"; NO_PICTURES: Object = b"pict"; NO_IDENTIFIERS: Object = b"iden"; NO_VISIBILITY: Tag = b"visi"; NO_STORAGE_SIZE: u64 = b"stsz"; NO_CREDIT: Object = b"crdt"; // multi - NO_SLUG: &str = b"slug"; + NO_SLUG: str = b"slug"; NO_METASOURCE: Object = b"msrc"; CR_NODE: u64 = b"node"; CR_KIND: Tag = b"kind"; - CR_ROLE: &str = b"role"; // multi + CR_ROLE: str = b"role"; // multi TR_KIND: Tag = b"kind"; TR_SOURCE: Object = b"sour"; - TR_NAME: &str = b"name"; - TR_CODEC: &str = b"codc"; - TR_LANGUAGE: &str = b"lang"; // BCP 47 + TR_NAME: str = b"name"; + TR_CODEC: str = b"codc"; + TR_LANGUAGE: str = b"lang"; // BCP 47 TR_RATE: f64 = b"rate"; TR_BIT_DEPTH: u32 = b"bdep"; TR_CHANNELS: u32 = b"chnl"; TR_PIXEL_WIDTH: u32 = b"pwid"; TR_PIXEL_HEIGHT: u32 = b"phei"; - TRSOURCE_LOCAL_PATH: &str = b"lpat"; + TRSOURCE_LOCAL_PATH: str = b"lpat"; TRSOURCE_LOCAL_TRACKNUM: u64 = b"ltrn"; CH_START: f64 = b"strt"; CH_END: f64 = b"end1"; - CH_NAME: &str = b"name"; + CH_NAME: str = b"name"; - PICT_COVER: &str = b"covr"; - PICT_BACKDROP: &str = b"bdro"; + PICT_COVER: str = b"covr"; + PICT_BACKDROP: str = b"bdro"; RTYP_IMDB: f64 = b"imdb"; RTYP_TMDB: f64 = b"tmdb"; @@ -65,31 +65,31 @@ fields! { RTYP_YOUTUBE_SUBSCRIBERS: f64 = b"ytsu"; RTYP_TRAKT: f64 = b"trkt"; - IDENT_MUSICBRAINZ_RECORDING: &str = b"mbrc"; - IDENT_MUSICBRAINZ_ARTIST: &str = b"mbar"; - IDENT_MUSICBRAINZ_RELEASE: &str = b"mbrl"; - IDENT_MUSICBRAINZ_RELEASE_GROUP: &str = b"mbrg"; - IDENT_ACOUST_ID_TRACK: &str = b"actr"; - IDENT_YOUTUBE_VIDEO: &str = b"ytvi"; - IDENT_YOUTUBE_CHANNEL: &str = b"ytc1"; - IDENT_YOUTUBE_CHANNEL_HANDLE: &str = b"ytch"; - IDENT_BANDCAMP: &str = b"bcmp"; - IDENT_ISRC: &str = b"isrc"; - IDENT_BARCODE: &str = b"barc"; - IDENT_TRAKT_MOVIE: &str = b"trmv"; - IDENT_TRAKT_SHOW: &str = b"trsh"; - IDENT_TRAKT_SEASON: &str = b"trse"; - IDENT_TRAKT_EPISODE: &str = b"trep"; - IDENT_TRAKT_PERSON: &str = b"trpe"; - IDENT_IMDB: &str = b"imdb"; - IDENT_IMDB_PERSON: &str = b"impe"; - IDENT_TMDB_SERIES: &str = b"tmse"; - IDENT_TMDB_MOVIE: &str = b"tmmv"; - IDENT_TMDB_PERSON: &str = b"tmpe"; - IDENT_TVDB: &str = b"tvdb"; - IDENT_OMDB: &str = b"omdb"; - IDENT_VGMDB_ARTIST: &str = b"vgar"; - IDENT_WIKIDATA: &str = b"wkdt"; + IDENT_MUSICBRAINZ_RECORDING: str = b"mbrc"; + IDENT_MUSICBRAINZ_ARTIST: str = b"mbar"; + IDENT_MUSICBRAINZ_RELEASE: str = b"mbrl"; + IDENT_MUSICBRAINZ_RELEASE_GROUP: str = b"mbrg"; + IDENT_ACOUST_ID_TRACK: str = b"actr"; + IDENT_YOUTUBE_VIDEO: str = b"ytvi"; + IDENT_YOUTUBE_CHANNEL: str = b"ytc1"; + IDENT_YOUTUBE_CHANNEL_HANDLE: str = b"ytch"; + IDENT_BANDCAMP: str = b"bcmp"; + IDENT_ISRC: str = b"isrc"; + IDENT_BARCODE: str = b"barc"; + IDENT_TRAKT_MOVIE: str = b"trmv"; + IDENT_TRAKT_SHOW: str = b"trsh"; + IDENT_TRAKT_SEASON: str = b"trse"; + IDENT_TRAKT_EPISODE: str = b"trep"; + IDENT_TRAKT_PERSON: str = b"trpe"; + IDENT_IMDB: str = b"imdb"; + IDENT_IMDB_PERSON: str = b"impe"; + IDENT_TMDB_SERIES: str = b"tmse"; + IDENT_TMDB_MOVIE: str = b"tmmv"; + IDENT_TMDB_PERSON: str = b"tmpe"; + IDENT_TVDB: str = b"tvdb"; + IDENT_OMDB: str = b"omdb"; + IDENT_VGMDB_ARTIST: str = b"vgar"; + IDENT_WIKIDATA: str = b"wkdt"; } diff --git a/common/src/user.rs b/common/src/user.rs index 6511b10..b83cc9c 100644 --- a/common/src/user.rs +++ b/common/src/user.rs @@ -6,13 +6,13 @@ use jellyobject::{Object, Tag, enums, fields}; -pub type User<'a> = Object<'a>; +pub type User = Object; fields! { - USER_LOGIN: &str = b"Ulgn"; - USER_PASSWORD: &[u8] = b"Upwd"; + USER_LOGIN: str = b"Ulgn"; + USER_PASSWORD: [u8] = b"Upwd"; USER_PASSWORD_REQUIRE_CHANGE: () = b"Upwc"; - USER_NAME: &str = b"Unam"; + USER_NAME: str = b"Unam"; USER_ADMIN: () = b"Uadm"; USER_THEME_PRESET: Tag = b"Utpr"; USER_THEME_ACCENT: u32 = b"Utac"; |