use crate::builtin::derive_common::{
is_numeric_type, is_primitive_type, standalone_fn_name, type_has_derive,
};
use crate::builtin::return_types::{is_none_check, unwrap_option_or_null};
use crate::ts_syn::abi::ir::type_registry::{ResolvedTypeRef, TypeRegistry};
use super::types::OrdField;
pub(crate) fn generate_field_compare_for_interface(
field: &OrdField,
self_var: &str,
other_var: &str,
allow_null: bool,
resolved: Option<&ResolvedTypeRef>,
registry: Option<&TypeRegistry>,
) -> String {
let field_name = &field.name;
let ts_type = &field.ts_type;
let null_return = if allow_null { "null" } else { "0" };
if let (Some(resolved), Some(registry)) = (resolved, registry)
&& !resolved.is_collection
&& resolved.registry_key.is_some()
&& type_has_derive(registry, &resolved.base_type_name, "PartialOrd")
{
let fn_name = standalone_fn_name(&resolved.base_type_name, "PartialCompare");
return format!("{fn_name}({self_var}.{field_name}, {other_var}.{field_name})");
}
if is_numeric_type(ts_type) {
format!(
"({self_var}.{field_name} < {other_var}.{field_name} ? -1 : \
{self_var}.{field_name} > {other_var}.{field_name} ? 1 : 0)"
)
} else if ts_type == "string" {
format!("{self_var}.{field_name}.localeCompare({other_var}.{field_name})")
} else if ts_type == "boolean" {
format!(
"({self_var}.{field_name} === {other_var}.{field_name} ? 0 : \
{self_var}.{field_name} ? 1 : -1)"
)
} else if is_primitive_type(ts_type) {
format!("({self_var}.{field_name} === {other_var}.{field_name} ? 0 : {null_return})")
} else if ts_type.ends_with("[]")
|| ts_type.starts_with("Array<")
|| ts_type.starts_with("ReadonlyArray<")
{
let unwrap_opt = unwrap_option_or_null("optResult");
format!(
"(() => {{ \
const a = {self_var}.{field_name}; \
const b = {other_var}.{field_name}; \
if (!Array.isArray(a) || !Array.isArray(b)) return {null_return}; \
const minLen = Math.min(a.length, b.length); \
for (let i = 0; i < minLen; i++) {{ \
let cmp: number | null; \
if (typeof (a[i] as any)?.compareTo === 'function') {{ \
const optResult = (a[i] as any).compareTo(b[i]); \
cmp = {unwrap_opt}; \
}} else {{ \
cmp = a[i] < b[i] ? -1 : a[i] > b[i] ? 1 : 0; \
}} \
if (cmp === null) return {null_return}; \
if (cmp !== 0) return cmp; \
}} \
return a.length < b.length ? -1 : a.length > b.length ? 1 : 0; \
}})()"
)
} else if ts_type == "Date" {
format!(
"(() => {{ \
const a = {self_var}.{field_name}; \
const b = {other_var}.{field_name}; \
if (!(a instanceof Date) || !(b instanceof Date)) return {null_return}; \
const ta = a.getTime(); \
const tb = b.getTime(); \
return ta < tb ? -1 : ta > tb ? 1 : 0; \
}})()"
)
} else if ts_type == "URL" || ts_type == "URLSearchParams" {
let method = if ts_type == "URL" {
"href"
} else {
"toString()"
};
let a = format!("{self_var}.{field_name}.{method}");
let b = format!("{other_var}.{field_name}.{method}");
format!("((cmp => cmp < 0 ? -1 : cmp > 0 ? 1 : 0)({a}.localeCompare({b})))")
} else {
let unwrap_opt = unwrap_option_or_null("optResult");
let is_none = is_none_check("optResult");
format!(
"(() => {{ \
if (typeof ({self_var}.{field_name} as any)?.compareTo === 'function') {{ \
const optResult = ({self_var}.{field_name} as any).compareTo({other_var}.{field_name}); \
return {is_none} ? {null_return} : {unwrap_opt}; \
}} \
return {self_var}.{field_name} === {other_var}.{field_name} ? 0 : {null_return}; \
}})()"
)
}
}