This removes the pijul_extension::channel_state module and refactors the authors module to iterate the change log by itself on startup.
37JT3GCX7BSR2SEC7MRTN77CIQQWR2MCAZLZSNSJZU5AIWQ2CEVQC use std::cell::OnceCell;use std::collections::HashMap;use libpijul::changestore::ChangeStore;use libpijul::pristine::TxnErr;use libpijul::pristine::sanakirja::{MutTxn, SanakirjaError};#[derive(Debug, thiserror::Error)]pub enum ChannelStateError<C: std::error::Error + 'static> {#[error("unable to iterate changes: {0:#?}")]ReverseLog(SanakirjaError),#[error("encountered error while iterating log: {0:#?}")]Item(SanakirjaError),#[error("unable to get external hash: {0:#?}")]InternalHash(TxnErr<SanakirjaError>),#[error("unable to read changes: {0:#?}")]ChangeStore(C),}pub struct ChannelState {root_change: ChangeId,}impl ChannelState {pub fn new<C>(transaction: &ArcTxn<MutTxn<()>>,channel: &ChannelRef<MutTxn<()>>,change_store: &C,) -> Result<Self, ChannelStateError<C::Error>>whereC: ChangeStore + Clone + Send + 'static,{let mut changes = HashMap::new();let mut root_change = OnceCell::new();// TODO: authors// let mut authors = HashSet::new();let read_transaction = transaction.read();let mut reverse_log = read_transaction.reverse_log(&*channel.read(), None).map_err(ChannelStateError::ReverseLog)?.peekable();while let Some(log_result) = reverse_log.next() {// TODO: merklelet (_, (hash, merkle)) = log_result.map_err(ChannelStateError::Item)?;let change_id = *read_transaction.get_internal(hash).map_err(ChannelStateError::InternalHash)?.unwrap();// TODO: `change_id.is_root()` is broken?if reverse_log.peek().is_none() {root_change.set(change_id).unwrap();} else {let change_header = change_store.map_err(ChannelStateError::ChangeStore)?;changes.try_insert(change_id, change_header).unwrap();}}Ok(ChannelState {root_change: root_change.take().unwrap(),changes,})}self.changes.get(change_id)}pub fn iter_change_headers(&self) -> impl Iterator<Item = &ChangeHeader> {}}self.changes.values().map(|change| &change.header)pub fn find_vertex_hunk(&self,vertex: Vertex<ChangeId>,) -> Option<(usize, &BaseHunk<Atom<Option<Hash>>, Local>)> {let change = self.changes.get(&vertex.change)?;change.changes.iter().enumerate().find_map(|(index, hunk)| {if hunk.iter().any(|atom| match atom {Atom::NewVertex(new_vertex) => {// Changes that depend on this one may later fragment the original contents,// so the current vertex may be a subset of the original contents.new_vertex.start <= vertex.start && new_vertex.end >= vertex.end}Atom::EdgeMap(_edge_map) => false,}) {// Hunks are 1-indexedSome((index + 1, hunk))} else {None}})}pub fn get_change(&self, change_id: &ChangeId) -> Option<&Change> {.get_change(&hash.into())// TODO: lazy access from ChangeStore?changes: HashMap<ChangeId, Change>,use libpijul::{ArcTxn, ChangeId, ChannelRef, GraphTxnT, Hash, TxnTExt, Vertex};use libpijul::change::{Atom, BaseHunk, Change, ChangeHeader, Local};
pub enum GetChangeError<C: std::error::Error + 'static> {#[error("Unable to begin transaction: {0}")]Transaction(#[from] SanakirjaError),#[error("Unable to get external hash: {0}")]Hash(#[from] TxnErr<SanakirjaError>),#[error("Failed to get change: {0}")]Change(C),#[error("No matching change for {change_id:?}")]NoMatchingChange { change_id: ChangeId },}#[derive(Debug, thiserror::Error)]pub enum FindVertexHunkError<C: std::error::Error + 'static> {#[error("Unable to get change: {0}")]Change(#[from] GetChangeError<C>),#[error("No hunk found for vertex: {vertex:?}")]NoHunkForVertex { vertex: Vertex<ChangeId> },}#[derive(Debug, thiserror::Error)]
#[error("Change not found: {change_id:?}")]ChangeNotFound { change_id: ChangeId },#[error("No hunk found for vertex: {vertex:?}")]NoHunkForVertex { vertex: Vertex<ChangeId> },
#[error("Failed to get change for {change_id:?}: {error}")]Change {change_id: ChangeId,error: GetChangeError<C>,},#[error("Unable to find hunk for vertex: {0}")]FindVertexHunk(#[from] FindVertexHunkError<C>),
pub fn get_change(&self, change_id: &ChangeId) -> Option<&Change> {self.channel_state.get_change(change_id)
pub fn get_change(&self, change_id: ChangeId) -> Result<Change, GetChangeError<C::Error>> {let transaction = self.pristine.arc_txn_begin()?;let read_transaction = transaction.read();let external_hash = read_transaction.get_external(&change_id)?.ok_or(GetChangeError::NoMatchingChange { change_id })?;self.change_store.get_change(&external_hash.into()).map_err(GetChangeError::Change)}pub fn find_vertex_hunk(&self,vertex: Vertex<ChangeId>,) -> Result<(usize,BaseHunk<Atom<Option<libpijul::Hash>>, libpijul::change::Local>,),FindVertexHunkError<C::Error>,> {let change = self.get_change(vertex.change)?;change.hashed.changes.into_iter().enumerate().find_map(|(index, hunk)| {hunk.iter().any(|atom| match atom {Atom::NewVertex(new_vertex) => {// Changes that depend on this one may later fragment the original contents,// so the current vertex may be a subset of the original contents.new_vertex.start <= vertex.start && new_vertex.end >= vertex.end}Atom::EdgeMap(_edge_map) => false,})// Hunks are 1-indexed.then_some((index + 1, hunk))}).ok_or(FindVertexHunkError::NoHunkForVertex { vertex })
let Some(change) = self.channel_state.get_change(&vertex.change) else {return Err(ActiveHunkError::ChangeNotFound {change_id: vertex.change,});};
let change =self.get_change(vertex.change).map_err(|error| ActiveHunkError::Change {change_id: vertex.change,error,})?;
CreditSource::Tracked { vertex } => self.channel_state.find_vertex_hunk(vertex).ok_or_else(|| ActiveHunkError::NoHunkForVertex { vertex })?,
CreditSource::Tracked { vertex } => {let (index, hunk) = self.find_vertex_hunk(vertex)?;(index, Cow::Owned(hunk))}
}#[derive(Debug, thiserror::Error)]pub enum GetAuthorsError<C: std::error::Error + 'static> {#[error("Unable to get log of changes: {0}")]Log(SanakirjaError),#[error("Error while iterating log: {0}")]LogEntry(SanakirjaError),#[error("Failed to get change header for {hash:?}: {error}")]ChangeHeader { hash: SerializedHash, error: C },
pub fn new(channel_state: &ChannelState, dot_directory: &Utf8Path) -> Self {let mut authors = HashMap::new();let repository_identities_path = dot_directory.join("identities");
pub fn new<C>(dot_directory: &Utf8Path,transaction: &ArcTxn<MutTxn<()>>,channel: &ChannelRef<MutTxn<()>>,change_store: &C,) -> Result<Self, GetAuthorsError<C::Error>>whereC: ChangeStore + Clone + Send + 'static,{let mut authors = Self {authors: HashMap::new(),};// Get local identitiesauthors.insert_local_identities();// Get identities stored in the repositoryauthors.insert_repository_identities(dot_directory, transaction, channel, change_store)?;Ok(authors)}
for change_header in channel_state.iter_change_headers() {
fn insert_repository_identities<C>(&mut self,dot_directory: &Utf8Path,transaction: &ArcTxn<MutTxn<()>>,channel: &ChannelRef<MutTxn<()>>,change_store: &C,) -> Result<(), GetAuthorsError<C::Error>>whereC: ChangeStore + Clone + Send + 'static,{let repository_identities_path = dot_directory.join("identities");let read_transaction = transaction.read();let log_entries = read_transaction.log(&*channel.read(), 0).map_err(GetAuthorsError::Log)?;for log_entry in log_entries {let (_index, (serialized_hash, _merkle)) =log_entry.map_err(GetAuthorsError::LogEntry)?;let change_header =change_store.get_header(&serialized_hash.into()).map_err(|error| GetAuthorsError::ChangeHeader {hash: *serialized_hash,error,})?;
let change = repository.get_change(&vertex.change).ok_or_else(|| {napi::Error::from_reason(format!("Unable to get change for verte {vertex:#?}"))
let change = repository.get_change(vertex.change).map_err(|error| {napi::Error::from_reason(format!("Unable to get change for vertex {vertex:#?}: {error}"))
let change = repository.get_change(&vertex.change).ok_or_else(|| {napi::Error::from_reason(format!("Unable to get change for vertex: {vertex:#?}"))
let change = repository.get_change(vertex.change).map_err(|error| {napi::Error::from_reason(format!("Unable to get change for vertex {vertex:#?}: {error}"))