panproto_gat/
alg_struct.rs1use std::sync::Arc;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct AlgStruct {
14 pub name: Arc<str>,
16 pub params: Vec<StructParam>,
18 pub fields: Vec<StructField>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
24pub struct StructParam {
25 pub name: Arc<str>,
27 pub sort: Arc<str>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub struct StructField {
34 pub name: Arc<str>,
36 pub sort: Arc<str>,
38 pub optional: bool,
40}
41
42impl AlgStruct {
43 #[must_use]
45 pub fn new(name: impl Into<Arc<str>>) -> Self {
46 Self {
47 name: name.into(),
48 params: Vec::new(),
49 fields: Vec::new(),
50 }
51 }
52
53 #[must_use]
55 pub fn with_param(mut self, name: impl Into<Arc<str>>, sort: impl Into<Arc<str>>) -> Self {
56 self.params.push(StructParam {
57 name: name.into(),
58 sort: sort.into(),
59 });
60 self
61 }
62
63 #[must_use]
65 pub fn with_field(mut self, name: impl Into<Arc<str>>, sort: impl Into<Arc<str>>) -> Self {
66 self.fields.push(StructField {
67 name: name.into(),
68 sort: sort.into(),
69 optional: false,
70 });
71 self
72 }
73
74 #[must_use]
76 pub fn with_optional_field(
77 mut self,
78 name: impl Into<Arc<str>>,
79 sort: impl Into<Arc<str>>,
80 ) -> Self {
81 self.fields.push(StructField {
82 name: name.into(),
83 sort: sort.into(),
84 optional: true,
85 });
86 self
87 }
88
89 #[must_use]
91 pub fn required_field_count(&self) -> usize {
92 self.fields.iter().filter(|f| !f.optional).count()
93 }
94}
95
96#[cfg(test)]
97#[allow(clippy::unwrap_used, clippy::expect_used)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn build_struct_with_builder() {
103 let s = AlgStruct::new("Person")
104 .with_param("T", "Type")
105 .with_field("name", "string")
106 .with_field("age", "int")
107 .with_optional_field("email", "string");
108
109 assert_eq!(&*s.name, "Person");
110 assert_eq!(s.params.len(), 1);
111 assert_eq!(s.fields.len(), 3);
112 assert_eq!(s.required_field_count(), 2);
113 }
114
115 #[test]
116 fn empty_struct() {
117 let s = AlgStruct::new("Unit");
118 assert!(s.params.is_empty());
119 assert!(s.fields.is_empty());
120 assert_eq!(s.required_field_count(), 0);
121 }
122
123 #[test]
128 fn alg_struct_with_name_overlap_between_fields_and_params_is_inert() {
129 let s = AlgStruct::new("Self")
133 .with_param("Self", "Type")
134 .with_field("Self", "Type")
135 .with_field("value", "Self");
136 assert_eq!(&*s.name, "Self");
137 assert_eq!(s.params.len(), 1);
138 assert_eq!(s.fields.len(), 2);
139
140 let json = serde_json::to_string(&s).expect("serialize");
142 let back: AlgStruct = serde_json::from_str(&json).expect("deserialize");
143 assert_eq!(s, back);
144 }
145
146 #[test]
147 fn serialization_round_trip() {
148 let s = AlgStruct::new("Pair")
149 .with_param("A", "Sort")
150 .with_param("B", "Sort")
151 .with_field("fst", "A")
152 .with_field("snd", "B");
153
154 let json = serde_json::to_string(&s).expect("serialize");
155 let deserialized: AlgStruct = serde_json::from_str(&json).expect("deserialize");
156 assert_eq!(s, deserialized);
157 }
158}