solti_api/grpc/convert/
phase.rs1use solti_model::TaskPhase;
8
9use crate::error::ApiError;
10use crate::proto_api;
11
12impl TryFrom<TaskPhase> for proto_api::TaskPhase {
13 type Error = ApiError;
14
15 fn try_from(phase: TaskPhase) -> Result<Self, Self::Error> {
16 Ok(match phase {
17 TaskPhase::Succeeded => Self::Succeeded,
18 TaskPhase::Exhausted => Self::Exhausted,
19 TaskPhase::Canceled => Self::Canceled,
20 TaskPhase::Pending => Self::Pending,
21 TaskPhase::Running => Self::Running,
22 TaskPhase::Timeout => Self::Timeout,
23 TaskPhase::Failed => Self::Failed,
24 _ => {
25 return Err(ApiError::Internal(
26 "handler returned an unsupported task phase".into(),
27 ));
28 }
29 })
30 }
31}
32
33#[cfg(feature = "grpc")]
35pub(crate) fn proto_to_domain_phase(raw: i32) -> Result<TaskPhase, ApiError> {
36 let status = proto_api::TaskPhase::try_from(raw)
37 .map_err(|_| ApiError::InvalidRequest(format!("invalid status value: {raw}")))?;
38
39 match status {
40 proto_api::TaskPhase::Succeeded => Ok(TaskPhase::Succeeded),
41 proto_api::TaskPhase::Exhausted => Ok(TaskPhase::Exhausted),
42 proto_api::TaskPhase::Canceled => Ok(TaskPhase::Canceled),
43 proto_api::TaskPhase::Pending => Ok(TaskPhase::Pending),
44 proto_api::TaskPhase::Running => Ok(TaskPhase::Running),
45 proto_api::TaskPhase::Timeout => Ok(TaskPhase::Timeout),
46 proto_api::TaskPhase::Failed => Ok(TaskPhase::Failed),
47
48 proto_api::TaskPhase::Unspecified => Err(ApiError::InvalidRequest(
49 "status cannot be unspecified".into(),
50 )),
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn task_phase_maps_all_known_variants_both_ways() {
60 let cases = [
61 (TaskPhase::Pending, proto_api::TaskPhase::Pending),
62 (TaskPhase::Running, proto_api::TaskPhase::Running),
63 (TaskPhase::Succeeded, proto_api::TaskPhase::Succeeded),
64 (TaskPhase::Failed, proto_api::TaskPhase::Failed),
65 (TaskPhase::Timeout, proto_api::TaskPhase::Timeout),
66 (TaskPhase::Canceled, proto_api::TaskPhase::Canceled),
67 (TaskPhase::Exhausted, proto_api::TaskPhase::Exhausted),
68 ];
69
70 for (domain, expected_proto) in cases {
71 let proto = proto_api::TaskPhase::try_from(domain).unwrap();
72 assert_eq!(proto, expected_proto, "mismatch for {:?}", domain);
73
74 #[cfg(feature = "grpc")]
75 assert_eq!(
76 proto_to_domain_phase(expected_proto as i32).unwrap(),
77 domain
78 );
79 }
80 }
81
82 #[cfg(feature = "grpc")]
83 #[test]
84 fn proto_to_domain_phase_rejects_unknown_values() {
85 for (raw, expected_message) in [
86 (
87 proto_api::TaskPhase::Unspecified as i32,
88 "unspecified".to_owned(),
89 ),
90 (9999, "9999".to_owned()),
91 ] {
92 let error = proto_to_domain_phase(raw).unwrap_err();
93 assert!(
94 matches!(error, ApiError::InvalidRequest(message) if message.contains(&expected_message))
95 );
96 }
97 }
98}