summaryrefslogtreecommitdiff
path: root/systems/sheet/src/sheet.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-02-24 12:33:51 +0800
committer魏曹先生 <1992414357@qq.com>2026-02-24 12:34:15 +0800
commit2f251facf156b6c89e6be3ba690261556baa02fa (patch)
treeea5aeaff34551575536ea7a46de125ac4293fe59 /systems/sheet/src/sheet.rs
parent554cd69f91bb98eef9033531d9b1c3daee305c53 (diff)
Implement SheetSystem core library
Add IndexSource type for resource addressing and implement mapping system with LocalMapping, Mapping, and MappingBuf types. Create Sheet and SheetData structs for managing sheet data with editing capabilities. Implement binary format serialization/deserialization with reader and writer modules. Add constants for file format layout and comprehensive test suite for roundtrip verification.
Diffstat (limited to 'systems/sheet/src/sheet.rs')
-rw-r--r--systems/sheet/src/sheet.rs375
1 files changed, 374 insertions, 1 deletions
diff --git a/systems/sheet/src/sheet.rs b/systems/sheet/src/sheet.rs
index 54420ab..68c4c78 100644
--- a/systems/sheet/src/sheet.rs
+++ b/systems/sheet/src/sheet.rs
@@ -1 +1,374 @@
-pub struct Sheet {}
+use std::{
+ collections::HashSet,
+ fs::File,
+ path::{Path, PathBuf},
+};
+
+use memmap2::Mmap;
+use tokio::fs;
+
+use crate::{
+ index_source::IndexSource,
+ mapping::{LocalMapping, LocalMappingForward, Mapping, MappingBuf},
+ sheet::{
+ error::{ReadSheetDataError, SheetEditError},
+ reader::{read_mapping, read_sheet_data},
+ writer::convert_sheet_data_to_bytes,
+ },
+};
+
+pub mod constants;
+pub mod error;
+pub mod reader;
+pub mod writer;
+
+#[cfg(test)]
+pub mod test;
+
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct Sheet {
+ /// Sheet Name
+ name: String,
+
+ /// Data in the sheet
+ data: SheetData,
+
+ /// Edit information
+ edit: SheetEdit,
+}
+
+/// Full Sheet information
+///
+/// Used to wrap as a Sheet object for editing and persistence
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct SheetData {
+ /// All local mappings
+ mappings: HashSet<LocalMapping>,
+}
+
+/// Mmap for SheetData
+pub struct SheetDataMmap {
+ mmap: Mmap,
+}
+
+/// Editing state of the Sheet
+///
+/// Stored in the Sheet, records the editing operations that **will** be performed on its SheetData
+/// The content will be cleared after the edits are applied
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct SheetEdit {
+ /// Edit history
+ list: Vec<SheetEditItem>,
+}
+
+#[derive(Default, Debug, Clone, PartialEq)]
+pub enum SheetEditItem {
+ /// Do nothing, this entry is not included in checksum and audit
+ #[default]
+ DoNothing,
+
+ /// Move a Mapping's Node to another Node
+ Move {
+ from_node: Vec<String>,
+ to_node: Vec<String>,
+ },
+
+ /// Swap the positions of two Mapping Nodes
+ Swap {
+ node_a: Vec<String>,
+ node_b: Vec<String>,
+ },
+
+ /// Erase the Mapping pointed to by a Node
+ EraseMapping { node: Vec<String> },
+
+ /// Insert a new Mapping
+ InsertMapping { mapping: LocalMapping },
+
+ /// Replace the IndexSource of a Mapping pointed to by a Node
+ ReplaceSource {
+ node: Vec<String>,
+ source: IndexSource,
+ },
+
+ /// Update the LocalMappingForward of a Mapping pointed to by a Node
+ UpdateForward {
+ node: Vec<String>,
+ forward: LocalMappingForward,
+ },
+}
+
+impl std::fmt::Display for SheetEditItem {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ SheetEditItem::DoNothing => write!(f, ""),
+ SheetEditItem::Move { from_node, to_node } => {
+ write!(
+ f,
+ "Move \"{}\" -> \"{}\"",
+ display_node_helper(from_node),
+ display_node_helper(to_node),
+ )
+ }
+ SheetEditItem::Swap { node_a, node_b } => {
+ write!(
+ f,
+ "Swap \"{}\" <-> \"{}\"",
+ display_node_helper(node_a),
+ display_node_helper(node_b),
+ )
+ }
+ SheetEditItem::EraseMapping { node } => {
+ write!(f, "Earse \"{}\"", display_node_helper(node),)
+ }
+ SheetEditItem::InsertMapping { mapping } => {
+ write!(f, "Insert {}", mapping.to_string())
+ }
+ SheetEditItem::ReplaceSource { node, source } => {
+ write!(
+ f,
+ "Replace \"{}\" => \"{}\"",
+ display_node_helper(node),
+ source.to_string()
+ )
+ }
+ SheetEditItem::UpdateForward { node, forward } => match forward {
+ LocalMappingForward::Latest => {
+ write!(f, "Update \"{}\" => Latest", display_node_helper(node))
+ }
+ LocalMappingForward::Ref { sheet_name } => {
+ write!(
+ f,
+ "Update \"{}\" => Ref(\"{}\")",
+ display_node_helper(node),
+ sheet_name
+ )
+ }
+ LocalMappingForward::Version {
+ version: version_name,
+ } => {
+ write!(
+ f,
+ "Update \"{}\" => Ver(\"{}\")",
+ display_node_helper(node),
+ version_name
+ )
+ }
+ },
+ }
+ }
+}
+
+#[inline(always)]
+fn display_node_helper(n: &Vec<String>) -> String {
+ n.join("/")
+}
+
+impl SheetData {
+ /// Create an empty SheetData
+ pub fn empty() -> Self {
+ Self {
+ mappings: HashSet::new(),
+ }
+ }
+
+ /// Read SheetData completely from the workspace
+ pub async fn full_read(
+ &mut self,
+ sheet_file: impl Into<PathBuf>,
+ ) -> Result<(), ReadSheetDataError> {
+ let file_data = fs::read(sheet_file.into()).await?;
+ let sheet_data = read_sheet_data(file_data.as_slice())?;
+ self.mappings = sheet_data.mappings;
+ Ok(())
+ }
+
+ /// Load MMAP from a Sheet file
+ pub fn mmap<'a>(sheet_file: impl AsRef<Path>) -> std::io::Result<SheetDataMmap> {
+ let file = File::open(sheet_file.as_ref())?;
+
+ // SAFETY: The file has been successfully opened and is managed by the SheetDataMmap wrapper
+ let mmap = unsafe { Mmap::map(&file)? };
+ Ok(SheetDataMmap { mmap })
+ }
+
+ /// Check if a mapping exists in SheetData
+ pub fn contains_mapping(&self, value: &Vec<String>) -> bool {
+ self.mappings.contains(value)
+ }
+
+ /// Read local mapping information from SheetData
+ pub fn read_local_mapping(&self, value: &Vec<String>) -> Option<&LocalMapping> {
+ self.mappings.get(value)
+ }
+
+ /// Wrap SheetData into a Sheet
+ pub fn pack(self, sheet_name: impl Into<String>) -> Sheet {
+ Sheet {
+ name: sheet_name.into(),
+ data: self,
+ edit: SheetEdit { list: Vec::new() },
+ }
+ }
+}
+
+impl SheetDataMmap {
+ /// Load mapping information from MMAP at high speed
+ pub fn mp<'a>(
+ &'a self,
+ node: &[&str],
+ ) -> Result<Option<(Mapping<'a>, LocalMappingForward)>, ReadSheetDataError> {
+ read_mapping(&self.mmap[..], node)
+ }
+
+ /// Load mapping information from Sheet file at high speed and copy into LocalMapping
+ pub fn mp_c<'a>(&self, node: &[&str]) -> Result<Option<LocalMapping>, ReadSheetDataError> {
+ match self.mp(node)? {
+ Some((mapping, forward)) => {
+ // Note:
+ // Regarding the `unwrap()` here:
+ // Data is read from the original SheetData, it cannot produce values longer than `u8::MAX`
+ // It cannot trigger local_mapping's validity check, so it can be safely unwrapped
+ let local_mapping = mapping.to_local_mapping(forward).unwrap();
+
+ Ok(Some(local_mapping))
+ }
+ None => Ok(None),
+ }
+ }
+}
+
+impl Sheet {
+ /// Unpack Sheet into pure data
+ pub fn unpack(self) -> SheetData {
+ self.data
+ }
+
+ /// Check if a mapping exists in the Sheet
+ pub fn contains_mapping(&self, value: &Vec<String>) -> bool {
+ self.data.contains_mapping(value)
+ }
+
+ /// Read local mapping information from Sheet data
+ pub fn read_local_mapping(&self, value: &Vec<String>) -> Option<&LocalMapping> {
+ self.data.read_local_mapping(value)
+ }
+
+ /// Read from Sheet data and clone into MappingBuf
+ pub fn read_mapping_buf(&self, value: &Vec<String>) -> Option<MappingBuf> {
+ match self.read_local_mapping(value) {
+ Some(v) => Some(v.to_mapping_buf_cloned(&self.name)),
+ None => None,
+ }
+ }
+
+ /// Insert mapping modification
+ pub fn insert_mapping(
+ &mut self,
+ mapping: impl Into<LocalMapping>,
+ ) -> Result<(), SheetEditError> {
+ self.edit.list.push(SheetEditItem::InsertMapping {
+ mapping: mapping.into(),
+ });
+ Ok(())
+ }
+
+ /// Insert mapping erasure
+ pub fn earse_mapping(&mut self, node: Vec<String>) -> Result<(), SheetEditError> {
+ self.edit.list.push(SheetEditItem::EraseMapping { node });
+ Ok(())
+ }
+
+ /// Insert mapping swap
+ pub fn swap_mapping(
+ &mut self,
+ node_a: Vec<String>,
+ node_b: Vec<String>,
+ ) -> Result<(), SheetEditError> {
+ self.edit.list.push(SheetEditItem::Swap { node_a, node_b });
+ Ok(())
+ }
+
+ /// Insert mapping move
+ pub fn move_mapping(
+ &mut self,
+ from_node: Vec<String>,
+ to_node: Vec<String>,
+ ) -> Result<(), SheetEditError> {
+ self.edit
+ .list
+ .push(SheetEditItem::Move { from_node, to_node });
+ Ok(())
+ }
+
+ /// Replace source
+ pub fn replace_source(
+ &mut self,
+ node: Vec<String>,
+ source: IndexSource,
+ ) -> Result<(), SheetEditError> {
+ self.edit
+ .list
+ .push(SheetEditItem::ReplaceSource { node, source });
+ Ok(())
+ }
+
+ /// Update forward
+ pub fn update_forward(
+ &mut self,
+ node: Vec<String>,
+ forward: LocalMappingForward,
+ ) -> Result<(), SheetEditError> {
+ self.edit
+ .list
+ .push(SheetEditItem::UpdateForward { node, forward });
+ Ok(())
+ }
+
+ /// Apply changes
+ pub fn apply(&mut self) {
+ // Logic for applying changes
+ todo!();
+
+ // Clear the edit list
+ #[allow(unreachable_code)] // Note: Remove after todo!() is completed
+ self.edit.list.clear();
+ }
+}
+
+// Implement the as_bytes function for SheetData
+
+impl SheetData {
+ /// Convert SheetData to byte data for storage in the file system
+ pub fn as_bytes(self) -> Vec<u8> {
+ convert_sheet_data_to_bytes(self)
+ }
+}
+
+impl From<SheetData> for Vec<u8> {
+ fn from(value: SheetData) -> Self {
+ value.as_bytes()
+ }
+}
+
+impl From<&SheetData> for Vec<u8> {
+ fn from(value: &SheetData) -> Self {
+ value.clone().as_bytes()
+ }
+}
+
+impl TryFrom<Vec<u8>> for SheetData {
+ type Error = ReadSheetDataError;
+
+ fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
+ read_sheet_data(value.as_slice())
+ }
+}
+
+impl TryFrom<&[u8]> for SheetData {
+ type Error = ReadSheetDataError;
+
+ fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+ read_sheet_data(value)
+ }
+}