quillmark_core/document/
edit.rs1use unicode_normalization::UnicodeNormalization;
14
15use crate::document::meta::{validate_composable_kind, CardKindError};
16use crate::document::{Card, Document, Payload};
17use crate::value::QuillValue;
18use crate::version::QuillReference;
19
20pub fn is_valid_field_name(name: &str) -> bool {
22 let normalized: String = name.nfc().collect();
23 if normalized.is_empty() {
24 return false;
25 }
26 let mut chars = normalized.chars();
27 let first = chars.next().unwrap();
28 if !first.is_ascii_lowercase() && first != '_' {
29 return false;
30 }
31 for ch in chars {
32 if !ch.is_ascii_lowercase() && !ch.is_ascii_digit() && ch != '_' {
33 return false;
34 }
35 }
36 true
37}
38
39#[derive(Debug, Clone, PartialEq, thiserror::Error)]
41pub enum EditError {
42 #[error("invalid field name '{0}': must match [a-z_][a-z0-9_]*")]
43 InvalidFieldName(String),
44
45 #[error("invalid card kind '{0}': must match [a-z_][a-z0-9_]*")]
46 InvalidKindName(String),
47
48 #[error("card kind 'main' is reserved for the document root")]
49 ReservedKind,
50
51 #[error("index {index} is out of range (len = {len})")]
52 IndexOutOfRange { index: usize, len: usize },
53}
54
55impl Document {
56 pub fn set_quill_ref(&mut self, reference: QuillReference) {
57 self.main_mut().payload_mut().set_quill(reference);
58 }
59
60 pub fn card_mut(&mut self, index: usize) -> Option<&mut Card> {
61 self.cards_mut().get_mut(index)
62 }
63
64 pub fn push_card(&mut self, card: Card) {
65 self.cards_vec_mut().push(card);
66 }
67
68 pub fn insert_card(&mut self, index: usize, card: Card) -> Result<(), EditError> {
70 let len = self.cards().len();
71 if index > len {
72 return Err(EditError::IndexOutOfRange { index, len });
73 }
74 self.cards_vec_mut().insert(index, card);
75 Ok(())
76 }
77
78 pub fn remove_card(&mut self, index: usize) -> Option<Card> {
79 if index >= self.cards().len() {
80 return None;
81 }
82 Some(self.cards_vec_mut().remove(index))
83 }
84
85 pub fn set_card_kind(
95 &mut self,
96 index: usize,
97 new_kind: impl Into<String>,
98 ) -> Result<(), EditError> {
99 let new_kind = new_kind.into();
100 validate_composable_kind(&new_kind).map_err(|e| match e {
101 CardKindError::InvalidName => EditError::InvalidKindName(new_kind.clone()),
102 CardKindError::Reserved => EditError::ReservedKind,
103 })?;
104 let len = self.cards().len();
105 let card = self
106 .card_mut(index)
107 .ok_or(EditError::IndexOutOfRange { index, len })?;
108 card.payload_mut().set_kind(new_kind);
109 Ok(())
110 }
111
112 pub fn move_card(&mut self, from: usize, to: usize) -> Result<(), EditError> {
115 let len = self.cards().len();
116 if from >= len {
117 return Err(EditError::IndexOutOfRange { index: from, len });
118 }
119 if to >= len {
120 return Err(EditError::IndexOutOfRange { index: to, len });
121 }
122 if from == to {
123 return Ok(());
124 }
125 let card = self.cards_vec_mut().remove(from);
126 self.cards_vec_mut().insert(to, card);
127 Ok(())
128 }
129}
130
131impl Card {
132 pub fn new(kind: impl Into<String>) -> Result<Self, EditError> {
134 let kind = kind.into();
135 validate_composable_kind(&kind).map_err(|e| match e {
136 CardKindError::InvalidName => EditError::InvalidKindName(kind.clone()),
137 CardKindError::Reserved => EditError::ReservedKind,
138 })?;
139 let mut payload = Payload::new();
140 payload.set_kind(kind);
141 Ok(Card::from_parts(payload, String::new()))
142 }
143
144 pub fn set_field(&mut self, name: &str, value: QuillValue) -> Result<(), EditError> {
149 if !is_valid_field_name(name) {
150 return Err(EditError::InvalidFieldName(name.to_string()));
151 }
152 self.payload_mut().insert(name.to_string(), value);
153 Ok(())
154 }
155
156 pub fn set_fill(&mut self, name: &str, value: QuillValue) -> Result<(), EditError> {
160 if !is_valid_field_name(name) {
161 return Err(EditError::InvalidFieldName(name.to_string()));
162 }
163 self.payload_mut().insert_fill(name.to_string(), value);
164 Ok(())
165 }
166
167 pub fn remove_field(&mut self, name: &str) -> Result<Option<QuillValue>, EditError> {
170 if !is_valid_field_name(name) {
171 return Err(EditError::InvalidFieldName(name.to_string()));
172 }
173 Ok(self.payload_mut().remove(name))
174 }
175
176 pub fn replace_body(&mut self, body: impl Into<String>) {
177 self.overwrite_body(body.into());
178 }
179}