use cow_sdk_core::{Address, AppCode};
use serde::de::Deserializer;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
AppDataDoc, AppDataError, AppDataValidated, info::app_data_info, schema::generate_app_data_doc,
};
use super::MetadataMap;
#[allow(
clippy::derive_partial_eq_without_eq,
reason = "the `metadata: MetadataMap` field is a `serde_json::Map<String, serde_json::Value>` alias, and `serde_json::Value` does not implement `Eq`"
)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct AppDataParams {
pub app_code: Option<AppCode>,
pub environment: Option<String>,
pub signer: Option<Address>,
pub flashloan: Option<crate::metadata::FlashloanHints>,
pub hooks: Option<crate::metadata::HookList>,
pub metadata: MetadataMap,
}
impl Serialize for AppDataParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut state = serializer.serialize_map(None)?;
if let Some(app_code) = &self.app_code {
state.serialize_entry("appCode", app_code)?;
}
if let Some(environment) = &self.environment {
state.serialize_entry("environment", environment)?;
}
let metadata_value = self
.metadata_wire_value()
.map_err(serde::ser::Error::custom)?;
state.serialize_entry("metadata", &metadata_value)?;
state.end()
}
}
impl<'de> Deserialize<'de> for AppDataParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wire {
#[serde(default, rename = "appCode")]
app_code: Option<AppCode>,
#[serde(default)]
environment: Option<String>,
#[serde(default)]
metadata: MetadataMap,
}
let Wire {
app_code,
environment,
mut metadata,
} = Wire::deserialize(deserializer)?;
let signer = match metadata.remove("signer") {
Some(value) => Some(serde_json::from_value::<Address>(value).map_err(|_| {
<D::Error as serde::de::Error>::custom("metadata.signer is not a valid address")
})?),
None => None,
};
let flashloan = match metadata.remove("flashloan") {
Some(value) => Some(
serde_json::from_value::<crate::metadata::FlashloanHints>(value).map_err(|_| {
<D::Error as serde::de::Error>::custom(
"metadata.flashloan failed flash-loan hints validation",
)
})?,
),
None => None,
};
let hooks = match metadata.get("hooks").cloned() {
Some(value) => Some(
serde_json::from_value::<crate::metadata::HookList>(value).map_err(|_| {
<D::Error as serde::de::Error>::custom("metadata.hooks failed hooks validation")
})?,
),
None => None,
};
Ok(Self {
app_code,
environment,
signer,
flashloan,
hooks,
metadata,
})
}
}
impl AppDataParams {
#[must_use]
pub fn new(app_code: AppCode) -> Self {
Self {
app_code: Some(app_code),
environment: None,
signer: None,
flashloan: None,
hooks: None,
metadata: MetadataMap::new(),
}
}
#[must_use]
pub fn with_environment(mut self, environment: impl Into<String>) -> Self {
self.environment = Some(environment.into());
self
}
#[must_use]
pub const fn with_signer(mut self, signer: Address) -> Self {
self.signer = Some(signer);
self
}
#[must_use]
pub const fn with_flashloan(mut self, flashloan: crate::metadata::FlashloanHints) -> Self {
self.flashloan = Some(flashloan);
self
}
#[must_use]
pub fn with_hooks(mut self, hooks: crate::metadata::HookList) -> Self {
self.hooks = Some(hooks);
self
}
#[must_use]
pub fn with_metadata(mut self, metadata: MetadataMap) -> Self {
self.metadata = metadata;
self
}
#[must_use]
pub fn into_doc(self) -> AppDataDoc {
generate_app_data_doc(self)
}
pub fn into_validated(self) -> Result<AppDataValidated, AppDataError> {
app_data_info(self.into_doc())
}
pub fn metadata_wire_value(&self) -> Result<Value, AppDataError> {
let mut metadata = self.metadata.clone();
if let Some(signer) = &self.signer {
metadata.insert("signer".to_owned(), Value::String(signer.to_hex_string()));
}
if let Some(flashloan) = &self.flashloan {
metadata.insert(
"flashloan".to_owned(),
serde_json::to_value(flashloan).map_err(AppDataError::from)?,
);
}
if let Some(hooks) = &self.hooks {
metadata.insert(
"hooks".to_owned(),
serde_json::to_value(hooks).map_err(AppDataError::from)?,
);
}
Ok(Value::Object(metadata))
}
}