#![allow(dead_code)]
use crate::compiler::v2_map_emission::concrete_type_from_annotation;
use shape_ast::ast::TypeAnnotation;
use shape_value::v2::ConcreteType;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub enum ConstantValue {
I64(i64),
F64(f64),
Bool(bool),
String(Arc<str>),
Array(ConcreteType, Vec<ConstantValue>),
Unit,
None,
Opaque(ConcreteType, [u8; 8]),
}
impl ConstantValue {
pub fn from_str(s: &str) -> Self {
ConstantValue::String(Arc::<str>::from(s))
}
pub fn from_string(s: String) -> Self {
ConstantValue::String(Arc::<str>::from(s.as_str()))
}
pub fn concrete_type(&self) -> ConcreteType {
match self {
ConstantValue::I64(_) => ConcreteType::I64,
ConstantValue::F64(_) => ConcreteType::F64,
ConstantValue::Bool(_) => ConcreteType::Bool,
ConstantValue::String(_) => ConcreteType::String,
ConstantValue::Array(elem, _) => ConcreteType::Array(Box::new(elem.clone())),
ConstantValue::Unit => ConcreteType::Void,
ConstantValue::None => ConcreteType::Option(Box::new(ConcreteType::Void)),
ConstantValue::Opaque(ct, _) => ct.clone(),
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
ConstantValue::I64(v) => Some(*v),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
ConstantValue::F64(v) => Some(*v),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
ConstantValue::Bool(v) => Some(*v),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
ConstantValue::String(s) => Some(s.as_ref()),
_ => None,
}
}
pub fn is_typed(&self) -> bool {
!matches!(self, ConstantValue::Opaque(_, _))
}
}
pub fn type_name_constant(ct: &ConcreteType) -> ConstantValue {
ConstantValue::from_string(ct.to_string())
}
pub fn type_annotation_to_constant_value(ann: &TypeAnnotation) -> Option<ConstantValue> {
let ct = concrete_type_from_annotation(ann)?;
Some(type_name_constant(&ct))
}
#[cfg(test)]
mod tests {
use super::*;
use shape_ast::ast::TypeAnnotation;
use shape_ast::ast::type_path::TypePath;
use shape_value::v2::ConcreteType;
#[test]
fn ctor_string_concrete_type_is_string() {
let v = ConstantValue::from_str("int");
assert_eq!(v.concrete_type(), ConcreteType::String);
assert_eq!(v.as_str(), Some("int"));
assert!(v.is_typed());
}
#[test]
fn ctor_i64_concrete_type_is_i64() {
let v = ConstantValue::I64(42);
assert_eq!(v.concrete_type(), ConcreteType::I64);
assert_eq!(v.as_i64(), Some(42));
assert!(v.is_typed());
}
#[test]
fn ctor_f64_concrete_type_is_f64() {
let v = ConstantValue::F64(3.14);
assert_eq!(v.concrete_type(), ConcreteType::F64);
assert_eq!(v.as_f64(), Some(3.14));
}
#[test]
fn ctor_bool_concrete_type_is_bool() {
let v = ConstantValue::Bool(true);
assert_eq!(v.concrete_type(), ConcreteType::Bool);
assert_eq!(v.as_bool(), Some(true));
}
#[test]
fn array_concrete_type_carries_element_type() {
let v = ConstantValue::Array(
ConcreteType::I64,
vec![ConstantValue::I64(1), ConstantValue::I64(2)],
);
assert_eq!(
v.concrete_type(),
ConcreteType::Array(Box::new(ConcreteType::I64))
);
}
#[test]
fn empty_array_still_types() {
let v = ConstantValue::Array(ConcreteType::F64, vec![]);
assert_eq!(
v.concrete_type(),
ConcreteType::Array(Box::new(ConcreteType::F64))
);
}
#[test]
fn unit_and_none_have_distinct_concrete_types() {
assert_eq!(ConstantValue::Unit.concrete_type(), ConcreteType::Void);
assert_eq!(
ConstantValue::None.concrete_type(),
ConcreteType::Option(Box::new(ConcreteType::Void))
);
}
#[test]
fn opaque_carries_explicit_type_tag() {
let v = ConstantValue::Opaque(ConcreteType::U8, [0; 8]);
assert_eq!(v.concrete_type(), ConcreteType::U8);
assert!(!v.is_typed());
}
#[test]
fn type_name_int_returns_typed_string() {
let v = type_name_constant(&ConcreteType::I64);
assert_eq!(v.concrete_type(), ConcreteType::String);
assert_eq!(v.as_str(), Some("int"));
}
#[test]
fn type_name_number_returns_typed_string() {
let v = type_name_constant(&ConcreteType::F64);
assert_eq!(v.concrete_type(), ConcreteType::String);
assert_eq!(v.as_str(), Some("number"));
}
#[test]
fn type_name_array_of_number() {
let v = type_name_constant(&ConcreteType::Array(Box::new(ConcreteType::F64)));
assert_eq!(v.as_str(), Some("Array<number>"));
}
#[test]
fn type_name_option_int() {
let v = type_name_constant(&ConcreteType::Option(Box::new(ConcreteType::I64)));
assert_eq!(v.as_str(), Some("int?"));
}
#[test]
fn annotation_int_resolves_to_typed_string() {
let ann = TypeAnnotation::Basic("int".into());
let v = type_annotation_to_constant_value(&ann)
.expect("int annotation should resolve");
assert_eq!(v.concrete_type(), ConcreteType::String);
assert_eq!(v.as_str(), Some("int"));
}
#[test]
fn annotation_array_f64_resolves_to_array_number_string() {
let ann = TypeAnnotation::Generic {
name: TypePath::simple("Array"),
args: vec![TypeAnnotation::Basic("f64".into())],
};
let whole = type_annotation_to_constant_value(&ann).expect("Array<f64> resolves");
assert_eq!(whole.as_str(), Some("Array<number>"));
let resolved =
concrete_type_from_annotation(&ann).expect("concrete_type_from_annotation works");
match resolved {
ConcreteType::Array(elem) => {
assert_eq!(*elem, ConcreteType::F64);
let elem_value = type_name_constant(&elem);
assert_eq!(elem_value.as_str(), Some("number"));
assert_eq!(elem_value.concrete_type(), ConcreteType::String);
}
other => panic!("expected Array, got {:?}", other),
}
}
#[test]
fn comptime_for_field_iteration_yields_typed_field_info() {
let fields = [
("x", TypeAnnotation::Basic("number".into())),
("y", TypeAnnotation::Basic("number".into())),
("name", TypeAnnotation::Basic("string".into())),
];
let typed_fields: Vec<(&str, ConstantValue)> = fields
.iter()
.map(|(n, ann)| {
let cv = type_annotation_to_constant_value(ann)
.expect("field type annotation must resolve");
(*n, cv)
})
.collect();
assert_eq!(typed_fields.len(), 3);
for (name, cv) in &typed_fields {
assert_eq!(
cv.concrete_type(),
ConcreteType::String,
"field '{}' should be typed String",
name
);
}
assert_eq!(typed_fields[0].1.as_str(), Some("number"));
assert_eq!(typed_fields[1].1.as_str(), Some("number"));
assert_eq!(typed_fields[2].1.as_str(), Some("string"));
}
#[test]
fn annotation_to_constant_value_returns_none_for_unions() {
let ann = TypeAnnotation::Union(vec![
TypeAnnotation::Basic("int".into()),
TypeAnnotation::Basic("string".into()),
]);
assert!(type_annotation_to_constant_value(&ann).is_none());
}
}