1use anyhow::Result;
6use typub_core::DraftSupport;
7
8pub fn validate_remote_status(
15 slug: &str,
16 platform: &str,
17 platform_id: Option<&str>,
18 remote_status: Option<&str>,
19) -> Result<()> {
20 if platform_id.is_some() {
21 match remote_status {
22 Some("draft") | Some("published") => Ok(()),
23 Some(invalid) => anyhow::bail!(
24 "Invalid remote_status '{}' for {}/{}: expected 'draft' or 'published'",
25 invalid,
26 slug,
27 platform
28 ),
29 None => anyhow::bail!(
30 "Missing remote_status for {}/{} with existing platform_id",
31 slug,
32 platform
33 ),
34 }
35 } else {
36 Ok(())
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum LifecycleAction {
43 CreatePublished,
45 CreateDraft,
47 UpdatePublished,
49 UpdateDraft,
51 TransitionDraftToPublished,
53 TransitionPublishedToDraft,
55 WarnCannotUnpublish,
57}
58
59pub fn determine_lifecycle_action(
70 has_remote_object: bool,
71 remote_status: Option<&str>,
72 desired_published: bool,
73 draft_support: DraftSupport,
74) -> LifecycleAction {
75 match (
76 has_remote_object,
77 remote_status,
78 desired_published,
79 draft_support,
80 ) {
81 (false, _, true, _) => LifecycleAction::CreatePublished,
83 (false, _, false, DraftSupport::StatusField { .. } | DraftSupport::SeparateObjects) => {
84 LifecycleAction::CreateDraft
85 }
86 (false, _, false, DraftSupport::None) => LifecycleAction::CreatePublished, (true, Some("published"), true, _) => LifecycleAction::UpdatePublished,
90 (true, Some("published"), false, DraftSupport::StatusField { reversible: true }) => {
91 LifecycleAction::TransitionPublishedToDraft
92 }
93 (true, Some("published"), false, DraftSupport::StatusField { reversible: false }) => {
94 LifecycleAction::WarnCannotUnpublish
95 }
96 (true, Some("published"), false, DraftSupport::SeparateObjects) => {
97 LifecycleAction::WarnCannotUnpublish
98 }
99 (true, Some("published"), false, DraftSupport::None) => LifecycleAction::UpdatePublished, (true, Some("draft"), true, DraftSupport::StatusField { .. }) => {
103 LifecycleAction::TransitionDraftToPublished
104 }
105 (true, Some("draft"), true, DraftSupport::SeparateObjects) => {
106 LifecycleAction::TransitionDraftToPublished
107 }
108 (true, Some("draft"), true, DraftSupport::None) => {
109 LifecycleAction::CreatePublished
112 }
113 (
114 true,
115 Some("draft"),
116 false,
117 DraftSupport::StatusField { .. } | DraftSupport::SeparateObjects,
118 ) => LifecycleAction::UpdateDraft,
119 (true, Some("draft"), false, DraftSupport::None) => {
120 LifecycleAction::CreatePublished
122 }
123
124 _ => LifecycleAction::CreatePublished,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 #![allow(clippy::expect_used)]
133 use super::*;
134
135 #[test]
136 fn test_validate_remote_status_ok() {
137 assert!(validate_remote_status("slug", "platform", Some("id"), Some("draft")).is_ok());
138 assert!(validate_remote_status("slug", "platform", Some("id"), Some("published")).is_ok());
139 assert!(validate_remote_status("slug", "platform", None, None).is_ok());
140 assert!(validate_remote_status("slug", "platform", None, Some("garbage")).is_ok());
141 }
142
143 #[test]
144 fn test_validate_remote_status_invalid() {
145 let result = validate_remote_status("slug", "platform", Some("id"), Some("garbage"));
146 assert!(result.is_err());
147 assert!(
148 result
149 .expect_err("already verified is_err()")
150 .to_string()
151 .contains("Invalid remote_status")
152 );
153 }
154
155 #[test]
156 fn test_validate_remote_status_missing() {
157 let result = validate_remote_status("slug", "platform", Some("id"), None);
158 assert!(result.is_err());
159 assert!(
160 result
161 .expect_err("already verified is_err()")
162 .to_string()
163 .contains("Missing remote_status")
164 );
165 }
166
167 #[test]
168 fn test_lifecycle_create_published() {
169 assert_eq!(
170 determine_lifecycle_action(false, None, true, DraftSupport::None),
171 LifecycleAction::CreatePublished
172 );
173 assert_eq!(
174 determine_lifecycle_action(
175 false,
176 None,
177 true,
178 DraftSupport::StatusField { reversible: true }
179 ),
180 LifecycleAction::CreatePublished
181 );
182 }
183
184 #[test]
185 fn test_lifecycle_create_draft() {
186 assert_eq!(
187 determine_lifecycle_action(
188 false,
189 None,
190 false,
191 DraftSupport::StatusField { reversible: true }
192 ),
193 LifecycleAction::CreateDraft
194 );
195 assert_eq!(
196 determine_lifecycle_action(false, None, false, DraftSupport::SeparateObjects),
197 LifecycleAction::CreateDraft
198 );
199 }
200
201 #[test]
202 fn test_lifecycle_create_published_when_no_draft_support() {
203 assert_eq!(
206 determine_lifecycle_action(false, None, false, DraftSupport::None),
207 LifecycleAction::CreatePublished
208 );
209 }
210
211 #[test]
212 fn test_lifecycle_update_published() {
213 assert_eq!(
214 determine_lifecycle_action(true, Some("published"), true, DraftSupport::None),
215 LifecycleAction::UpdatePublished
216 );
217 assert_eq!(
218 determine_lifecycle_action(
219 true,
220 Some("published"),
221 true,
222 DraftSupport::StatusField { reversible: true }
223 ),
224 LifecycleAction::UpdatePublished
225 );
226 }
227
228 #[test]
229 fn test_lifecycle_transition_draft_to_published() {
230 assert_eq!(
231 determine_lifecycle_action(
232 true,
233 Some("draft"),
234 true,
235 DraftSupport::StatusField { reversible: true }
236 ),
237 LifecycleAction::TransitionDraftToPublished
238 );
239 assert_eq!(
240 determine_lifecycle_action(true, Some("draft"), true, DraftSupport::SeparateObjects),
241 LifecycleAction::TransitionDraftToPublished
242 );
243 }
244
245 #[test]
246 fn test_lifecycle_transition_published_to_draft() {
247 assert_eq!(
248 determine_lifecycle_action(
249 true,
250 Some("published"),
251 false,
252 DraftSupport::StatusField { reversible: true }
253 ),
254 LifecycleAction::TransitionPublishedToDraft
255 );
256 }
257
258 #[test]
259 fn test_lifecycle_warn_cannot_unpublish() {
260 assert_eq!(
261 determine_lifecycle_action(
262 true,
263 Some("published"),
264 false,
265 DraftSupport::StatusField { reversible: false }
266 ),
267 LifecycleAction::WarnCannotUnpublish
268 );
269 assert_eq!(
270 determine_lifecycle_action(
271 true,
272 Some("published"),
273 false,
274 DraftSupport::SeparateObjects
275 ),
276 LifecycleAction::WarnCannotUnpublish
277 );
278 }
279
280 #[test]
281 fn test_lifecycle_update_draft() {
282 assert_eq!(
283 determine_lifecycle_action(
284 true,
285 Some("draft"),
286 false,
287 DraftSupport::StatusField { reversible: true }
288 ),
289 LifecycleAction::UpdateDraft
290 );
291 assert_eq!(
292 determine_lifecycle_action(true, Some("draft"), false, DraftSupport::SeparateObjects),
293 LifecycleAction::UpdateDraft
294 );
295 }
296}