use std::collections::HashMap;
use serde::ser::SerializeMap as _;
use serde::Serialize;
use super::{
baml_value::BamlValue, from_baml_value::FromBamlValue, from_baml_value_ref::FromBamlValueRef,
known_types::KnownTypes,
};
use crate::error::{BamlError, FullTypeName};
#[derive(Debug, Clone)]
pub struct DynamicClass<T: KnownTypes, S: KnownTypes> {
pub name: String,
pub(crate) fields: HashMap<String, BamlValue<T, S>>,
}
impl<T: KnownTypes, S: KnownTypes> DynamicClass<T, S> {
pub fn new(name: String) -> Self {
Self {
name,
fields: HashMap::new(),
}
}
pub fn with_fields(name: String, fields: HashMap<String, BamlValue<T, S>>) -> Self {
Self { name, fields }
}
pub fn fields(&self) -> impl Iterator<Item = (&str, &BamlValue<T, S>)> {
self.fields.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn has_field(&self, field_name: &str) -> bool {
self.fields.contains_key(field_name)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn get<V: FromBamlValue<T, S>>(&self, field_name: &str) -> Result<V, BamlError> {
let value = self
.fields
.get(field_name)
.ok_or_else(|| BamlError::internal(format!("missing field '{field_name}'")))?
.clone();
V::from_baml_value(value)
}
pub fn get_ref<'a, V: FromBamlValueRef<'a, T, S>>(
&'a self,
field_name: &str,
) -> Result<V, BamlError> {
let value = self
.fields
.get(field_name)
.ok_or_else(|| BamlError::internal(format!("missing field '{field_name}'")))?;
V::from_baml_value_ref(value)
}
pub fn pop<V: FromBamlValue<T, S>>(&mut self, field_name: &str) -> Result<V, BamlError> {
let value = self
.fields
.remove(field_name)
.ok_or_else(|| BamlError::internal(format!("missing field '{field_name}'")))?;
V::from_baml_value(value)
}
pub fn into_fields(self) -> HashMap<String, BamlValue<T, S>> {
self.fields
}
}
impl<T: KnownTypes + serde::Serialize, S: KnownTypes + serde::Serialize> serde::Serialize
for DynamicClass<T, S>
{
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.fields.serialize(serializer)
}
}
impl<'de, T: KnownTypes + serde::Deserialize<'de>, S: KnownTypes + serde::Deserialize<'de>>
serde::Deserialize<'de> for DynamicClass<T, S>
{
fn deserialize<D: serde::Deserializer<'de>>(_deserializer: D) -> Result<Self, D::Error> {
Err(serde::de::Error::custom(
"DynamicClass cannot be deserialized as the class name is not known",
))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DynamicEnum {
pub name: String,
pub value: String,
}
impl DynamicEnum {
pub fn name(&self) -> &str {
&self.name
}
}
impl Serialize for DynamicEnum {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.value)
}
}
impl<'de> serde::Deserialize<'de> for DynamicEnum {
fn deserialize<D: serde::Deserializer<'de>>(_deserializer: D) -> Result<Self, D::Error> {
Err(serde::de::Error::custom(
"DynamicEnum cannot be deserialized as the enum name is not known",
))
}
}
#[derive(Debug, Clone)]
pub struct DynamicUnion<T: KnownTypes, S: KnownTypes> {
pub name: String, pub variant_name: String, pub value: Box<BamlValue<T, S>>, }
impl<T: KnownTypes, S: KnownTypes> DynamicUnion<T, S> {
pub fn name(&self) -> &str {
&self.name
}
}
impl<T: KnownTypes + serde::Serialize, S: KnownTypes + serde::Serialize> serde::Serialize
for DynamicUnion<T, S>
{
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.value.serialize(serializer)
}
}
impl<'de, T: KnownTypes + serde::Deserialize<'de>, S: KnownTypes + serde::Deserialize<'de>>
serde::Deserialize<'de> for DynamicUnion<T, S>
{
fn deserialize<D: serde::Deserializer<'de>>(_deserializer: D) -> Result<Self, D::Error> {
Err(serde::de::Error::custom(
"DynamicUnion cannot be deserialized as the union name is not known",
))
}
}
impl<T: KnownTypes, S: KnownTypes> FullTypeName for DynamicClass<T, S> {
fn full_type_name(&self) -> String {
format!("DynamicClass({})", self.name)
}
}
impl FullTypeName for DynamicEnum {
fn full_type_name(&self) -> String {
format!("DynamicEnum({})", self.name)
}
}
impl<T: KnownTypes, S: KnownTypes> FullTypeName for DynamicUnion<T, S> {
fn full_type_name(&self) -> String {
format!("DynamicUnion({})", self.name)
}
}
use crate::error::BamlTypeName;
impl<T: KnownTypes, S: KnownTypes> BamlTypeName for DynamicClass<T, S> {
const BASE_TYPE_NAME: &'static str = "DynamicClass";
}
impl BamlTypeName for DynamicEnum {
const BASE_TYPE_NAME: &'static str = "DynamicEnum";
}
impl<T: KnownTypes, S: KnownTypes> BamlTypeName for DynamicUnion<T, S> {
const BASE_TYPE_NAME: &'static str = "DynamicUnion";
}