use std::str::FromStr;
use serde_json::Value;
use crate::{impl_str_enum, utils::error::Error};
#[derive(Clone, Debug)]
#[repr(u8)]
pub enum GestureType {
Rock = 0,
Scissors = 1,
Paper = 2,
}
#[derive(Clone, Debug)]
pub enum RedPacketType {
Random,
Average,
Specify,
Heartbeat,
RockPaperScissors,
}
#[derive(Clone, Debug)]
pub struct RedPacket {
pub r#type: RedPacketType,
pub money: u32,
pub count: u32,
pub msg: String,
pub recivers: Vec<String>,
pub gesture: Option<GestureType>,
}
#[derive(Clone, Debug)]
#[allow(non_snake_case)]
pub struct RedPacketGot {
pub userId: String,
pub userName: String,
pub avatar: String,
pub userMoney: u32,
pub time: String,
}
#[derive(Clone, Debug)]
#[allow(non_snake_case)]
pub struct RedPacketMessage {
pub msgType: String,
pub count: u32,
pub got: u32,
pub money: u32,
pub msg: String,
pub senderId: String,
pub GestureType: Option<GestureType>,
pub recivers: Vec<String>,
pub who: Vec<RedPacketGot>,
}
#[derive(Clone, Debug)]
#[allow(non_snake_case)]
pub struct RedPacketBase {
pub count: u32,
pub gesture: Option<GestureType>,
pub got: u32,
pub msg: String,
pub userName: String,
pub userAvatarURL: String,
}
#[derive(Clone, Debug)]
pub struct RedPacketInfo {
pub info: RedPacketBase,
pub recivers: Vec<String>,
pub who: Vec<RedPacketGot>,
}
fn parse_string_list(data: &Value, primary_key: &str, fallback_key: &str) -> Vec<String> {
data.get(primary_key)
.or_else(|| data.get(fallback_key))
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(ToString::to_string))
.collect()
})
.unwrap_or_default()
}
fn parse_gesture(
data: &Value,
primary_key: &str,
fallback_key: &str,
err_ctx: &str,
) -> Result<Option<GestureType>, Error> {
let gesture = data
.get(primary_key)
.and_then(|v| v.as_str())
.or_else(|| data.get(fallback_key).and_then(|v| v.as_str()));
match gesture {
Some(gesture_str) => GestureType::from_str(gesture_str)
.map(Some)
.map_err(|_| Error::Parse(format!("Invalid gesture in {}", err_ctx))),
None => Ok(None),
}
}
fn parse_who_list(data: &Value) -> Result<Vec<RedPacketGot>, Error> {
let Some(who_array) = data.get("who").and_then(|v| v.as_array()) else {
return Ok(Vec::new());
};
let mut got_list = Vec::with_capacity(who_array.len());
for item in who_array {
let user_money = item
.get("userMoney")
.or_else(|| item.get("money"))
.and_then(|v| {
v.as_u64()
.or_else(|| v.as_i64().and_then(|n| if n >= 0 { Some(n as u64) } else { None }))
.or_else(|| v.as_str().and_then(|s| s.parse::<u64>().ok()))
})
.unwrap_or(0) as u32;
got_list.push(RedPacketGot {
userId: item["userId"]
.as_str()
.ok_or_else(|| Error::Parse("Missing userId in who".to_string()))?
.to_string(),
userName: item["userName"]
.as_str()
.ok_or_else(|| Error::Parse("Missing userName in who".to_string()))?
.to_string(),
avatar: item["avatar"]
.as_str()
.ok_or_else(|| Error::Parse("Missing avatar in who".to_string()))?
.to_string(),
userMoney: user_money,
time: item["time"]
.as_str()
.ok_or_else(|| Error::Parse("Missing time in who".to_string()))?
.to_string(),
});
}
Ok(got_list)
}
#[derive(Clone, Debug)]
#[allow(non_snake_case)]
pub struct RedPacketStatusMsg {
pub oId: String,
pub count: u32,
pub got: u32,
pub whoGive: String,
pub whoGot: Vec<String>,
pub avatarURL20: String,
pub avatarURL48: String,
pub avatarURL210: String,
}
impl RedPacketStatusMsg {
pub fn from_value(data: &Value) -> Result<Self, Error> {
Ok(RedPacketStatusMsg {
oId: data["oId"]
.as_str()
.ok_or_else(|| Error::Parse("Missing oId in RedPacketStatusMsg".to_string()))?
.to_string(),
count: data["count"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid count in RedPacketStatusMsg".to_string())
})? as u32,
got: data["got"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid got in RedPacketStatusMsg".to_string())
})? as u32,
whoGive: data["whoGive"]
.as_str()
.ok_or_else(|| Error::Parse("Missing whoGive in RedPacketStatusMsg".to_string()))?
.to_string(),
whoGot: if let Some(who_got_array) = data["whoGot"].as_array() {
who_got_array
.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
} else {
data["whoGot"]
.as_str()
.map(|s| vec![s.to_string()])
.unwrap_or_default()
},
avatarURL20: data["userAvatarURL20"]
.as_str()
.ok_or_else(|| {
Error::Parse("Missing userAvatarURL20 in RedPacketStatusMsg".to_string())
})?
.to_string(),
avatarURL48: data["userAvatarURL48"]
.as_str()
.ok_or_else(|| {
Error::Parse("Missing userAvatarURL48 in RedPacketStatusMsg".to_string())
})?
.to_string(),
avatarURL210: data["userAvatarURL210"]
.as_str()
.ok_or_else(|| {
Error::Parse("Missing userAvatarURL210 in RedPacketStatusMsg".to_string())
})?
.to_string(),
})
}
}
impl Default for RedPacket {
fn default() -> Self {
RedPacket {
r#type: RedPacketType::Random,
money: 32,
count: 1,
msg: "摸鱼者, 事竟成!".to_string(),
recivers: Vec::new(),
gesture: None,
}
}
}
impl RedPacket {
pub fn from_value(data: &Value) -> Result<Self, Error> {
Ok(RedPacket {
r#type: RedPacketType::from_str(
data["type"]
.as_str()
.ok_or_else(|| Error::Parse("Missing type in RedPacket".to_string()))?,
)
.map_err(|_| Error::Parse("Invalid type in RedPacket".to_string()))?,
money: data["money"]
.as_u64()
.ok_or_else(|| Error::Parse("Missing or invalid money in RedPacket".to_string()))?
as u32,
count: data["count"]
.as_u64()
.ok_or_else(|| Error::Parse("Missing or invalid count in RedPacket".to_string()))?
as u32,
msg: data["msg"]
.as_str()
.ok_or_else(|| Error::Parse("Missing msg in RedPacket".to_string()))?
.to_string(),
recivers: parse_string_list(data, "recivers", "receivers"),
gesture: parse_gesture(data, "gesture", "GestureType", "RedPacket")?,
})
}
}
impl RedPacketMessage {
pub fn from_value(data: &Value) -> Result<Self, Error> {
Ok(RedPacketMessage {
msgType: data["msgType"]
.as_str()
.ok_or_else(|| Error::Parse("Missing msgType in RedPacketMessage".to_string()))?
.to_string(),
count: data["count"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid count in RedPacketMessage".to_string())
})? as u32,
got: data["got"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid got in RedPacketMessage".to_string())
})? as u32,
money: data["money"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid money in RedPacketMessage".to_string())
})? as u32,
msg: data["msg"]
.as_str()
.ok_or_else(|| Error::Parse("Missing msg in RedPacketMessage".to_string()))?
.to_string(),
senderId: data["senderId"]
.as_str()
.ok_or_else(|| Error::Parse("Missing senderId in RedPacketMessage".to_string()))?
.to_string(),
GestureType: parse_gesture(data, "gesture", "GestureType", "RedPacketMessage")?,
recivers: parse_string_list(data, "recivers", "receivers"),
who: parse_who_list(data)?,
})
}
}
impl RedPacketBase {
pub fn from_value(data: &Value) -> Result<Self, Error> {
Ok(RedPacketBase {
count: data["count"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid count in RedPacketBase".to_string())
})? as u32,
gesture: parse_gesture(data, "gesture", "GestureType", "RedPacketBase")?,
got: data["got"].as_u64().ok_or_else(|| {
Error::Parse("Missing or invalid got in RedPacketBase".to_string())
})? as u32,
msg: data["msg"]
.as_str()
.ok_or_else(|| Error::Parse("Missing msg in RedPacketBase".to_string()))?
.to_string(),
userName: data["userName"]
.as_str()
.ok_or_else(|| Error::Parse("Missing userName in RedPacketBase".to_string()))?
.to_string(),
userAvatarURL: data["userAvatarURL"]
.as_str()
.ok_or_else(|| Error::Parse("Missing userAvatarURL in RedPacketBase".to_string()))?
.to_string(),
})
}
}
impl RedPacketInfo {
pub fn from_value(data: &Value) -> Result<Self, Error> {
let info_data = &data["info"];
let info = RedPacketBase::from_value(info_data)?;
let recivers = parse_string_list(data, "recivers", "receivers");
let who = parse_who_list(data)?;
Ok(RedPacketInfo {
info,
recivers,
who,
})
}
}
impl_str_enum!(GestureType {
Rock => "石头",
Scissors => "剪刀",
Paper => "布",
});
impl_str_enum!(RedPacketType {
Random => "random",
Average => "average",
Specify => "specify",
Heartbeat => "heartbeat",
RockPaperScissors => "rockPaperScissors",
});