use crate::builtin::derive_common::{
collection_element_type, is_primitive_type, standalone_fn_name, type_has_derive,
};
use crate::ts_syn::abi::ir::type_registry::{ResolvedTypeRef, TypeRegistry};
use super::types::HashField;
pub fn generate_field_hash_for_interface(
field: &HashField,
var: &str,
resolved: Option<&ResolvedTypeRef>,
registry: Option<&TypeRegistry>,
) -> String {
let field_name = &field.name;
let ts_type = &field.ts_type;
if let (Some(resolved), Some(registry)) = (resolved, registry) {
if !resolved.is_collection
&& resolved.registry_key.is_some()
&& type_has_derive(registry, &resolved.base_type_name, "Hash")
{
let fn_name = standalone_fn_name(&resolved.base_type_name, "HashCode");
return format!("{fn_name}({var}.{field_name})");
}
if resolved.is_collection
&& let Some(elem) = collection_element_type(resolved)
&& elem.registry_key.is_some()
&& type_has_derive(registry, &elem.base_type_name, "Hash")
{
let elem_fn = standalone_fn_name(&elem.base_type_name, "HashCode");
let base = resolved.base_type_name.as_str();
match base {
"Map" => {
return format!(
"({var}.{field_name} instanceof Map \
? Array.from({var}.{field_name}.entries()).reduce((h, [k, v]) => \
(h * 31 + String(k).split('').reduce((hh, c) => (hh * 31 + c.charCodeAt(0)) | 0, 0) + \
{elem_fn}(v)) | 0, 0) \
: 0)"
);
}
"Set" => {
return format!(
"({var}.{field_name} instanceof Set \
? Array.from({var}.{field_name}).reduce((h, v) => \
(h * 31 + {elem_fn}(v)) | 0, 0) \
: 0)"
);
}
_ => {
return format!(
"(Array.isArray({var}.{field_name}) \
? {var}.{field_name}.reduce((h, v) => \
(h * 31 + {elem_fn}(v)) | 0, 0) \
: 0)"
);
}
}
}
}
if is_primitive_type(ts_type) {
match ts_type.as_str() {
"number" => {
format!(
"(Number.isInteger({var}.{field_name}) \
? {var}.{field_name} | 0 \
: {var}.{field_name}.toString().split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0))"
)
}
"bigint" => {
format!(
"{var}.{field_name}.toString().split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)"
)
}
"string" => {
format!(
"({var}.{field_name} ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)"
)
}
"boolean" => {
format!("({var}.{field_name} ? 1231 : 1237)")
}
_ => {
format!("({var}.{field_name} != null ? 1 : 0)")
}
}
} else if ts_type.ends_with("[]")
|| ts_type.starts_with("Array<")
|| ts_type.starts_with("ReadonlyArray<")
{
format!(
"(Array.isArray({var}.{field_name}) \
? {var}.{field_name}.reduce((h, v) => \
(h * 31 + (typeof (v as any)?.hashCode === 'function' \
? (v as any).hashCode() \
: (v != null ? String(v).split('').reduce((hh, c) => (hh * 31 + c.charCodeAt(0)) | 0, 0) : 0))) | 0, 0) \
: 0)"
)
} else if ts_type == "Date" {
format!("({var}.{field_name} instanceof Date ? {var}.{field_name}.getTime() | 0 : 0)")
} else if ts_type == "RegExp" {
format!(
"({var}.{field_name}.source + {var}.{field_name}.flags).split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)"
)
} else if ts_type == "URL" {
format!(
"{var}.{field_name}.href.split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)"
)
} else if matches!(
ts_type.as_str(),
"Uint8Array"
| "Int8Array"
| "Uint16Array"
| "Int16Array"
| "Uint32Array"
| "Int32Array"
| "Float32Array"
| "Float64Array"
| "BigInt64Array"
| "BigUint64Array"
| "Uint8ClampedArray"
) {
format!(
"Array.from({var}.{field_name}).reduce((h, v) => (h * 31 + (Number(v) | 0)) | 0, 0)"
)
} else if matches!(
ts_type.as_str(),
"Error"
| "TypeError"
| "RangeError"
| "SyntaxError"
| "ReferenceError"
| "URIError"
| "EvalError"
) {
format!(
"({var}.{field_name}.message ?? '').split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0)"
)
} else if ts_type.starts_with("Map<") {
format!(
"({var}.{field_name} instanceof Map \
? Array.from({var}.{field_name}.entries()).reduce((h, [k, v]) => \
(h * 31 + String(k).split('').reduce((hh, c) => (hh * 31 + c.charCodeAt(0)) | 0, 0) + \
(typeof (v as any)?.hashCode === 'function' ? (v as any).hashCode() : 0)) | 0, 0) \
: 0)"
)
} else if ts_type.starts_with("Set<") {
format!(
"({var}.{field_name} instanceof Set \
? Array.from({var}.{field_name}).reduce((h, v) => \
(h * 31 + (typeof (v as any)?.hashCode === 'function' \
? (v as any).hashCode() \
: (v != null ? String(v).split('').reduce((hh, c) => (hh * 31 + c.charCodeAt(0)) | 0, 0) : 0))) | 0, 0) \
: 0)"
)
} else {
format!(
"(typeof ({var}.{field_name} as any)?.hashCode === 'function' \
? ({var}.{field_name} as any).hashCode() \
: ({var}.{field_name} != null \
? JSON.stringify({var}.{field_name}).split('').reduce((h, c) => (h * 31 + c.charCodeAt(0)) | 0, 0) \
: 0))"
)
}
}