use std::collections::HashMap;
#[derive(Debug, thiserror::Error, Clone)]
pub enum BamlError {
#[error("internal error: {0}")]
Internal(String),
#[error("type error: expected {expected}, got {got}")]
TypeCheck { expected: String, got: String },
}
pub trait FullTypeName {
fn full_type_name(&self) -> String;
}
impl BamlError {
pub fn internal(msg: impl Into<String>) -> Self {
BamlError::Internal(msg.into())
}
pub fn type_check<T: BamlTypeName>(got: &impl FullTypeName) -> Self {
BamlError::TypeCheck {
expected: T::baml_type_name(),
got: got.full_type_name(),
}
}
}
pub trait BamlTypeName {
const BASE_TYPE_NAME: &'static str;
fn baml_type_name() -> String {
Self::BASE_TYPE_NAME.to_string()
}
}
impl BamlTypeName for String {
const BASE_TYPE_NAME: &'static str = "String";
}
impl BamlTypeName for i64 {
const BASE_TYPE_NAME: &'static str = "Int";
}
impl BamlTypeName for f64 {
const BASE_TYPE_NAME: &'static str = "Float";
}
impl BamlTypeName for bool {
const BASE_TYPE_NAME: &'static str = "Bool";
}
impl BamlTypeName for () {
const BASE_TYPE_NAME: &'static str = "Null";
}
impl BamlTypeName for &str {
const BASE_TYPE_NAME: &'static str = "String";
}
impl<T: BamlTypeName> BamlTypeName for Vec<T> {
const BASE_TYPE_NAME: &'static str = "List";
fn baml_type_name() -> String {
format!("List<{}>", T::baml_type_name())
}
}
impl<T: BamlTypeName> BamlTypeName for Option<T> {
const BASE_TYPE_NAME: &'static str = "Optional";
fn baml_type_name() -> String {
format!("Optional<{}>", T::baml_type_name())
}
}
impl<V: BamlTypeName> BamlTypeName for HashMap<String, V> {
const BASE_TYPE_NAME: &'static str = "Map";
fn baml_type_name() -> String {
format!("Map<String, {}>", V::baml_type_name())
}
}
impl<T: BamlTypeName> BamlTypeName for crate::types::Checked<T> {
const BASE_TYPE_NAME: &'static str = "Checked";
fn baml_type_name() -> String {
format!("Checked<{}>", T::baml_type_name())
}
}
impl<T: BamlTypeName> BamlTypeName for crate::types::StreamState<T> {
const BASE_TYPE_NAME: &'static str = "StreamState";
fn baml_type_name() -> String {
format!("StreamState<{}>", T::baml_type_name())
}
}
impl<T: BamlTypeName> BamlTypeName for Box<T> {
const BASE_TYPE_NAME: &'static str = T::BASE_TYPE_NAME;
fn baml_type_name() -> String {
T::baml_type_name()
}
}
pub(crate) struct Unknown;
impl BamlTypeName for Unknown {
const BASE_TYPE_NAME: &'static str = "?";
}
impl<T> BamlTypeName for &[T] {
const BASE_TYPE_NAME: &'static str = "List";
fn baml_type_name() -> String {
"List<?>".to_string()
}
}
impl<K, V> BamlTypeName for &HashMap<K, V> {
const BASE_TYPE_NAME: &'static str = "Map";
fn baml_type_name() -> String {
"Map<String, ?>".to_string()
}
}
#[macro_export]
macro_rules! baml_unreachable {
($($arg:tt)*) => {{
panic!(
"\n\n\
========================================\n\
BAML Internal Error\n\
========================================\n\n\
{}\n\n\
This is a bug in BAML. Please report it:\n\
- GitHub: https://github.com/BoundaryML/baml/issues\n\
- Discord: https://boundaryml.com/discord\n\n\
Include this error message and steps to reproduce.\n\
========================================\n",
format_args!($($arg)*)
)
}};
}