chunked-wal 0.2.0

Chunked write-ahead log implementation
Documentation
use std::fmt;
use std::fs::File;
use std::sync::Arc;

use crate::ChunkId;
use crate::WalTypes;
use crate::wal::file_persisted::ChunkPersistedCallback;

pub(crate) struct FileEntry<W>
where W: WalTypes
{
    pub(crate) starting_offset: u64,
    pub(crate) f: Arc<File>,

    /// Called after this file has been successfully synced.
    ///
    /// Receives the file, its starting offset, and the synced offset.
    /// The callback may be called multiple times.
    pub(crate) on_persisted: ChunkPersistedCallback<W>,
    /// for debug
    pub(crate) sync_id: u64,
}

impl<W> fmt::Display for FileEntry<W>
where W: WalTypes
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "FileEntry{{ starting_offset: {}, sync_id: {} }}",
            ChunkId(self.starting_offset),
            self.sync_id
        )
    }
}

impl<W> fmt::Debug for FileEntry<W>
where W: WalTypes
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FileEntry")
            .field("starting_offset", &ChunkId(self.starting_offset))
            .field("sync_id", &self.sync_id)
            .finish()
    }
}

impl<W> FileEntry<W>
where W: WalTypes
{
    pub(crate) fn new(
        starting_offset: u64,
        f: Arc<File>,
        on_persisted: ChunkPersistedCallback<W>,
    ) -> Self {
        Self {
            starting_offset,
            f,
            on_persisted,
            sync_id: 0,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::sync::Arc;
    use std::sync::mpsc::SyncSender;

    use crate::WalTypes;
    use crate::wal::file_entry::FileEntry;
    use crate::wal::file_persisted::ChunkPersistedCallback;
    use crate::wal::file_persisted::ChunkPersistedFn;

    #[derive(Debug, Default, Clone, PartialEq, Eq)]
    struct TestWal;

    impl WalTypes for TestWal {
        type Action = String;
        type Checkpoint = String;
        type Callback = SyncSender<Result<(), io::Error>>;
    }

    fn callback() -> ChunkPersistedCallback<TestWal> {
        let cb: ChunkPersistedFn<TestWal> = Arc::new(|_persisted, _state| {});
        ChunkPersistedCallback::new(cb, None)
    }

    #[test]
    fn test_file_entry_new_and_format() -> Result<(), io::Error> {
        let file = Arc::new(tempfile::tempfile()?);
        let entry = FileEntry::<TestWal>::new(12, file.clone(), callback());

        assert_eq!(12, entry.starting_offset);
        assert_eq!(0, entry.sync_id);
        assert!(Arc::ptr_eq(&file, &entry.f));
        assert_eq!(
            "FileEntry{ starting_offset: ChunkId(00_000_000_000_000_000_012), sync_id: 0 }",
            entry.to_string()
        );
        assert_eq!(
            "FileEntry { starting_offset: ChunkId(12), sync_id: 0 }",
            format!("{entry:?}")
        );

        Ok(())
    }
}