//! PageAggregate 构建器(Builder 模式)。 //! //! 使用方法: //! ```rust,ignore //! let aggregate = PageAggregate::builder() //! .document_id("doc_1") //! .workspace_id("ws_demo") //! .title("我的页面") //! .build(); //! ``` use crate::page_aggregate::{ PageAggregate, PageAggregateSource, PageBody, PageHead, PageIdentity, PageLayout, PageOptions, PagePermissions, PageStats, PageTree, }; use serde_json::{to_value, Value}; /// PageAggregate 构建器。 /// /// 所有字段都有与当前 `build_page_aggregate_snapshot` 一致的默认值。 /// 使用 `build()` 方法消费 builder 并产出 `PageAggregate`。 #[derive(Debug)] pub struct PageAggregateBuilder { document_id: String, workspace_id: String, source: PageAggregateSource, parent_id: Option, path: Vec, sidebar_tree_membership: Vec, body_ref: Option, title: String, updated_at: Value, read_only: bool, disable_download: bool, disable_copy: bool, wide_layout: bool, small_text: bool, layout_density: String, show_heading_numbers: bool, show_toc: bool, show_structure: bool, protect_editing: bool, show_word_count: bool, collapse_backlinks: bool, page_font: String, hide_child_pages: bool, show_block_ref_count: bool, embed_default_block_id: Value, content: Value, revision: Value, conflict_detection_key: Value, page_subtree: Value, word_count: u64, character_count: u64, block_count: u64, todo_total: u64, todo_done: u64, } impl PageAggregateBuilder { /// 创建新的构建器,所有字段使用与当前 `build_page_aggregate_snapshot` 一致的默认值。 pub fn new() -> Self { Self { document_id: String::new(), workspace_id: "default".to_string(), source: PageAggregateSource::CompatMetaContentJoin, parent_id: None, path: Vec::new(), sidebar_tree_membership: vec!["page-tree".into(), "file-tree".into()], body_ref: None, title: "无标题".to_string(), updated_at: Value::Null, read_only: false, disable_download: false, disable_copy: false, wide_layout: false, small_text: false, layout_density: "normal".to_string(), show_heading_numbers: true, show_toc: false, show_structure: false, protect_editing: false, show_word_count: true, collapse_backlinks: false, page_font: "default".to_string(), hide_child_pages: false, show_block_ref_count: false, embed_default_block_id: Value::Null, content: Value::Null, revision: Value::Null, conflict_detection_key: Value::Null, page_subtree: Value::Null, word_count: 0, character_count: 0, block_count: 0, todo_total: 0, todo_done: 0, } } // ── Identity ── pub fn document_id(mut self, value: impl Into) -> Self { self.document_id = value.into(); self } pub fn workspace_id(mut self, value: impl Into) -> Self { self.workspace_id = value.into(); self } pub fn source(mut self, value: PageAggregateSource) -> Self { self.source = value; self } pub fn parent_id(mut self, value: Option) -> Self { self.parent_id = value; self } pub fn path(mut self, value: Vec) -> Self { self.path = value; self } pub fn sidebar_tree_membership(mut self, value: Vec) -> Self { self.sidebar_tree_membership = value; self } pub fn body_ref(mut self, value: Option) -> Self { self.body_ref = value; self } // ── Head ── pub fn title(mut self, value: impl Into) -> Self { self.title = value.into(); self } pub fn updated_at(mut self, value: Value) -> Self { self.updated_at = value; self } pub fn read_only(mut self, value: bool) -> Self { self.read_only = value; self } pub fn disable_download(mut self, value: bool) -> Self { self.disable_download = value; self } pub fn disable_copy(mut self, value: bool) -> Self { self.disable_copy = value; self } // ── Layout / PageOptions ── pub fn wide_layout(mut self, value: bool) -> Self { self.wide_layout = value; self } pub fn small_text(mut self, value: bool) -> Self { self.small_text = value; self } pub fn layout_density(mut self, value: impl Into) -> Self { self.layout_density = value.into(); self } pub fn show_heading_numbers(mut self, value: bool) -> Self { self.show_heading_numbers = value; self } pub fn show_toc(mut self, value: bool) -> Self { self.show_toc = value; self } pub fn show_structure(mut self, value: bool) -> Self { self.show_structure = value; self } pub fn protect_editing(mut self, value: bool) -> Self { self.protect_editing = value; self } pub fn show_word_count(mut self, value: bool) -> Self { self.show_word_count = value; self } pub fn collapse_backlinks(mut self, value: bool) -> Self { self.collapse_backlinks = value; self } pub fn page_font(mut self, value: impl Into) -> Self { self.page_font = value.into(); self } pub fn hide_child_pages(mut self, value: bool) -> Self { self.hide_child_pages = value; self } pub fn show_block_ref_count(mut self, value: bool) -> Self { self.show_block_ref_count = value; self } pub fn embed_default_block_id(mut self, value: Value) -> Self { self.embed_default_block_id = value; self } // ── Body ── pub fn content(mut self, value: Value) -> Self { self.content = value; self } pub fn revision(mut self, value: Value) -> Self { self.revision = value; self } pub fn conflict_detection_key(mut self, value: Value) -> Self { self.conflict_detection_key = value; self } // ── Tree ── pub fn page_subtree(mut self, value: Value) -> Self { self.page_subtree = value; self } // ── Stats ── pub fn word_count(mut self, value: u64) -> Self { self.word_count = value; self } pub fn character_count(mut self, value: u64) -> Self { self.character_count = value; self } pub fn block_count(mut self, value: u64) -> Self { self.block_count = value; self } pub fn todo_total(mut self, value: u64) -> Self { self.todo_total = value; self } pub fn todo_done(mut self, value: u64) -> Self { self.todo_done = value; self } // ── Build ── /// 消费 builder 并产出 `PageAggregate`。 pub fn build(self) -> PageAggregate { let page_options = PageOptions { wide_layout: self.wide_layout, small_text: self.small_text, layout_density: self.layout_density, show_heading_numbers: self.show_heading_numbers, show_toc: self.show_toc, show_structure: self.show_structure, protect_editing: self.protect_editing, show_word_count: self.show_word_count, collapse_backlinks: self.collapse_backlinks, page_font: self.page_font, hide_child_pages: self.hide_child_pages, show_block_ref_count: self.show_block_ref_count, embed_default_block_id: self.embed_default_block_id, }; let layout_options = to_value(&page_options).expect("PageOptions 序列化不应失败"); let updated_at_text = self.updated_at.as_str().map(ToOwned::to_owned); let page_id = self.document_id.clone(); PageAggregate { schema: "mnote.page_aggregate.v1".to_string(), projection_version: PageAggregate::VERSION, source: self.source, page_id, parent_id: self.parent_id, title: self.title.clone(), path: if self.path.is_empty() { vec![self.document_id.clone()] } else { self.path }, sidebar_tree_membership: self.sidebar_tree_membership, body_ref: self.body_ref, layout_options, updated_at: updated_at_text, identity: PageIdentity { document_id: self.document_id, workspace_id: self.workspace_id, }, head: PageHead { title: self.title, updated_at: self.updated_at, permissions: PagePermissions { read_only: self.read_only, disable_download: self.disable_download, disable_copy: self.disable_copy, }, }, layout: PageLayout { page_options }, body: PageBody { content: self.content, revision: self.revision, conflict_detection_key: self.conflict_detection_key, }, tree: PageTree { page_subtree: self.page_subtree, }, stats: PageStats { word_count: self.word_count, character_count: self.character_count, block_count: self.block_count, todo_total: self.todo_total, todo_done: self.todo_done, }, } } } impl Default for PageAggregateBuilder { fn default() -> Self { Self::new() } }