use salvo::prelude::ToSchema;
use serde::{Deserialize, Serialize, de, ser::SerializeStruct};
use serde_json::value::from_value as from_json_value;
use crate::push::PushFormat;
use crate::serde::{JsonObject, JsonValue, RawJsonValue, from_raw_json_value};
#[derive(ToSchema, Serialize, Deserialize, Clone, Debug)]
pub struct HttpPusherData {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<PushFormat>,
#[serde(default, skip_serializing_if = "JsonValue::is_null")]
#[salvo(schema(value_type = Object, additional_properties = true))]
pub default_payload: JsonValue,
}
impl HttpPusherData {
pub fn new(url: String) -> Self {
Self {
url,
format: None,
default_payload: JsonValue::default(),
}
}
}
#[derive(ToSchema, Clone, Debug)]
#[non_exhaustive]
pub enum PusherKind {
Http(HttpPusherData),
Email(EmailPusherData),
#[doc(hidden)]
#[salvo(schema(skip))]
_Custom(CustomPusherData),
}
impl PusherKind {
pub fn try_new(kind: &str, data: JsonValue) -> Result<Self, serde_json::Error> {
match kind.as_ref() {
"http" => from_json_value(data).map(Self::Http),
"email" => Ok(Self::Email(EmailPusherData)),
_ => from_json_value(data).map(Self::_Custom),
}
}
pub fn name(&self) -> &str {
match self {
PusherKind::Http(_) => "http",
PusherKind::Email(_) => "email",
PusherKind::_Custom(data) => data.kind.as_str(),
}
}
pub fn json_data(&self) -> Result<JsonValue, serde_json::Error> {
match self {
PusherKind::Http(data) => serde_json::to_value(data),
PusherKind::Email(data) => serde_json::to_value(data),
PusherKind::_Custom(data) => serde_json::to_value(data),
}
}
}
impl Serialize for PusherKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut st = serializer.serialize_struct("PusherAction", 3)?;
match self {
PusherKind::Http(data) => {
st.serialize_field("kind", &"http")?;
st.serialize_field("data", data)?;
}
PusherKind::Email(_) => {
st.serialize_field("kind", &"email")?;
st.serialize_field("data", &JsonObject::new())?;
}
PusherKind::_Custom(custom) => {
st.serialize_field("kind", &custom.kind)?;
st.serialize_field("data", &custom.data)?;
}
}
st.end()
}
}
#[derive(Debug, Deserialize)]
struct PusherKindDeHelper {
kind: String,
data: Box<RawJsonValue>,
}
impl<'de> Deserialize<'de> for PusherKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let PusherKindDeHelper { kind, data } = from_raw_json_value(&json)?;
match kind.as_ref() {
"http" => from_raw_json_value(&data).map(Self::Http),
"email" => Ok(Self::Email(EmailPusherData)),
_ => from_raw_json_value(&json).map(Self::_Custom),
}
}
}
#[derive(ToSchema, Serialize, Clone, Debug)]
pub struct Pusher {
#[serde(flatten)]
pub ids: PusherIds,
#[serde(flatten)]
pub kind: PusherKind,
pub app_display_name: String,
pub device_display_name: String,
pub lang: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub profile_tag: Option<String>,
}
#[derive(Debug, Deserialize)]
struct PusherDeHelper {
#[serde(flatten)]
ids: PusherIds,
app_display_name: String,
device_display_name: String,
profile_tag: Option<String>,
lang: String,
}
impl<'de> Deserialize<'de> for Pusher {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
let PusherDeHelper {
ids,
app_display_name,
device_display_name,
profile_tag,
lang,
} = from_raw_json_value(&json)?;
let kind = from_raw_json_value(&json)?;
Ok(Self {
ids,
kind,
app_display_name,
device_display_name,
profile_tag,
lang,
})
}
}
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct PusherInit {
pub ids: PusherIds,
pub kind: PusherKind,
pub app_display_name: String,
pub device_display_name: String,
pub profile_tag: Option<String>,
pub lang: String,
}
impl From<PusherInit> for Pusher {
fn from(init: PusherInit) -> Self {
let PusherInit {
ids,
kind,
app_display_name,
device_display_name,
profile_tag,
lang,
} = init;
Self {
ids,
kind,
app_display_name,
device_display_name,
profile_tag,
lang,
}
}
}
#[derive(ToSchema, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PusherIds {
pub pushkey: String,
pub app_id: String,
}
impl PusherIds {
pub fn new(pushkey: String, app_id: String) -> Self {
Self { pushkey, app_id }
}
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug, Default)]
pub struct EmailPusherData;
impl EmailPusherData {
pub fn new() -> Self {
Self::default()
}
}
#[doc(hidden)]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CustomPusherData {
kind: String,
data: JsonObject,
}
#[derive(ToSchema, Clone, Debug, Default, Serialize, Deserialize)]
pub struct PusherData {
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<PushFormat>,
#[serde(default, skip_serializing_if = "JsonValue::is_null")]
pub default_payload: JsonValue,
}
impl PusherData {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
#[cfg(not(feature = "unstable-unspecified"))]
{
self.format.is_none()
}
#[cfg(feature = "unstable-unspecified")]
{
self.format.is_none() && self.default_payload.is_null()
}
}
}
impl From<crate::push::HttpPusherData> for PusherData {
fn from(data: crate::push::HttpPusherData) -> Self {
let crate::push::HttpPusherData {
format,
default_payload,
..
} = data;
Self {
format,
default_payload,
}
}
}