use std::collections::BTreeMap;use anyhow::Context;use gix::{ObjectId, bstr::BStr};use itertools::{Either, Itertools};use rkyv::{Archive, Serialize};use rocksdb::{DB, WriteBatch};use yoke::{Yoke, Yokeable};use super::{Yoked,prefixes::{TREE_FAMILY, TREE_ITEM_FAMILY},};#[derive(Serialize, Archive, Debug, PartialEq, Eq, Hash)]pub struct Tree {pub indexed_tree_id: u64,}impl Tree {pub fn insert(&self,database: &DB,batch: &mut WriteBatch,tree_oid: ObjectId,) -> Result<(), anyhow::Error> {let cf = database.cf_handle(TREE_FAMILY).context("tree column family missing")?;batch.put_cf(cf,tree_oid.as_slice(),rkyv::to_bytes::<rkyv::rancor::Error>(self)?,);Ok(())}pub fn find(database: &DB, tree_oid: ObjectId) -> Result<Option<u64>, anyhow::Error> {let cf = database.cf_handle(TREE_FAMILY).context("tree column family missing")?;let Some(data) = database.get_cf(cf, tree_oid.as_slice())? else {return Ok(None);};let data = rkyv::access::<<Self as Archive>::Archived, rkyv::rancor::Error>(data.as_ref())?;Ok(Some(data.indexed_tree_id.to_native()))}}#[derive(Serialize, Archive, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]#[rkyv(derive(Ord, PartialOrd, Eq, PartialEq, Debug))]#[rkyv(compare(PartialOrd, PartialEq))]pub struct TreeKey(pub String);#[derive(Serialize, Archive, Debug, PartialEq, Eq, Default, Yokeable)]pub struct SortedTree(pub BTreeMap<TreeKey, SortedTreeItem>);impl SortedTree {pub fn insert(&self,digest: u64,database: &DB,batch: &mut WriteBatch,) -> Result<(), anyhow::Error> {let cf = database.cf_handle(TREE_ITEM_FAMILY).context("tree column family missing")?;batch.put_cf(cf,digest.to_ne_bytes(),rkyv::to_bytes::<rkyv::rancor::Error>(self)?,);Ok(())}pub fn get(digest: u64, database: &DB) -> Result<Option<YokedSortedTree>, anyhow::Error> {let cf = database.cf_handle(TREE_ITEM_FAMILY).expect("tree column family missing");database.get_cf(cf, digest.to_ne_bytes())?.map(|data| {Yoke::try_attach_to_cart(data.into_boxed_slice(), |data| {rkyv::access::<_, rkyv::rancor::Error>(data)})}).transpose().context("failed to parse full tree")}}#[derive(Serialize, Archive, Debug, PartialEq, Eq)]#[rkyv(bytecheck(bounds(__C: rkyv::validation::ArchiveContext)),serialize_bounds(__S: rkyv::ser::Writer + rkyv::ser::Allocator, __S::Error: rkyv::rancor::Source),)]pub enum SortedTreeItem {File,Directory(#[rkyv(omit_bounds)] SortedTree),}#[derive(Serialize, Archive, Debug, PartialEq, Eq, Hash)]pub struct Submodule {pub url: String,pub oid: [u8; 20],}#[derive(Serialize, Archive, Debug, PartialEq, Eq, Hash)]pub enum TreeItemKind {Submodule(Submodule),Tree,File,}#[derive(Serialize, Archive, Debug, PartialEq, Eq, Hash, Yokeable)]pub struct TreeItem {pub mode: u16,pub kind: TreeItemKind,}pub type YokedSortedTree = Yoked<&'static <SortedTree as Archive>::Archived>;pub type YokedTreeItem = Yoked<&'static <TreeItem as Archive>::Archived>;pub type YokedTreeItemKey = Yoked<&'static [u8]>;pub type YokedTreeItemKeyUtf8 = Yoked<&'static str>;impl TreeItem {pub fn insert(&self,buffer: &mut Vec<u8>,digest: u64,path: &BStr,database: &DB,batch: &mut WriteBatch,) -> Result<(), anyhow::Error> {let cf = database.cf_handle(TREE_ITEM_FAMILY).context("tree column family missing")?;buffer.clear();buffer.reserve(std::mem::size_of::<u64>() + path.len() + std::mem::size_of::<usize>());buffer.extend_from_slice(&digest.to_ne_bytes());buffer.extend_from_slice(&memchr::memchr_iter(b'/', path).count().to_be_bytes());buffer.extend_from_slice(path.as_ref());batch.put_cf(cf, &buffer, rkyv::to_bytes::<rkyv::rancor::Error>(self)?);Ok(())}pub fn find_exact(database: &DB,digest: u64,path: &[u8],) -> Result<Option<YokedTreeItem>, anyhow::Error> {let cf = database.cf_handle(TREE_ITEM_FAMILY).expect("tree column family missing");let mut buffer = Vec::with_capacity(std::mem::size_of::<u64>() + path.len());buffer.extend_from_slice(&digest.to_ne_bytes());buffer.extend_from_slice(&memchr::memchr_iter(b'/', path).count().to_be_bytes());buffer.extend_from_slice(path);database.get_cf(cf, buffer)?.map(|data| {Yoke::try_attach_to_cart(data.into_boxed_slice(), |data| {rkyv::access::<_, rkyv::rancor::Error>(data)})}).transpose().context("failed to parse tree item")}pub fn find_prefix<'a>(database: &'a DB,digest: u64,prefix: Option<&[u8]>,) -> impl Iterator<Item = Result<(YokedTreeItemKey, YokedTreeItem), anyhow::Error>> + use<'a>{let cf = database.cf_handle(TREE_ITEM_FAMILY).expect("tree column family missing");let (iterator, key) = match prefix {None => {let iterator = database.prefix_iterator_cf(cf, digest.to_ne_bytes());(iterator, Either::Left(Either::Left(digest.to_be_bytes())))}Some([]) => {let mut buffer = [0_u8; std::mem::size_of::<u64>() + std::mem::size_of::<usize>()];buffer[..std::mem::size_of::<u64>()].copy_from_slice(&digest.to_ne_bytes());buffer[std::mem::size_of::<u64>()..].copy_from_slice(&0_usize.to_be_bytes());let iterator = database.prefix_iterator_cf(cf, buffer);(iterator, Either::Left(Either::Right(buffer)))}Some(prefix) => {let mut buffer = Vec::with_capacity(std::mem::size_of::<u64>() + prefix.len() + std::mem::size_of::<usize>(),);buffer.extend_from_slice(&digest.to_ne_bytes());buffer.extend_from_slice(&(memchr::memchr_iter(b'/', prefix).count() + 1).to_be_bytes(),);buffer.extend_from_slice(prefix);buffer.push(b'/');let iterator = database.prefix_iterator_cf(cf, &buffer);(iterator, Either::Right(buffer))}};iterator.take_while(move |v| {v.as_ref().is_ok_and(|(k, _)| {k.starts_with(match key.as_ref() {Either::Left(Either::Right(v)) => v.as_ref(),Either::Left(Either::Left(v)) => v.as_ref(),Either::Right(v) => v.as_ref(),})})}).map_ok(|(key, value)| {let key = Yoke::attach_to_cart(key, |data| {&data[std::mem::size_of::<u64>() + std::mem::size_of::<usize>()..]});let value = Yoke::try_attach_to_cart(value, |data| {rkyv::access::<_, rkyv::rancor::Error>(data)}).context("Failed to open repository")?;Ok((key, value))}).flatten()}pub fn contains(database: &DB, digest: u64) -> Result<bool, anyhow::Error> {let cf = database.cf_handle(TREE_ITEM_FAMILY).context("tree column family missing")?;Ok(database.prefix_iterator_cf(cf, digest.to_ne_bytes()).next().transpose()?.is_some())}}