wenlan_types/
space_context.rs1use serde::de::{Error as _, Visitor};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq, Default)]
12pub enum WriteSpaceTarget {
13 #[default]
14 Inherit,
15 Uncategorized,
16 Named(String),
17}
18
19impl WriteSpaceTarget {
20 pub fn is_inherit(&self) -> bool {
21 matches!(self, Self::Inherit)
22 }
23
24 pub fn named(&self) -> Option<&str> {
25 match self {
26 Self::Named(name) => Some(name),
27 Self::Inherit | Self::Uncategorized => None,
28 }
29 }
30
31 pub fn as_deref(&self) -> Option<&str> {
32 self.named()
33 }
34}
35
36impl From<Option<String>> for WriteSpaceTarget {
37 fn from(value: Option<String>) -> Self {
38 match value {
39 Some(name) => Self::Named(name),
40 None => Self::Inherit,
41 }
42 }
43}
44
45impl From<String> for WriteSpaceTarget {
46 fn from(value: String) -> Self {
47 Self::Named(value)
48 }
49}
50
51impl Serialize for WriteSpaceTarget {
52 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53 where
54 S: Serializer,
55 {
56 match self {
57 Self::Inherit | Self::Uncategorized => serializer.serialize_none(),
58 Self::Named(name) => serializer.serialize_str(name),
59 }
60 }
61}
62
63impl<'de> Deserialize<'de> for WriteSpaceTarget {
64 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
65 where
66 D: Deserializer<'de>,
67 {
68 struct TargetVisitor;
69
70 impl<'de> Visitor<'de> for TargetVisitor {
71 type Value = WriteSpaceTarget;
72
73 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74 formatter.write_str("null or a non-empty Space name")
75 }
76
77 fn visit_none<E>(self) -> Result<Self::Value, E>
78 where
79 E: serde::de::Error,
80 {
81 Ok(WriteSpaceTarget::Uncategorized)
82 }
83
84 fn visit_unit<E>(self) -> Result<Self::Value, E>
85 where
86 E: serde::de::Error,
87 {
88 Ok(WriteSpaceTarget::Uncategorized)
89 }
90
91 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
92 where
93 D: Deserializer<'de>,
94 {
95 let value = String::deserialize(deserializer)?;
96 let trimmed = value.trim();
97 if trimmed.is_empty() {
98 return Err(D::Error::custom("Space name must not be empty"));
99 }
100 Ok(WriteSpaceTarget::Named(trimmed.to_string()))
101 }
102 }
103
104 deserializer.deserialize_option(TargetVisitor)
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "snake_case")]
110pub enum WriteSpaceSource {
111 Request,
112 Header,
113 Default,
114 Uncategorized,
115 Existing,
116 #[serde(other)]
117 Unknown,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
121#[serde(rename_all = "snake_case")]
122pub enum WriteOutcome {
123 Created,
124 ResolvedExisting,
125 AttachedExisting,
126 #[serde(other)]
127 Unknown,
128}
129
130#[cfg(test)]
131mod tests {
132 use super::{WriteOutcome, WriteSpaceSource, WriteSpaceTarget};
133 use serde::{Deserialize, Serialize};
134
135 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
136 struct WriteFixture {
137 #[serde(default, skip_serializing_if = "WriteSpaceTarget::is_inherit")]
138 space: WriteSpaceTarget,
139 }
140
141 #[test]
142 fn write_target_distinguishes_missing_null_and_named_space() {
143 let missing: WriteFixture = serde_json::from_str("{}").unwrap();
144 let uncategorized: WriteFixture = serde_json::from_str(r#"{"space":null}"#).unwrap();
145 let named: WriteFixture = serde_json::from_str(r#"{"space":"Work"}"#).unwrap();
146
147 assert_eq!(missing.space, WriteSpaceTarget::Inherit);
148 assert_eq!(uncategorized.space, WriteSpaceTarget::Uncategorized);
149 assert_eq!(named.space, WriteSpaceTarget::Named("Work".to_string()));
150 assert_eq!(serde_json::to_string(&missing).unwrap(), "{}");
151 assert_eq!(
152 serde_json::to_string(&uncategorized).unwrap(),
153 r#"{"space":null}"#
154 );
155 assert_eq!(
156 serde_json::to_string(&named).unwrap(),
157 r#"{"space":"Work"}"#
158 );
159 }
160
161 #[test]
162 fn receipt_enums_accept_future_values() {
163 assert_eq!(
164 serde_json::from_str::<WriteSpaceSource>(r#""future_source""#).unwrap(),
165 WriteSpaceSource::Unknown
166 );
167 assert_eq!(
168 serde_json::from_str::<WriteOutcome>(r#""future_outcome""#).unwrap(),
169 WriteOutcome::Unknown
170 );
171 }
172}