use std::collections::{HashMap, HashSet};
use crate::parser::ast::types::Type;
use super::ModuleMetadata;
pub fn infer_copy_from_metadata_structs_pub(
all_struct_fields: &HashMap<String, Vec<Vec<String>>>,
existing_copy: &mut Vec<String>,
) {
infer_copy_from_metadata_structs(all_struct_fields, existing_copy);
}
pub(in crate::metadata) fn infer_copy_from_metadata_structs(
all_struct_fields: &HashMap<String, Vec<Vec<String>>>,
existing_copy: &mut Vec<String>,
) {
let mut copy_set: HashSet<String> = existing_copy.iter().cloned().collect();
const MAX_PASSES: usize = 32;
for _ in 0..MAX_PASSES {
let mut changed = false;
for (struct_name, variants) in all_struct_fields {
if copy_set.contains(struct_name) {
continue;
}
let all_variants_copy = variants.iter().all(|field_types| {
field_types
.iter()
.all(|ft| is_copy_type_string(ft, ©_set))
});
if all_variants_copy {
copy_set.insert(struct_name.clone());
changed = true;
}
}
if !changed {
break;
}
}
for name in ©_set {
if !existing_copy.contains(name) {
existing_copy.push(name.clone());
}
}
}
fn is_copy_type_string(s: &str, copy_set: &HashSet<String>) -> bool {
match s {
"Bool" | "Int32" | "Float" => true,
s if s.starts_with("Custom(\"") && s.ends_with("\")") => {
let name = &s[8..s.len() - 2];
matches!(
name,
"f32"
| "f64"
| "i8"
| "i16"
| "i32"
| "i64"
| "i128"
| "u8"
| "u16"
| "u32"
| "u64"
| "u128"
| "usize"
| "isize"
| "bool"
| "char"
) || copy_set.contains(name)
}
s if s.starts_with("Array(") => {
let inner = &s[6..s.len() - 1];
if let Some(comma_pos) = inner.rfind(", ") {
let ty_str = &inner[..comma_pos];
is_copy_type_string(ty_str.trim(), copy_set)
} else {
false
}
}
_ => false,
}
}
impl ModuleMetadata {
pub fn serialize_type(ty: &Type) -> String {
format!("{:?}", ty)
}
pub fn deserialize_type(s: &str) -> Option<Type> {
match s {
"Custom(\"f32\")" => Some(Type::Custom("f32".to_string())),
"Custom(\"f64\")" => Some(Type::Custom("f64".to_string())),
"Custom(\"i32\")" => Some(Type::Custom("i32".to_string())),
"Custom(\"u32\")" => Some(Type::Custom("u32".to_string())),
"Custom(\"Self\")" => Some(Type::Custom("Self".to_string())),
"Int32" => Some(Type::Int32),
"Float" => Some(Type::Float),
"Bool" => Some(Type::Bool),
"String" => Some(Type::String),
"string" | "Custom(\"string\")" => Some(Type::String),
s if s.starts_with("Array(") && s.ends_with(')') => {
let inner = &s[6..s.len() - 1];
if let Some(comma_pos) = inner.rfind(", ") {
let (ty_str, n_str) = inner.split_at(comma_pos);
let n_str = n_str.trim_start_matches(", ");
if let (Some(inner_ty), Ok(n)) = (
Self::deserialize_type(ty_str.trim()),
n_str.parse::<usize>(),
) {
return Some(Type::Array(Box::new(inner_ty), n));
}
}
None
}
s if s.starts_with("Vec(") && s.ends_with(')') => {
let inner = &s[4..s.len() - 1];
Self::deserialize_type(inner).map(|t| Type::Vec(Box::new(t)))
}
s if s.starts_with("Option(") && s.ends_with(')') => {
let inner = &s[7..s.len() - 1];
Self::deserialize_type(inner).map(|t| Type::Option(Box::new(t)))
}
s if s.starts_with("Reference(") && s.ends_with(')') => {
let inner = &s[10..s.len() - 1];
Self::deserialize_type(inner).map(|t| Type::Reference(Box::new(t)))
}
s if s.starts_with("MutableReference(") && s.ends_with(')') => {
let inner = &s[17..s.len() - 1];
Self::deserialize_type(inner).map(|t| Type::MutableReference(Box::new(t)))
}
s if s.starts_with("Custom(") => {
let rest = s
.strip_prefix("Custom(\"")
.and_then(|r| r.strip_suffix("\")"));
rest.map(|name| Type::Custom(name.to_string()))
}
_ => None,
}
}
}