diff --git a/src/core.rs b/src/core.rs index 96de648..f29caad 100644 --- a/src/core.rs +++ b/src/core.rs @@ -18,10 +18,7 @@ pub(crate) trait Core: Send + Sync { #[derive(Serialize)] pub(crate) struct ClientConfig { - #[serde( - rename = "serviceAccountToken", - skip_serializing_if = "String::is_empty" - )] + #[serde(rename = "serviceAccountToken")] pub sa_token: String, #[serde(rename = "programmingLanguage")] pub language: String, @@ -41,7 +38,7 @@ pub(crate) struct ClientConfig { pub system_os_version: String, #[serde(rename = "architecture")] pub system_arch: String, - #[serde(skip_serializing)] + #[serde(rename = "account_name", skip_serializing_if = "Option::is_none")] pub account_name: Option, } @@ -183,22 +180,23 @@ mod tests { } #[test] - fn service_account_config_serializes_token() { + fn service_account_config_omits_account_name() { let mut config = ClientConfig::new_default(); config.sa_token = "ops_test".to_string(); let value = serde_json::to_value(&config).unwrap(); assert_eq!(value["serviceAccountToken"], "ops_test"); + assert!(value.get("account_name").is_none()); } #[test] - fn desktop_config_omits_auth_fields_from_core_config() { + fn desktop_config_includes_account_name() { let mut config = ClientConfig::new_default(); config.account_name = Some("myaccount".to_string()); let value = serde_json::to_value(&config).unwrap(); - assert!(value.get("serviceAccountToken").is_none()); - assert!(value.get("account_name").is_none()); + assert_eq!(value["serviceAccountToken"], ""); + assert_eq!(value["account_name"], "myaccount"); } struct InvalidUtf8Core; diff --git a/src/core_shared_lib.rs b/src/core_shared_lib.rs index 7e5c848..4df2140 100644 --- a/src/core_shared_lib.rs +++ b/src/core_shared_lib.rs @@ -37,23 +37,33 @@ struct Request { } /// JSON response envelope received from the shared library. -/// Go's json.Marshal encodes `[]byte` as a base64 string. +/// The payload may arrive as either a base64 string (Go's json.Marshal of []byte) +/// or a raw JSON byte array, depending on the library version. #[derive(Deserialize)] struct Response { success: bool, - #[serde(with = "base64_payload")] + #[serde(with = "flexible_payload")] payload: Vec, } -mod base64_payload { +mod flexible_payload { use base64::Engine; use serde::{Deserialize, Deserializer}; + #[derive(Deserialize)] + #[serde(untagged)] + enum BytesOrBase64 { + Bytes(Vec), + Base64(String), + } + pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { - let s = String::deserialize(deserializer)?; - base64::engine::general_purpose::STANDARD - .decode(&s) - .map_err(serde::de::Error::custom) + match BytesOrBase64::deserialize(deserializer)? { + BytesOrBase64::Bytes(v) => Ok(v), + BytesOrBase64::Base64(s) => base64::engine::general_purpose::STANDARD + .decode(&s) + .map_err(serde::de::Error::custom), + } } } @@ -284,4 +294,17 @@ mod tests { assert!(response.success); assert_eq!(response.payload, b"payload"); } + + #[test] + fn response_payload_deserializes_from_byte_array() { + let response_json = serde_json::json!({ + "success": false, + "payload": [104, 101, 108, 108, 111], + }) + .to_string(); + + let response: Response = serde_json::from_str(&response_json).unwrap(); + assert!(!response.success); + assert_eq!(response.payload, b"hello"); + } }