lingxia-lxapp 0.7.0

LxApp (lightweight application) container and runtime for LingXia framework
use serde_json::Value;

fn normalize_event_name(event_name: &str) -> Option<String> {
    let normalized = event_name.trim().to_lowercase();
    if normalized.is_empty() {
        return None;
    }
    Some(normalized)
}

fn resolve_function_name(bindings_json: &str, event_name: &str) -> Option<String> {
    let bindings: Value = serde_json::from_str(bindings_json).ok()?;
    let object = bindings.as_object()?;

    if let Some(value) = object.get(event_name).and_then(Value::as_str) {
        let function_name = value.trim();
        if !function_name.is_empty() {
            return Some(function_name.to_string());
        }
    }

    for (raw_key, raw_value) in object {
        let key = raw_key.trim().to_lowercase();
        if key != event_name {
            continue;
        }
        if let Some(value) = raw_value.as_str() {
            let function_name = value.trim();
            if !function_name.is_empty() {
                return Some(function_name.to_string());
            }
        }
    }

    None
}

pub fn on_native_component_event(
    appid: &str,
    path: &str,
    _component_id: &str,
    event_name: &str,
    payload_json: &str,
    bindings_json: &str,
) -> bool {
    if appid.is_empty() || path.is_empty() || payload_json.is_empty() || bindings_json.is_empty() {
        return false;
    }

    let Some(normalized_event_name) = normalize_event_name(event_name) else {
        return false;
    };
    let Some(function_name) = resolve_function_name(bindings_json, &normalized_event_name) else {
        return false;
    };

    crate::try_get(appid)
        .and_then(|lxapp| lxapp.get_page(path))
        .map(|page| page.call_js(function_name, payload_json.to_string()))
        .is_some_and(|result| result.is_ok())
}

#[cfg(test)]
mod tests {
    use super::{normalize_event_name, resolve_function_name};

    #[test]
    fn normalize_event_name_keeps_original_name() {
        assert_eq!(
            normalize_event_name("buffering").as_deref(),
            Some("buffering")
        );
    }

    #[test]
    fn normalize_event_name_trims_and_lowercases() {
        assert_eq!(
            normalize_event_name("  PlayRequest  ").as_deref(),
            Some("playrequest")
        );
    }

    #[test]
    fn resolve_function_name_from_exact_key() {
        let bindings = r#"{"timeupdate":"onTimeUpdate"}"#;
        assert_eq!(
            resolve_function_name(bindings, "timeupdate").as_deref(),
            Some("onTimeUpdate")
        );
    }

    #[test]
    fn resolve_function_name_from_case_insensitive_key() {
        let bindings = r#"{"TimeUpdate":"onTimeUpdate"}"#;
        assert_eq!(
            resolve_function_name(bindings, "timeupdate").as_deref(),
            Some("onTimeUpdate")
        );
    }

    #[test]
    fn resolve_function_name_ignores_non_string_values() {
        let bindings = r#"{"timeupdate":123}"#;
        assert!(resolve_function_name(bindings, "timeupdate").is_none());
    }
}