concord 2.5.0

A terminal user interface client for Discord
use serde_json::Value;

use crate::discord::{
    events::{AppEvent, ChannelUnreadInfo},
    ids::marker::{ChannelMarker, GuildMarker, MessageMarker},
};

use super::shared::parse_id;

pub(super) fn parse_feature_read_state_ack(data: &Value) -> Option<AppEvent> {
    Some(AppEvent::FeatureReadStateAck {
        read_state_type: u8::try_from(data.get("ack_type")?.as_u64()?).ok()?,
        resource_id: parse_snowflake(data.get("resource_id")?)?,
        entity_id: parse_snowflake(data.get("entity_id")?)?,
        version: data.get("version")?.as_u64()?,
    })
}

pub(super) fn parse_channel_pins_ack(data: &Value) -> Option<AppEvent> {
    Some(AppEvent::ChannelPinsAck {
        channel_id: parse_id::<ChannelMarker>(data.get("channel_id")?)?,
        timestamp: data.get("timestamp")?.as_str()?.to_owned(),
        version: data.get("version")?.as_u64()?,
    })
}

pub(super) fn parse_channel_unread_update(data: &Value) -> Option<AppEvent> {
    let guild_id = parse_id::<GuildMarker>(data.get("guild_id")?)?;
    let channels = data
        .get("channel_unread_updates")?
        .as_array()?
        .iter()
        .filter_map(parse_channel_unread)
        .collect();
    Some(AppEvent::ChannelUnreadUpdate { guild_id, channels })
}

fn parse_channel_unread(value: &Value) -> Option<ChannelUnreadInfo> {
    Some(ChannelUnreadInfo {
        channel_id: parse_id::<ChannelMarker>(value.get("id")?)?,
        last_message_id: value.get("last_message_id").map(parse_id::<MessageMarker>),
        last_pin_timestamp: value
            .get("last_pin_timestamp")
            .map(|timestamp| timestamp.as_str().map(str::to_owned)),
    })
}

fn parse_snowflake(value: &Value) -> Option<u64> {
    value
        .as_str()
        .and_then(|value| value.parse().ok())
        .or_else(|| value.as_u64())
        .filter(|value| *value != 0)
}