aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-02-27 20:56:20 +0100
committermetamuffin <metamuffin@disroot.org>2026-02-27 20:56:20 +0100
commit7930d543a2aa68d4ad2958605827d7eb1baa91f8 (patch)
treefe59d1f549e303a96b78d3e925d75abb70b73af0 /common
parentc05bfcc2775f0e11db6e856bfcf06d0419c35d54 (diff)
downloadjellything-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.rs76
-rw-r--r--common/object/src/debug.rs14
-rw-r--r--common/object/src/json.rs4
-rw-r--r--common/object/src/lib.rs276
-rw-r--r--common/object/src/path.rs18
-rw-r--r--common/object/src/registry.rs39
-rw-r--r--common/object/src/tag.rs73
-rw-r--r--common/object/src/tests.rs36
-rw-r--r--common/object/src/value.rs139
-rw-r--r--common/src/api.rs7
-rw-r--r--common/src/internal.rs2
-rw-r--r--common/src/lib.rs1
-rw-r--r--common/src/node.rs78
-rw-r--r--common/src/user.rs8
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";