quillmark_core/document/
edit.rs1use unicode_normalization::UnicodeNormalization;
20
21use crate::document::meta::{validate_composable_kind, CardKindError};
22use crate::document::{Card, Document, Payload};
23use crate::value::QuillValue;
24use crate::version::QuillReference;
25
26pub fn is_valid_field_name(name: &str) -> bool {
28 let normalized: String = name.nfc().collect();
29 if normalized.is_empty() {
30 return false;
31 }
32 let mut chars = normalized.chars();
33 let first = chars.next().unwrap();
34 if !first.is_ascii_lowercase() && first != '_' {
35 return false;
36 }
37 for ch in chars {
38 if !ch.is_ascii_lowercase() && !ch.is_ascii_digit() && ch != '_' {
39 return false;
40 }
41 }
42 true
43}
44
45#[derive(Debug, Clone, PartialEq, thiserror::Error)]
47pub enum EditError {
48 #[error("invalid field name '{0}': must match [a-z_][a-z0-9_]*")]
49 InvalidFieldName(String),
50
51 #[error("invalid card kind '{0}': must match [a-z_][a-z0-9_]*")]
52 InvalidKindName(String),
53
54 #[error("card kind 'main' is reserved for the document root")]
55 ReservedKind,
56
57 #[error("index {index} is out of range (len = {len})")]
58 IndexOutOfRange { index: usize, len: usize },
59}
60
61impl Document {
62 pub fn set_quill_ref(&mut self, reference: QuillReference) {
63 self.main_mut().payload_mut().set_quill(reference);
64 }
65
66 pub fn card_mut(&mut self, index: usize) -> Option<&mut Card> {
67 self.cards_mut().get_mut(index)
68 }
69
70 pub fn push_card(&mut self, card: Card) {
71 self.cards_vec_mut().push(card);
72 }
73
74 pub fn insert_card(&mut self, index: usize, card: Card) -> Result<(), EditError> {
76 let len = self.cards().len();
77 if index > len {
78 return Err(EditError::IndexOutOfRange { index, len });
79 }
80 self.cards_vec_mut().insert(index, card);
81 Ok(())
82 }
83
84 pub fn remove_card(&mut self, index: usize) -> Option<Card> {
85 if index >= self.cards().len() {
86 return None;
87 }
88 Some(self.cards_vec_mut().remove(index))
89 }
90
91 pub fn set_card_kind(
101 &mut self,
102 index: usize,
103 new_kind: impl Into<String>,
104 ) -> Result<(), EditError> {
105 let new_kind = new_kind.into();
106 validate_composable_kind(&new_kind).map_err(|e| match e {
107 CardKindError::InvalidName => EditError::InvalidKindName(new_kind.clone()),
108 CardKindError::Reserved => EditError::ReservedKind,
109 })?;
110 let len = self.cards().len();
111 let card = self
112 .card_mut(index)
113 .ok_or(EditError::IndexOutOfRange { index, len })?;
114 card.payload_mut().set_kind(new_kind);
115 Ok(())
116 }
117
118 pub fn move_card(&mut self, from: usize, to: usize) -> Result<(), EditError> {
121 let len = self.cards().len();
122 if from >= len {
123 return Err(EditError::IndexOutOfRange { index: from, len });
124 }
125 if to >= len {
126 return Err(EditError::IndexOutOfRange { index: to, len });
127 }
128 if from == to {
129 return Ok(());
130 }
131 let card = self.cards_vec_mut().remove(from);
132 self.cards_vec_mut().insert(to, card);
133 Ok(())
134 }
135}
136
137impl Card {
138 pub fn new(kind: impl Into<String>) -> Result<Self, EditError> {
140 let kind = kind.into();
141 validate_composable_kind(&kind).map_err(|e| match e {
142 CardKindError::InvalidName => EditError::InvalidKindName(kind.clone()),
143 CardKindError::Reserved => EditError::ReservedKind,
144 })?;
145 let mut payload = Payload::new();
146 payload.set_kind(kind);
147 Ok(Card::from_parts(payload, String::new()))
148 }
149
150 pub fn set_field(&mut self, name: &str, value: QuillValue) -> Result<(), EditError> {
155 if !is_valid_field_name(name) {
156 return Err(EditError::InvalidFieldName(name.to_string()));
157 }
158 self.payload_mut().insert(name.to_string(), value);
159 Ok(())
160 }
161
162 pub fn set_fill(&mut self, name: &str, value: QuillValue) -> Result<(), EditError> {
166 if !is_valid_field_name(name) {
167 return Err(EditError::InvalidFieldName(name.to_string()));
168 }
169 self.payload_mut().insert_fill(name.to_string(), value);
170 Ok(())
171 }
172
173 pub fn remove_field(&mut self, name: &str) -> Result<Option<QuillValue>, EditError> {
176 if !is_valid_field_name(name) {
177 return Err(EditError::InvalidFieldName(name.to_string()));
178 }
179 Ok(self.payload_mut().remove(name))
180 }
181
182 pub fn set_ext(&mut self, value: serde_json::Map<String, serde_json::Value>) {
191 self.payload_mut().set_ext(value);
192 }
193
194 pub fn remove_ext(&mut self) -> Option<serde_json::Map<String, serde_json::Value>> {
198 self.payload_mut().take_ext()
199 }
200
201 pub fn set_ext_namespace(&mut self, namespace: impl Into<String>, value: serde_json::Value) {
208 let mut map = self.payload_mut().take_ext().unwrap_or_default();
209 map.insert(namespace.into(), value);
210 self.payload_mut().set_ext(map);
211 }
212
213 pub fn replace_body(&mut self, body: impl Into<String>) {
214 self.overwrite_body(body.into());
215 }
216}