1use std::fmt;
8
9use serde::de::{Error as _, IgnoredAny, MapAccess, Visitor};
10use serde::ser::SerializeStruct;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13#[cfg(doc)]
14use super::Bus;
15use super::{Extras, Impedance, Transformer3W, WINDING_IN_SERVICE_EXTRA, Winding};
16
17const PARTIAL_TYPE: &str = "powerio.PartialTransformer3W";
18
19#[derive(Deserialize)]
29#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
30struct Transformer3WFields {
31 windings: [Winding; 3],
33 z: [Impedance; 3],
37 star_vm: f64,
39 star_va: f64,
40 mag_g: f64,
42 mag_b: f64,
43 in_service: bool,
44 name: Option<String>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
47 uid: Option<String>,
48 extras: Extras,
49 #[serde(default, rename = "type", deserialize_with = "reject_reserved")]
51 #[cfg_attr(feature = "schema", schemars(skip))]
52 _reserved_type: (),
53 #[serde(default, rename = "value", deserialize_with = "reject_reserved")]
54 #[cfg_attr(feature = "schema", schemars(skip))]
55 _reserved_value: (),
56}
57
58fn reject_reserved<'de, D: Deserializer<'de>>(_: D) -> Result<(), D::Error> {
59 Err(D::Error::custom(
60 "a transformer body cannot contain structural type/value fields",
61 ))
62}
63
64impl From<Transformer3WFields> for Transformer3W {
65 fn from(fields: Transformer3WFields) -> Self {
66 Self {
67 windings: fields.windings,
68 z: fields.z,
69 star_vm: fields.star_vm,
70 star_va: fields.star_va,
71 mag_g: fields.mag_g,
72 mag_b: fields.mag_b,
73 in_service: fields.in_service,
74 name: fields.name,
75 uid: fields.uid,
76 extras: fields.extras,
77 }
78 }
79}
80
81struct TransformerBody<'a>(&'a Transformer3W);
82
83impl Serialize for TransformerBody<'_> {
84 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
85 let t = self.0;
86 let mut body =
87 serializer.serialize_struct("Transformer3W", 9 + usize::from(t.uid.is_some()))?;
88 body.serialize_field("windings", &t.windings)?;
89 body.serialize_field("z", &t.z)?;
90 body.serialize_field("star_vm", &t.star_vm)?;
91 body.serialize_field("star_va", &t.star_va)?;
92 body.serialize_field("mag_g", &t.mag_g)?;
93 body.serialize_field("mag_b", &t.mag_b)?;
94 body.serialize_field("in_service", &t.in_service)?;
95 body.serialize_field("name", &t.name)?;
96 if let Some(uid) = &t.uid {
97 body.serialize_field("uid", uid)?;
98 }
99 body.serialize_field("extras", &t.extras)?;
100 body.end()
101 }
102}
103
104impl Serialize for Transformer3W {
105 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
106 if self.winding_in_service() == [true; 3] {
107 return TransformerBody(self).serialize(serializer);
108 }
109 let mut wrapper = serializer.serialize_struct("PartialTransformer3W", 2)?;
110 wrapper.serialize_field("type", PARTIAL_TYPE)?;
111 wrapper.serialize_field("value", &TransformerBody(self))?;
112 wrapper.end()
113 }
114}
115
116#[derive(Deserialize)]
117enum PartialType {
118 #[serde(rename = "powerio.PartialTransformer3W")]
119 Partial,
120}
121
122#[derive(Deserialize)]
123#[serde(field_identifier, rename_all = "snake_case")]
124enum Field {
125 Windings,
126 Z,
127 StarVm,
128 StarVa,
129 MagG,
130 MagB,
131 InService,
132 Name,
133 Uid,
134 Extras,
135 Type,
136 Value,
137 #[serde(other)]
138 Other,
139}
140
141impl<'de> Deserialize<'de> for Transformer3W {
142 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
143 struct TransformerVisitor;
144 impl<'de> Visitor<'de> for TransformerVisitor {
145 type Value = Transformer3W;
146
147 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
148 formatter.write_str("a legacy transformer or powerio.PartialTransformer3W record")
149 }
150
151 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
152 let mut windings = None;
153 let mut z = None;
154 let mut star_vm = None;
155 let mut star_va = None;
156 let mut mag_g = None;
157 let mut mag_b = None;
158 let mut in_service = None;
159 let mut name = None;
160 let mut uid = None;
161 let mut extras = None;
162 let mut kind = None;
163 let mut value: Option<Transformer3WFields> = None;
164 let mut has_body = false;
165 macro_rules! read_field {
169 ($field:ident) => {{
170 if $field.is_some() {
171 return Err(A::Error::duplicate_field(stringify!($field)));
172 }
173 $field = Some(map.next_value()?);
174 has_body = true;
175 }};
176 }
177 while let Some(field) = map.next_key()? {
178 match field {
179 Field::Windings => read_field!(windings),
180 Field::Z => read_field!(z),
181 Field::StarVm => read_field!(star_vm),
182 Field::StarVa => read_field!(star_va),
183 Field::MagG => read_field!(mag_g),
184 Field::MagB => read_field!(mag_b),
185 Field::InService => read_field!(in_service),
186 Field::Name => read_field!(name),
187 Field::Uid => read_field!(uid),
188 Field::Extras => read_field!(extras),
189 Field::Type => {
190 if kind.is_some() {
191 return Err(A::Error::duplicate_field("type"));
192 }
193 kind = Some(map.next_value::<PartialType>()?);
194 }
195 Field::Value => {
196 if value.is_some() {
197 return Err(A::Error::duplicate_field("value"));
198 }
199 value = Some(map.next_value()?);
200 }
201 Field::Other => {
202 map.next_value::<IgnoredAny>()?;
203 }
204 }
205 }
206 if kind.is_some() || value.is_some() {
207 if has_body {
208 return Err(A::Error::custom(
209 "powerio.PartialTransformer3W requires all transformer fields inside value",
210 ));
211 }
212 kind.ok_or_else(|| A::Error::missing_field("type"))?;
213 let transformer =
214 Transformer3W::from(value.ok_or_else(|| A::Error::missing_field("value"))?);
215 let flags = transformer.extras.get(WINDING_IN_SERVICE_EXTRA)
216 .and_then(|value| <[bool; 3]>::deserialize(value).ok())
217 .ok_or_else(|| A::Error::custom("powerio.PartialTransformer3W requires three boolean winding_in_service flags"))?;
218 if flags == [true; 3] {
219 return Err(A::Error::custom(
220 "powerio.PartialTransformer3W requires a nondefault winding state",
221 ));
222 }
223 return Ok(transformer);
224 }
225 let transformer = Transformer3W {
226 windings: windings.ok_or_else(|| A::Error::missing_field("windings"))?,
227 z: z.ok_or_else(|| A::Error::missing_field("z"))?,
228 star_vm: star_vm.ok_or_else(|| A::Error::missing_field("star_vm"))?,
229 star_va: star_va.ok_or_else(|| A::Error::missing_field("star_va"))?,
230 mag_g: mag_g.ok_or_else(|| A::Error::missing_field("mag_g"))?,
231 mag_b: mag_b.ok_or_else(|| A::Error::missing_field("mag_b"))?,
232 in_service: in_service.ok_or_else(|| A::Error::missing_field("in_service"))?,
233 name: name.unwrap_or(None),
234 uid: uid.unwrap_or(None),
235 extras: extras.ok_or_else(|| A::Error::missing_field("extras"))?,
236 };
237 if transformer.winding_in_service() != [true; 3] {
238 return Err(A::Error::custom(
239 "nondefault winding states require a powerio.PartialTransformer3W record",
240 ));
241 }
242 Ok(transformer)
243 }
244 }
245 deserializer.deserialize_map(TransformerVisitor)
246 }
247}
248
249#[cfg(feature = "schema")]
250#[derive(schemars::JsonSchema)]
251#[allow(dead_code)] #[serde(tag = "type", content = "value")]
253enum PartialTransformer3WWire {
254 #[serde(rename = "powerio.PartialTransformer3W")]
255 Partial(PartialTransformer3WFields),
256}
257
258#[cfg(feature = "schema")]
259struct PartialTransformer3WFields;
260
261#[cfg(feature = "schema")]
262impl schemars::JsonSchema for PartialTransformer3WFields {
263 fn schema_name() -> std::borrow::Cow<'static, str> {
264 "PartialTransformer3WFields".into()
265 }
266
267 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
268 let mut body = serde_json::to_value(Transformer3WFields::json_schema(generator))
269 .expect("transformer schema is serializable");
270 body["properties"]["extras"]["properties"] = serde_json::json!({
273 "winding_in_service": {
274 "type": "array", "minItems": 3, "maxItems": 3,
275 "items": {"type": "boolean"},
276 "not": {"const": [true, true, true]}
277 }
278 });
279 body["properties"]["extras"]["required"] = serde_json::json!(["winding_in_service"]);
280 serde_json::from_value(body).expect("an extended object remains a schema")
281 }
282}
283
284#[cfg(feature = "schema")]
285impl schemars::JsonSchema for Transformer3W {
286 fn schema_name() -> std::borrow::Cow<'static, str> {
287 "Transformer3W".into()
288 }
289
290 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
291 schemars::json_schema!({"anyOf": [
293 Transformer3WFields::json_schema(generator),
294 generator.subschema_for::<PartialTransformer3WWire>(),
295 ]})
296 }
297}