use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::str::FromStr;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectId(pub [u8; 16]);
impl ProjectId {
pub const GLOBAL: Self = ProjectId([0u8; 16]);
pub fn from_path(path: &str) -> Self {
let hash = blake3::hash(path.as_bytes());
let mut bytes = [0u8; 16];
bytes.copy_from_slice(&hash.as_bytes()[..16]);
ProjectId(bytes)
}
pub fn is_global(&self) -> bool {
self.0 == [0u8; 16]
}
}
impl fmt::Display for ProjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl FromStr for ProjectId {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case("global") {
return Ok(ProjectId::GLOBAL);
}
let s = s.trim_start_matches("0x");
if s.len() != 32 {
return Err(format!("ProjectId must be 32 hex chars, got {}", s.len()));
}
let mut bytes = [0u8; 16];
for (i, chunk) in s.as_bytes().chunks(2).enumerate() {
let chunk =
std::str::from_utf8(chunk).map_err(|_| "invalid utf-8 in hex".to_string())?;
bytes[i] = u8::from_str_radix(chunk, 16).map_err(|e| format!("invalid hex: {}", e))?;
}
Ok(ProjectId(bytes))
}
}
impl Serialize for ProjectId {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for ProjectId {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
ProjectId::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct BlockHeader {
pub x: f32,
pub y: f32,
pub z: f32,
pub zoom: f32,
pub depth: u8,
pub layer_id: u8,
pub data_offset: u32,
pub data_len: u16,
pub parent_idx: u32,
pub child_count: u16,
pub crc16: [u8; 2],
pub project_id: ProjectId,
pub importance: u8,
pub flags: u8,
}
pub type MemoryBlockHeader = BlockHeader;
impl BlockHeader {
#[inline(always)]
pub fn is_visible_to(&self, active: ProjectId, include_global: bool) -> bool {
self.project_id == active || (include_global && self.project_id.is_global())
}
}
#[derive(Clone)]
pub struct MemoryQueryOptions {
pub active_project: ProjectId,
pub include_global: bool,
pub min_importance: u8,
pub max_results: usize,
}
impl Default for MemoryQueryOptions {
fn default() -> Self {
Self {
active_project: ProjectId::GLOBAL,
include_global: true,
min_importance: 0,
max_results: usize::MAX,
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct MetaHeader {
pub magic: [u8; 4],
pub version: u32,
pub block_count: u32,
pub depth_count: u32,
}
pub struct AppendEntry {
pub text: String,
pub layer_id: u8,
pub importance: u8,
pub depth: u8,
pub x: f32,
pub y: f32,
pub z: f32,
pub emotion: [f32; 21],
pub project_id: ProjectId,
}