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".to_string(),
71 });
72 }
73
74 if !self.manifest.has_precise_layout() {
75 return Err(crate::Error::StateRequirementNotMet {
76 state: DocumentState::Frozen,
77 requirement: "at least one precise layout".to_string(),
78 });
79 }
80
81 if self.manifest.id.is_pending() {
83 let doc_id = self.compute_id()?;
84 self.manifest.id = doc_id;
85 }
86
87 self.manifest.state = DocumentState::Frozen;
88 self.manifest.modified = Utc::now();
89
90 Ok(())
91 }
92
93 pub fn publish(&mut self) -> Result<()> {
101 if self.manifest.state != DocumentState::Frozen {
102 return Err(crate::Error::InvalidStateTransition {
103 from: self.manifest.state,
104 to: DocumentState::Published,
105 });
106 }
107
108 self.manifest.state = DocumentState::Published;
109 self.manifest.modified = Utc::now();
110
111 Ok(())
112 }
113
114 pub fn revert_to_draft(&mut self) -> Result<()> {
125 if self.manifest.state != DocumentState::Review {
126 return Err(crate::Error::InvalidStateTransition {
127 from: self.manifest.state,
128 to: DocumentState::Draft,
129 });
130 }
131
132 if self.has_signatures() {
133 return Err(crate::Error::ValidationFailed {
134 reason: "cannot revert to draft: document has signatures".to_string(),
135 });
136 }
137
138 self.manifest.state = DocumentState::Draft;
139 self.manifest.id = DocumentId::pending();
140 self.manifest.modified = Utc::now();
141
142 Ok(())
143 }
144
145 pub fn fork(&self) -> Result<Document> {
159 let parent_id = if self.manifest.id.is_pending() {
161 self.compute_id()?
162 } else {
163 self.manifest.id.clone()
164 };
165
166 let lineage = Lineage::from_parent(parent_id, self.manifest.lineage.as_ref());
168
169 let mut forked = self.clone();
171
172 forked.manifest.id = DocumentId::pending();
174 forked.manifest.state = DocumentState::Draft;
175 forked.manifest.created = Utc::now();
176 forked.manifest.modified = Utc::now();
177 forked.manifest.lineage = Some(lineage);
178 forked.manifest.security = None;
179 #[cfg(feature = "signatures")]
180 {
181 forked.signature_file = None;
182 }
183 #[cfg(feature = "encryption")]
184 {
185 forked.encryption_metadata = None;
186 }
187
188 Ok(forked)
189 }
190
191 pub fn set_lineage(
201 &mut self,
202 parent: Option<DocumentId>,
203 version: u32,
204 note: Option<String>,
205 ) -> Result<()> {
206 self.require_mutable("modify lineage")?;
207
208 let lineage = if let Some(parent_id) = parent {
209 Lineage::from_parent(parent_id, None).with_note(note.unwrap_or_default())
210 } else {
211 let mut l = Lineage::root();
212 l.version = Some(version);
213 if let Some(n) = note {
214 l = l.with_note(n);
215 }
216 l
217 };
218
219 self.manifest.lineage = Some(lineage);
220 self.manifest.modified = Utc::now();
221
222 Ok(())
223 }
224}