fe2o3_amqp_types/messaging/
target.rs

1use serde_amqp::macros::{DeserializeComposite, SerializeComposite};
2use serde_amqp::primitives::{Array, Boolean, Symbol};
3use serde_amqp::Value;
4
5use crate::definitions::{Fields, Seconds};
6
7use super::{
8    Address, LifetimePolicy, NodeProperties, SupportedDistModes, TerminusDurability,
9    TerminusExpiryPolicy,
10};
11
12#[cfg(feature = "transaction")]
13use crate::transaction::Coordinator;
14
15/// The target archetype represented as a enum
16///
17/// For details, please see part 1.3, 3.5.4, and 4.5.1 in the core
18/// specification.
19#[derive(Debug, Clone)]
20pub enum TargetArchetype {
21    /// 3.5.4 Target
22    Target(Target),
23
24    /// 4.5.1 Coordinator
25    #[cfg_attr(docsrs, doc(cfg(feature = "transaction")))]
26    #[cfg(feature = "transaction")]
27    Coordinator(Coordinator),
28}
29
30mod target_archetype_serde_impl {
31    use serde::{
32        de::{self, VariantAccess},
33        ser,
34    };
35
36    use super::TargetArchetype;
37
38    impl ser::Serialize for TargetArchetype {
39        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40        where
41            S: serde::Serializer,
42        {
43            match self {
44                TargetArchetype::Target(value) => value.serialize(serializer),
45                #[cfg(feature = "transaction")]
46                TargetArchetype::Coordinator(value) => value.serialize(serializer),
47            }
48        }
49    }
50
51    enum Field {
52        Target,
53        #[cfg(feature = "transaction")]
54        Coordinator,
55    }
56
57    struct FieldVisitor {}
58
59    impl<'de> de::Visitor<'de> for FieldVisitor {
60        type Value = Field;
61
62        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
63            formatter.write_str("variant identifier for TargetArchetype")
64        }
65
66        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
67        where
68            E: de::Error,
69        {
70            let val = match v {
71                "amqp:target:list" => Field::Target,
72                #[cfg(feature = "transaction")]
73                "amqp:coordinator:list" => Field::Coordinator,
74                _ => {
75                    return Err(de::Error::custom(
76                        "Wrong descriptor symbol value for Target archetype",
77                    ))
78                }
79            };
80            Ok(val)
81        }
82
83        fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
84        where
85            E: de::Error,
86        {
87            let val = match v {
88                0x0000_0000_0000_0029 => Field::Target,
89                #[cfg(feature = "transaction")]
90                0x0000_0000_0000_0030 => Field::Coordinator,
91                _ => {
92                    return Err(de::Error::custom(
93                        "Wrong descriptor code value for Target archetype",
94                    ))
95                }
96            };
97            Ok(val)
98        }
99    }
100
101    impl<'de> de::Deserialize<'de> for Field {
102        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103        where
104            D: serde::Deserializer<'de>,
105        {
106            deserializer.deserialize_identifier(FieldVisitor {})
107        }
108    }
109
110    struct Visitor {}
111
112    impl<'de> de::Visitor<'de> for Visitor {
113        type Value = TargetArchetype;
114
115        fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
116            formatter.write_str("variant identifier for TargetArchetype")
117        }
118
119        fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
120        where
121            A: de::EnumAccess<'de>,
122        {
123            let (val, variant) = data.variant()?;
124
125            match val {
126                Field::Target => {
127                    let value = variant.newtype_variant()?;
128                    Ok(TargetArchetype::Target(value))
129                }
130                #[cfg(feature = "transaction")]
131                Field::Coordinator => {
132                    let value = variant.newtype_variant()?;
133                    Ok(TargetArchetype::Coordinator(value))
134                }
135            }
136        }
137    }
138
139    impl<'de> de::Deserialize<'de> for TargetArchetype {
140        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141        where
142            D: serde::Deserializer<'de>,
143        {
144            #[cfg(not(feature = "transaction"))]
145            const VARIANTS: &[&str] = &["amqp:target:list"];
146            #[cfg(feature = "transaction")]
147            const VARIANTS: &[&str] = &["amqp:target:list", "amqp:coordinator:list"];
148            deserializer.deserialize_enum("TargetArchetype", VARIANTS, Visitor {})
149        }
150    }
151}
152
153/// 3.5.4 Target
154///
155/// <type name="target" class="composite" source="list" provides="target">
156///     <descriptor name="amqp:target:list" code="0x00000000:0x00000029"/>
157/// </type>
158#[derive(Debug, Clone, Default, DeserializeComposite, SerializeComposite)]
159#[amqp_contract(
160    name = "amqp:target:list",
161    code = "0x0000_0000:0x0000_0029",
162    encoding = "list",
163    rename_all = "kebab-case"
164)]
165pub struct Target {
166    /// <field name="address" type="*" requires="address"/>
167    pub address: Option<Address>,
168
169    /// <field name="durable" type="terminus-durability" default="none"/>
170    #[amqp_contract(default)]
171    pub durable: TerminusDurability,
172
173    /// <field name="expiry-policy" type="terminus-expiry-policy" default="session-end"/>
174    #[amqp_contract(default)]
175    pub expiry_policy: TerminusExpiryPolicy,
176
177    /// <field name="timeout" type="seconds" default="0"/>
178    #[amqp_contract(default)]
179    pub timeout: Seconds,
180
181    /// <field name="dynamic" type="boolean" default="false"/>
182    #[amqp_contract(default)]
183    pub dynamic: Boolean,
184
185    /// <field name="dynamic-node-properties" type="node-properties"/>
186    ///
187    /// Properties of the dynamically created node
188    ///
189    /// If the dynamic field is not set to true this field MUST be left unset.
190    ///
191    /// When set by the sending link endpoint, this field contains the desired properties of the
192    /// node the sender wishes to be created. When set by the receiving link endpoint this field
193    /// contains the actual properties of the dynamically created node. See subsection 3.5.9 Node
194    /// Properties for standard node properties. A registry of other commonly used node-properties
195    /// and their meanings is maintained [AMQPNODEPROP].
196    pub dynamic_node_properties: Option<NodeProperties>,
197
198    /// <field name="capabilities" type="symbol" multiple="true"/>
199    pub capabilities: Option<Array<Symbol>>,
200}
201
202impl Target {
203    /// Creates a TargetBuilder for Target
204    pub fn builder() -> TargetBuilder {
205        TargetBuilder::new()
206    }
207}
208
209impl TryFrom<TargetArchetype> for Target {
210    type Error = TargetArchetype;
211
212    fn try_from(value: TargetArchetype) -> Result<Self, Self::Error> {
213        match value {
214            TargetArchetype::Target(target) => Ok(target),
215            #[cfg(feature = "transaction")]
216            _ => Err(value),
217        }
218    }
219}
220
221impl<T: Into<Address>> From<T> for Target {
222    fn from(val: T) -> Self {
223        Self {
224            address: Some(val.into()),
225            ..Default::default()
226        }
227        // Self::builder().address(val.into()).build()
228    }
229}
230
231impl<T: Into<Target>> From<T> for TargetArchetype {
232    fn from(value: T) -> Self {
233        let target = value.into();
234        Self::Target(target)
235    }
236}
237
238/// [`Target`] builder
239#[derive(Debug, Default, Clone)]
240pub struct TargetBuilder {
241    /// The [`Target`] instance being built
242    pub target: Target,
243}
244
245impl TargetBuilder {
246    /// Creates a new [`Target`] builder
247    pub fn new() -> Self {
248        Self {
249            target: Default::default(),
250        }
251    }
252
253    /// Set the "address" field
254    pub fn address(mut self, address: impl Into<Address>) -> Self {
255        self.target.address = Some(address.into());
256        self
257    }
258
259    /// Set the "durable" field
260    pub fn durable(mut self, durability: TerminusDurability) -> Self {
261        self.target.durable = durability;
262        self
263    }
264
265    /// Set the "expiry-policy" field
266    pub fn expiry_policy(mut self, policy: TerminusExpiryPolicy) -> Self {
267        self.target.expiry_policy = policy;
268        self
269    }
270
271    /// Set the "timeout" field
272    pub fn timeout(mut self, timeout: Seconds) -> Self {
273        self.target.timeout = timeout;
274        self
275    }
276
277    /// Set the "dynamic" field
278    pub fn dynamic(mut self, dynamic: bool) -> Self {
279        self.target.dynamic = dynamic;
280        self
281    }
282
283    /// Set the "dynamic-node-properties" field
284    ///
285    /// Properties of the dynamically created node
286    ///
287    /// If the dynamic field is not set to true this field MUST be left unset.
288    ///
289    /// When set by the sending link endpoint, this field contains the desired properties of the
290    /// node the sender wishes to be created. When set by the receiving link endpoint this field
291    /// contains the actual properties of the dynamically created node. See subsection 3.5.9 Node
292    /// Properties for standard node properties. A registry of other commonly used node-properties
293    /// and their meanings is maintained [AMQPNODEPROP].
294    ///
295    /// # Example
296    ///
297    /// ```rust
298    /// use fe2o3_amqp_types::messaging::{Target, LifetimePolicy, DeleteOnClose};
299    ///
300    /// let source = Target::builder()
301    ///     .dynamic(true)
302    ///     .dynamic_node_properties(LifetimePolicy::DeleteOnClose(DeleteOnClose{}))
303    ///     .build();
304    /// ```
305    pub fn dynamic_node_properties(mut self, properties: impl Into<Fields>) -> Self {
306        self.target.dynamic_node_properties = Some(properties.into());
307        self
308    }
309
310    /// Add a "lifetime-policy" to the "dynamic-node-properties" field
311    ///
312    /// If the dynamic field is not set to true this field MUST be left unset.
313    ///
314    /// When set by the sending link endpoint, this field contains the desired properties of the
315    /// node the sender wishes to be created. When set by the receiving link endpoint this field
316    /// contains the actual properties of the dynamically created node. See subsection 3.5.9 Node
317    /// Properties for standard node properties. A registry of other commonly used node-properties
318    /// and their meanings is maintained [AMQPNODEPROP].
319    ///
320    /// # Example
321    ///
322    /// ```rust
323    /// use fe2o3_amqp_types::messaging::{Target, DeleteOnClose};
324    ///
325    /// let source = Target::builder()
326    ///     .dynamic(true)
327    ///     .add_lifetime_policy(DeleteOnClose{})
328    ///     .build();
329    /// ```
330    pub fn add_lifetime_policy(mut self, policy: impl Into<LifetimePolicy>) -> Self {
331        let policy: LifetimePolicy = policy.into();
332        match &mut self.target.dynamic_node_properties {
333            Some(map) => {
334                map.insert(Symbol::from("lifetime-policy"), Value::from(policy));
335            }
336            None => {
337                self.target.dynamic_node_properties = Some(policy.into());
338            }
339        };
340        self
341    }
342
343    /// Add "supported-dist-modes" entry to the "dynamic-node-properties" field
344    ///
345    /// If the dynamic field is not set to true this field MUST be left unset.
346    ///
347    /// When set by the sending link endpoint, this field contains the desired properties of the
348    /// node the sender wishes to be created. When set by the receiving link endpoint this field
349    /// contains the actual properties of the dynamically created node. See subsection 3.5.9 Node
350    /// Properties for standard node properties. A registry of other commonly used node-properties
351    /// and their meanings is maintained [AMQPNODEPROP].
352    ///
353    /// # Example
354    ///
355    /// ```rust
356    /// use fe2o3_amqp_types::messaging::{Target, DistributionMode};
357    ///
358    /// let source = Target::builder()
359    ///     .dynamic(true)
360    ///     .add_supported_dist_modes(DistributionMode::Move)
361    ///     .build();
362    /// ```
363    pub fn add_supported_dist_modes(mut self, modes: impl Into<SupportedDistModes>) -> Self {
364        let modes: SupportedDistModes = modes.into();
365        match &mut self.target.dynamic_node_properties {
366            Some(map) => {
367                map.insert(Symbol::from("supported-dist-modes"), Value::from(modes));
368            }
369            None => self.target.dynamic_node_properties = Some(modes.into()),
370        };
371        self
372    }
373
374    /// Set the "capabilities" field
375    pub fn capabilities(mut self, capabilities: impl Into<Array<Symbol>>) -> Self {
376        self.target.capabilities = Some(capabilities.into());
377        self
378    }
379
380    /// Build the [`Target`]
381    pub fn build(self) -> Target {
382        self.target
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use serde_amqp::{from_slice, to_vec};
389
390    use super::{Target, TargetArchetype};
391
392    #[test]
393    fn test_target_archetype_variant_target() {
394        let target = Target::default();
395        let buf = to_vec(&target).unwrap();
396        let archetype: TargetArchetype = from_slice(&buf).unwrap();
397        println!("{:?}", archetype);
398
399        // println!("{:?}", std::mem::size_of::<Target>());
400    }
401
402    #[cfg(feature = "transaction")]
403    #[test]
404    fn test_target_archetype_variant_coordinator() {
405        use crate::transaction::Coordinator;
406        let coordinator = Coordinator::new(None);
407        let buf = to_vec(&coordinator).unwrap();
408        let archetype: TargetArchetype = from_slice(&buf).unwrap();
409        println!("{:?}", archetype);
410
411        // println!("{:?}", std::mem::size_of::<Coordinator>());
412    }
413}