/* 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 */ use crate::{Object, Tag, slice_to_ob}; use core::{marker::Sized, unimplemented}; use std::{borrow::Cow, fmt::Display, str::FromStr}; 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 { unimplemented!() } fn load_unaligned(_buf: &'a [u8]) -> Option { unimplemented!() } fn store_aligned(&self, _buf: &mut Vec) { unimplemented!() } fn store_unaligned(&self, _buf: &mut Vec) { unimplemented!() } fn size(&self) -> usize; } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ValueType { Binary, String, Object, UInt, Int, Tag, Float, Bool, } impl ValueType { #[inline] pub const fn is_aligned(self) -> bool { match self { ValueType::Binary | ValueType::String => false, _ => true, } } pub fn from_num(n: u32) -> Self { match n { 0 => Self::Binary, 1 => Self::String, 2 => Self::Object, 3 => Self::UInt, 4 => Self::Int, 5 => Self::Tag, 6 => Self::Float, 7 => Self::Bool, _ => unreachable!(), } } } #[derive(Debug, Clone, PartialEq)] pub enum Value<'a> { Tag(Tag), U32(u32), U64(u64), I64(i64), String(Cow<'a, str>), Binary(Cow<'a, [u8]>), } impl<'a> From<&'a str> for Value<'a> { fn from(value: &'a str) -> Self { Self::String(value.into()) } } impl From for Value<'static> { fn from(value: String) -> Self { Self::String(value.into()) } } impl From for Value<'static> { fn from(value: u32) -> Self { Self::U32(value) } } impl From for Value<'static> { fn from(value: u64) -> Self { Self::U64(value) } } impl From for Value<'static> { fn from(value: i64) -> Self { Self::I64(value) } } impl From for Value<'static> { fn from(value: Tag) -> Self { Self::Tag(value) } } impl Display for Value<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Value::Tag(tag) => write!(f, "{tag}"), Value::U32(x) => write!(f, "{x}"), Value::U64(x) => write!(f, "{x}"), Value::I64(x) => write!(f, "{x}"), Value::String(x) => write!(f, "{x:?}"), Value::Binary(x) => write!(f, "{x:?}"), } } } impl FromStr for Value<'static> { type Err = &'static str; fn from_str(s: &str) -> Result { Ok(if s.len() == 4 { Value::Tag(Tag::new(s.as_bytes().try_into().unwrap())) } else if let Some(s) = s.strip_suffix("i64") { Value::I64(s.parse().map_err(|_| "invalid i64 literal")?) } else if let Some(s) = s.strip_suffix("u64") { Value::U64(s.parse().map_err(|_| "invalid u64 literal")?) } else if let Some(s) = s.strip_suffix("u32") { Value::U32(s.parse().map_err(|_| "invalid u32 literal")?) } else if let Some(s) = s.strip_circumfix("\"", "\"") { Value::String(s.to_owned().into()) } else { return Err("invalid value literal"); }) } } impl<'a> ValueSer<'a> for &'a str { const TYPE: ValueType = ValueType::String; fn load_unaligned(buf: &'a [u8]) -> Option { str::from_utf8(buf).ok() } fn store_unaligned(&self, buf: &mut Vec) { buf.extend(self.as_bytes()); } fn size(&self) -> usize { self.len() } } impl ValueSer<'_> for u32 { const TYPE: ValueType = ValueType::UInt; fn load_aligned(buf: &[u32]) -> Option { buf.get(0).copied().map(u32::from_be) } fn store_aligned(&self, buf: &mut Vec) { buf.push(self.to_be()); } fn size(&self) -> usize { 4 } } impl ValueSer<'_> for Tag { const TYPE: ValueType = ValueType::Tag; fn load_aligned(buf: &[u32]) -> Option { buf.get(0).copied().map(u32::from_be).map(Tag) } fn store_aligned(&self, buf: &mut Vec) { buf.push(self.0.to_be()); } fn size(&self) -> usize { 4 } } impl ValueSer<'_> for u64 { const TYPE: ValueType = ValueType::UInt; fn load_aligned(buf: &[u32]) -> Option { let hi = u32::from_be(*buf.get(0)?) as u64; let lo = u32::from_be(*buf.get(1)?) as u64; Some(hi << 32 | lo) } fn store_aligned(&self, buf: &mut Vec) { buf.push(((self >> 32) as u32).to_be()); buf.push((*self as u32).to_be()); } fn size(&self) -> usize { 8 } } impl ValueSer<'_> for f64 { const TYPE: ValueType = ValueType::Float; fn load_aligned(buf: &[u32]) -> Option { if buf.len() < 2 { return None; }; let a = u32::to_be_bytes(buf[0]); let b = u32::to_be_bytes(buf[1]); Some(f64::from_be_bytes([ a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3], ])) } fn store_aligned(&self, buf: &mut Vec) { let b = self.to_be_bytes(); buf.push(u32::from_be_bytes([b[0], b[1], b[2], b[3]])); buf.push(u32::from_be_bytes([b[4], b[5], b[6], b[7]])); } fn size(&self) -> usize { 8 } } impl ValueSer<'_> for i64 { const TYPE: ValueType = ValueType::Int; fn load_aligned(buf: &[u32]) -> Option { u64::load_aligned(buf).map(|x| x as i64) } fn store_aligned(&self, buf: &mut Vec) { (*self as u64).store_aligned(buf); } fn size(&self) -> usize { 8 } } impl<'a> ValueSer<'a> for &'a Object { const TYPE: ValueType = ValueType::Object; fn load_aligned(buf: &'a [u32]) -> Option { // TODO validate Some(slice_to_ob(buf)) } fn store_aligned(&self, buf: &mut Vec) { buf.extend(&self.0); } fn size(&self) -> usize { self.0.len() * size_of::() } } impl<'a> ValueSer<'a> for &'a [u8] { const TYPE: ValueType = ValueType::Binary; fn load_unaligned(buf: &'a [u8]) -> Option { Some(buf) } fn store_unaligned(&self, buf: &mut Vec) { buf.extend(*self); } fn size(&self) -> usize { self.len() } } impl<'a> ValueSer<'a> for () { const TYPE: ValueType = ValueType::Int; fn load_aligned(_buf: &'a [u32]) -> Option { Some(()) } fn store_aligned(&self, _buf: &mut Vec) {} fn size(&self) -> usize { 0 } }