use std::sync::Arc;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AlgStruct {
pub name: Arc<str>,
pub params: Vec<StructParam>,
pub fields: Vec<StructField>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StructParam {
pub name: Arc<str>,
pub sort: Arc<str>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StructField {
pub name: Arc<str>,
pub sort: Arc<str>,
pub optional: bool,
}
impl AlgStruct {
#[must_use]
pub fn new(name: impl Into<Arc<str>>) -> Self {
Self {
name: name.into(),
params: Vec::new(),
fields: Vec::new(),
}
}
#[must_use]
pub fn with_param(mut self, name: impl Into<Arc<str>>, sort: impl Into<Arc<str>>) -> Self {
self.params.push(StructParam {
name: name.into(),
sort: sort.into(),
});
self
}
#[must_use]
pub fn with_field(mut self, name: impl Into<Arc<str>>, sort: impl Into<Arc<str>>) -> Self {
self.fields.push(StructField {
name: name.into(),
sort: sort.into(),
optional: false,
});
self
}
#[must_use]
pub fn with_optional_field(
mut self,
name: impl Into<Arc<str>>,
sort: impl Into<Arc<str>>,
) -> Self {
self.fields.push(StructField {
name: name.into(),
sort: sort.into(),
optional: true,
});
self
}
#[must_use]
pub fn required_field_count(&self) -> usize {
self.fields.iter().filter(|f| !f.optional).count()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn build_struct_with_builder() {
let s = AlgStruct::new("Person")
.with_param("T", "Type")
.with_field("name", "string")
.with_field("age", "int")
.with_optional_field("email", "string");
assert_eq!(&*s.name, "Person");
assert_eq!(s.params.len(), 1);
assert_eq!(s.fields.len(), 3);
assert_eq!(s.required_field_count(), 2);
}
#[test]
fn empty_struct() {
let s = AlgStruct::new("Unit");
assert!(s.params.is_empty());
assert!(s.fields.is_empty());
assert_eq!(s.required_field_count(), 0);
}
#[test]
fn alg_struct_with_name_overlap_between_fields_and_params_is_inert() {
let s = AlgStruct::new("Self")
.with_param("Self", "Type")
.with_field("Self", "Type")
.with_field("value", "Self");
assert_eq!(&*s.name, "Self");
assert_eq!(s.params.len(), 1);
assert_eq!(s.fields.len(), 2);
let json = serde_json::to_string(&s).expect("serialize");
let back: AlgStruct = serde_json::from_str(&json).expect("deserialize");
assert_eq!(s, back);
}
#[test]
fn serialization_round_trip() {
let s = AlgStruct::new("Pair")
.with_param("A", "Sort")
.with_param("B", "Sort")
.with_field("fst", "A")
.with_field("snd", "B");
let json = serde_json::to_string(&s).expect("serialize");
let deserialized: AlgStruct = serde_json::from_str(&json).expect("deserialize");
assert_eq!(s, deserialized);
}
}