kcode_k1_web_source_object/
lib.rs1use std::fmt;
4use std::sync::Mutex;
5
6use kcode_k1_objects::{K1Objects, TxId};
7use kcode_k1_web_package::SourcePackage;
8
9const SOURCE_PACKAGE_FILE_TYPE: &str = "k1-web-source-package-v1";
11
12pub struct SourceObjectCapture<'a> {
14 objects: &'a K1Objects,
15 captured: Mutex<Option<TxId>>,
16}
17
18impl<'a> SourceObjectCapture<'a> {
19 pub fn new(objects: &'a K1Objects) -> Self {
21 Self {
22 objects,
23 captured: Mutex::new(None),
24 }
25 }
26
27 pub fn preserve(&self, package: &SourcePackage) -> Result<(), String> {
29 let bytes = kcode_k1_web_transaction::encode(package).map_err(|error| error.to_string())?;
30 let mut captured = self
31 .captured
32 .lock()
33 .map_err(|_| CaptureStateError::Poisoned.to_string())?;
34 let id = self
35 .objects
36 .save("", SOURCE_PACKAGE_FILE_TYPE, "", &bytes)?;
37 *captured = Some(id);
38 Ok(())
39 }
40
41 pub fn captured(&self) -> Option<TxId> {
43 self.captured.lock().ok().and_then(|captured| *captured)
44 }
45
46 pub fn finish(self) -> Result<TxId, CaptureStateError> {
48 self.captured
49 .into_inner()
50 .map_err(|_| CaptureStateError::Poisoned)?
51 .ok_or(CaptureStateError::Missing)
52 }
53}
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub enum CaptureStateError {
58 Poisoned,
60 Missing,
62}
63
64impl fmt::Display for CaptureStateError {
65 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
66 formatter.write_str(match self {
67 Self::Poisoned => "source object capture mutex poisoned",
68 Self::Missing => "publication completed without a captured source object",
69 })
70 }
71}
72
73impl std::error::Error for CaptureStateError {}
74
75#[cfg(test)]
76mod tests {
77 use super::CaptureStateError;
78
79 #[test]
80 fn capture_state_errors_have_stable_messages() {
81 assert_eq!(
82 CaptureStateError::Poisoned.to_string(),
83 "source object capture mutex poisoned"
84 );
85 assert_eq!(
86 CaptureStateError::Missing.to_string(),
87 "publication completed without a captured source object"
88 );
89 }
90}