cdx_core/document/
state.rs1use chrono::Utc;
2
3use crate::manifest::Lineage;
4use crate::{DocumentId, DocumentState, Result};
5
6use super::Document;
7use super::MutableResource;
8
9impl Document {
10 pub fn submit_for_review(&mut self) -> Result<()> {
21 if self.manifest.state != DocumentState::Draft {
22 return Err(crate::Error::InvalidStateTransition {
23 from: self.manifest.state,
24 to: DocumentState::Review,
25 });
26 }
27
28 let doc_id = self.compute_id()?;
30 self.manifest.id = doc_id;
31 self.manifest.state = DocumentState::Review;
32 self.manifest.modified = Utc::now();
33
34 Ok(())
35 }
36
37 pub fn freeze(&mut self) -> Result<()> {
52 if self.manifest.state != DocumentState::Review {
53 return Err(crate::Error::InvalidStateTransition {
54 from: self.manifest.state,
55 to: DocumentState::Frozen,
56 });
57 }
58
59 if !self.has_signatures() {
61 return Err(crate::Error::StateRequirementNotMet {
62 state: DocumentState::Frozen,
63 requirement: "at least one signature".to_string(),
64 });
65 }
66
67 if self.manifest.lineage.is_none() {
68 return Err(crate::Error::StateRequirementNotMet {
69 state: DocumentState::Frozen,
70 requirement: "lineage information (call set_lineage for root documents)"
71 .to_string(),
72 });
73 }
74
75 if !self.manifest.has_precise_layout() {
76 return Err(crate::Error::StateRequirementNotMet {
77 state: DocumentState::Frozen,
78 requirement: "at least one precise layout".to_string(),
79 });
80 }
81
82 if self.manifest.id.is_pending() {
84 let doc_id = self.compute_id()?;
85 self.manifest.id = doc_id;
86 }
87
88 self.manifest.state = DocumentState::Frozen;
89 self.manifest.modified = Utc::now();
90
91 Ok(())
92 }
93
94 pub fn publish(&mut self) -> Result<()> {
102 if self.manifest.state != DocumentState::Frozen {
103 return Err(crate::Error::InvalidStateTransition {
104 from: self.manifest.state,
105 to: DocumentState::Published,
106 });
107 }
108
109 self.manifest.state = DocumentState::Published;
110 self.manifest.modified = Utc::now();
111
112 Ok(())
113 }
114
115 pub fn revert_to_draft(&mut self) -> Result<()> {
126 if self.manifest.state != DocumentState::Review {
127 return Err(crate::Error::InvalidStateTransition {
128 from: self.manifest.state,
129 to: DocumentState::Draft,
130 });
131 }
132
133 if self.has_signatures() {
134 return Err(crate::Error::ValidationFailed {
135 reason: "cannot revert to draft: document has signatures".to_string(),
136 });
137 }
138
139 self.manifest.state = DocumentState::Draft;
140 self.manifest.id = DocumentId::pending();
141 self.manifest.modified = Utc::now();
142
143 Ok(())
144 }
145
146 pub fn fork(&self) -> Result<Document> {
160 let parent_id = if self.manifest.id.is_pending() {
162 self.compute_id()?
163 } else {
164 self.manifest.id.clone()
165 };
166
167 let lineage = Lineage::from_parent(parent_id, self.manifest.lineage.as_ref());
169
170 let mut forked = self.clone();
172
173 forked.manifest.id = DocumentId::pending();
175 forked.manifest.state = DocumentState::Draft;
176 forked.manifest.created = Utc::now();
177 forked.manifest.modified = Utc::now();
178 forked.manifest.lineage = Some(lineage);
179 forked.manifest.security = None;
180 #[cfg(feature = "signatures")]
181 {
182 forked.signature_file = None;
183 }
184 #[cfg(feature = "encryption")]
185 {
186 forked.encryption_metadata = None;
187 }
188
189 Ok(forked)
190 }
191
192 pub fn set_lineage(
202 &mut self,
203 parent: Option<DocumentId>,
204 version: u32,
205 note: Option<String>,
206 ) -> Result<()> {
207 self.require_mutable("modify lineage")?;
208
209 let lineage = if let Some(parent_id) = parent {
210 let mut l = Lineage::from_parent(parent_id, None);
211 l.version = Some(version);
212 if let Some(n) = note {
213 l = l.with_note(n);
214 }
215 l
216 } else {
217 let mut l = Lineage::root();
218 l.version = Some(version);
219 if let Some(n) = note {
220 l = l.with_note(n);
221 }
222 l
223 };
224
225 self.manifest.lineage = Some(lineage);
226 self.manifest.modified = Utc::now();
227
228 Ok(())
229 }
230}