1use serde::{Deserialize, Serialize};
2use std::fmt::Display;
3use std::path::PathBuf;
4
5#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
12pub enum UpdatePhase {
13 #[default]
15 #[serde(rename = "no-update")]
16 NoUpdate,
17 #[serde(rename = "prepare")]
20 Prepare,
21 #[serde(rename = "replace")]
24 Replace,
25 #[serde(rename = "cleanup")]
27 Cleanup,
28}
29
30impl Display for UpdatePhase {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 UpdatePhase::NoUpdate => write!(f, "no-update"),
34 UpdatePhase::Prepare => write!(f, "prepare"),
35 UpdatePhase::Replace => write!(f, "replace"),
36 UpdatePhase::Cleanup => write!(f, "cleanup"),
37 }
38 }
39}
40
41#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
45pub struct UpdateState {
46 #[serde(rename = "app", default, skip_serializing_if = "Option::is_none")]
48 pub target_application: Option<PathBuf>,
49
50 #[serde(rename = "update", default, skip_serializing_if = "Option::is_none")]
52 pub temporary_application: Option<PathBuf>,
53
54 pub phase: UpdatePhase,
56}
57
58impl UpdateState {
59 pub fn for_phase(&self, phase: UpdatePhase) -> Self {
62 UpdateState {
63 target_application: self.target_application.clone(),
64 temporary_application: self.temporary_application.clone(),
65 phase,
66 }
67 }
68}
69
70impl Display for UpdateState {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{:?}", self.phase)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_serialize() {
82 assert_eq!(
83 serde_json::to_string(&UpdateState {
84 target_application: Some(PathBuf::from("/bin/app")),
85 temporary_application: Some(PathBuf::from("/tmp/app-update")),
86 phase: UpdatePhase::Replace
87 })
88 .unwrap(),
89 r#"{"app":"/bin/app","update":"/tmp/app-update","phase":"replace"}"#
90 );
91
92 assert_eq!(
93 serde_json::to_string(&UpdateState {
94 target_application: None,
95 temporary_application: Some(PathBuf::from("/tmp/app-update")),
96 phase: UpdatePhase::Cleanup
97 })
98 .unwrap(),
99 r#"{"update":"/tmp/app-update","phase":"cleanup"}"#
100 );
101 }
102
103 #[test]
104 fn test_deserialize() {
105 let update = UpdateState {
106 target_application: None,
107 temporary_application: Some(PathBuf::from("/tmp/app-update")),
108 phase: UpdatePhase::Cleanup,
109 };
110
111 let deserialized: UpdateState =
112 serde_json::from_str(r#"{"update":"/tmp/app-update","phase":"cleanup"}"#).unwrap();
113 assert_eq!(deserialized, update);
114 }
115
116 #[test]
117 fn test_to_string() {
118 assert_eq!(UpdatePhase::Prepare.to_string(), "prepare");
119 assert_eq!(UpdatePhase::Replace.to_string(), "replace");
120 assert_eq!(UpdatePhase::Cleanup.to_string(), "cleanup");
121 assert_eq!(UpdatePhase::NoUpdate.to_string(), "no-update");
122 }
123
124 #[test]
125 fn test_for_phase() {
126 let update = UpdateState {
127 target_application: Some(PathBuf::from("/bin/app")),
128 temporary_application: Some(PathBuf::from("/tmp/app-update")),
129 phase: UpdatePhase::Replace,
130 };
131
132 let new_update = update.for_phase(UpdatePhase::Cleanup);
133 assert_eq!(new_update.target_application, update.target_application);
134 assert_eq!(
135 new_update.temporary_application,
136 update.temporary_application
137 );
138 assert_eq!(
139 update.phase,
140 UpdatePhase::Replace,
141 "the old update entry should not be modified"
142 );
143 assert_eq!(
144 new_update.phase,
145 UpdatePhase::Cleanup,
146 "the new update entry should have the correct phase"
147 );
148 }
149}