use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(dead_code)]
pub struct SdkManifest {
pub version: String,
pub modules: Vec<SdkModule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SdkModule {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub methods: Vec<SdkMethod>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub functions: Vec<SdkMethod>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SdkMethod {
pub name: String,
pub ffi_name: String,
pub receiver: String,
pub params: Vec<SdkParam>,
pub return_type: SdkReturnType,
pub ffi_return_type: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub ffi_out_params: Vec<SdkParam>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SdkParam {
pub name: String,
#[serde(rename = "type")]
pub param_type: String,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub ffi_params: Vec<SdkParam>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SdkReturnType {
Simple(String),
Result { result: String },
Option { option: String },
}
impl SdkReturnType {
pub fn simple(name: impl Into<String>) -> Self {
Self::Simple(name.into())
}
pub fn result(inner: impl Into<String>) -> Self {
Self::Result {
result: inner.into(),
}
}
pub fn option(inner: impl Into<String>) -> Self {
Self::Option {
option: inner.into(),
}
}
}
pub fn generate_manifest_json(module: &SdkModule) -> String {
serde_json::to_string_pretty(module)
.unwrap_or_else(|e| format!("{{\"error\": \"Failed to serialize manifest: {}\"}}", e))
}
pub fn manifest_const_name(module_name: &str) -> String {
format!(
"GOUD_API_MANIFEST_{}",
module_name.to_uppercase().replace('-', "_")
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sdk_method_serialization() {
let method = SdkMethod {
name: "should_close".to_string(),
ffi_name: "goud_window_should_close".to_string(),
receiver: "ref".to_string(),
params: vec![],
return_type: SdkReturnType::simple("bool"),
ffi_return_type: "bool".to_string(),
ffi_out_params: vec![],
};
let json = serde_json::to_string_pretty(&method).unwrap();
assert!(json.contains("should_close"));
assert!(json.contains("goud_window_should_close"));
}
#[test]
fn test_sdk_module_serialization() {
let module = SdkModule {
name: "window".to_string(),
feature: Some("native".to_string()),
methods: vec![SdkMethod {
name: "should_close".to_string(),
ffi_name: "goud_window_should_close".to_string(),
receiver: "ref".to_string(),
params: vec![],
return_type: SdkReturnType::simple("bool"),
ffi_return_type: "bool".to_string(),
ffi_out_params: vec![],
}],
functions: vec![],
};
let json = generate_manifest_json(&module);
assert!(json.contains("window"));
assert!(json.contains("should_close"));
}
#[test]
fn test_result_return_type() {
let rt = SdkReturnType::result("f32");
let json = serde_json::to_string(&rt).unwrap();
assert!(json.contains("\"result\":\"f32\""));
}
#[test]
fn test_option_return_type() {
let rt = SdkReturnType::option("Contact");
let json = serde_json::to_string(&rt).unwrap();
assert!(json.contains("\"option\":\"Contact\""));
}
#[test]
fn test_manifest_const_name() {
assert_eq!(manifest_const_name("window"), "GOUD_API_MANIFEST_WINDOW");
assert_eq!(
manifest_const_name("renderer-3d"),
"GOUD_API_MANIFEST_RENDERER_3D"
);
}
}