use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RegistrationSpec {
pub method: String,
pub callback_param: String,
pub callback_bound: String,
pub callback_contract: String,
#[serde(default)]
pub variants: Vec<RegistrationVariantSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RegistrationVariantSpec {
pub name: String,
#[serde(default)]
pub fixed: std::collections::BTreeMap<String, String>,
#[serde(default)]
pub doc: Option<String>,
#[serde(default)]
pub style: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct EntrypointSpec {
pub method: String,
pub kind: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ServiceConfig {
pub owner_type: String,
#[serde(default)]
pub constructor: Option<String>,
#[serde(default)]
pub configurators: Vec<String>,
#[serde(default)]
pub registrations: Vec<RegistrationSpec>,
#[serde(default)]
pub entrypoints: Vec<EntrypointSpec>,
#[serde(default)]
pub skip_languages: Vec<String>,
#[serde(default)]
pub host_app_inner_accessor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct HandlerContractConfig {
pub trait_name: String,
pub dispatch_method: String,
#[serde(default = "default_true")]
pub is_async: bool,
#[serde(default)]
pub wire_request_type: Option<String>,
#[serde(default)]
pub wire_response_type: Option<String>,
#[serde(default)]
pub optional_overrides: Vec<String>,
#[serde(default)]
pub dispatch_extra_params: Vec<String>,
#[serde(default)]
pub wire_param_name: Option<String>,
#[serde(default)]
pub dispatch_return_type: Option<String>,
#[serde(default)]
pub response_adapter: Option<String>,
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
pub struct RegistrationVariantLanguageOverrideSpec {
#[serde(default)]
pub style: Option<String>,
#[serde(default)]
pub handler_shape: Option<String>,
#[serde(default)]
pub method_prefix: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn service_config_minimal_deserializes() {
let toml_str = r#"
owner_type = "App"
"#;
let cfg: ServiceConfig = toml::from_str(toml_str).unwrap();
assert_eq!(cfg.owner_type, "App");
assert!(cfg.constructor.is_none());
assert!(cfg.configurators.is_empty());
assert!(cfg.registrations.is_empty());
assert!(cfg.entrypoints.is_empty());
assert!(cfg.skip_languages.is_empty());
}
#[test]
fn service_config_full_roundtrips() {
let toml_str = r#"
owner_type = "App"
constructor = "new"
configurators = ["set_address", "set_tls"]
skip_languages = ["wasm"]
[[registrations]]
method = "add_route"
callback_param = "handler"
callback_bound = "IntoHandler"
callback_contract = "Handler"
[[entrypoints]]
method = "run"
kind = "run"
[[entrypoints]]
method = "into_router"
kind = "finalize"
"#;
let cfg: ServiceConfig = toml::from_str(toml_str).unwrap();
assert_eq!(cfg.owner_type, "App");
assert_eq!(cfg.constructor.as_deref(), Some("new"));
assert_eq!(cfg.configurators, vec!["set_address", "set_tls"]);
assert_eq!(cfg.registrations.len(), 1);
assert_eq!(cfg.registrations[0].method, "add_route");
assert_eq!(cfg.registrations[0].callback_bound, "IntoHandler");
assert_eq!(cfg.registrations[0].callback_contract, "Handler");
assert_eq!(cfg.entrypoints.len(), 2);
assert_eq!(cfg.entrypoints[0].kind, "run");
assert_eq!(cfg.entrypoints[1].kind, "finalize");
assert_eq!(cfg.skip_languages, vec!["wasm"]);
}
#[test]
fn handler_contract_config_defaults() {
let toml_str = r#"
trait_name = "Handler"
dispatch_method = "call"
"#;
let cfg: HandlerContractConfig = toml::from_str(toml_str).unwrap();
assert_eq!(cfg.trait_name, "Handler");
assert_eq!(cfg.dispatch_method, "call");
assert!(cfg.is_async, "is_async should default to true");
assert!(cfg.wire_request_type.is_none());
assert!(cfg.wire_response_type.is_none());
assert!(cfg.optional_overrides.is_empty());
}
#[test]
fn handler_contract_config_full() {
let toml_str = r#"
trait_name = "Handler"
dispatch_method = "call"
is_async = true
wire_request_type = "RequestData"
wire_response_type = "ResponseData"
optional_overrides = ["on_error"]
"#;
let cfg: HandlerContractConfig = toml::from_str(toml_str).unwrap();
assert_eq!(cfg.wire_request_type.as_deref(), Some("RequestData"));
assert_eq!(cfg.wire_response_type.as_deref(), Some("ResponseData"));
assert_eq!(cfg.optional_overrides, vec!["on_error"]);
}
}