walle-core 0.6.0

Onebot Lib in Rust
Documentation
use crate::{
    prelude::WalleError,
    util::{Value, ValueMap, ValueMapExt},
    value, value_map,
};
use walle_macro::{_OneBot as OneBot, _PushToValueMap as PushToValueMap};

pub type Segments = Vec<MessageSegment>;

#[derive(Debug, Clone, PartialEq)]
pub struct MessageSegment {
    pub ty: String,
    pub data: ValueMap,
}

impl ValueMapExt for MessageSegment {
    fn get_downcast<T>(&self, key: &str) -> Result<T, WalleError>
    where
        T: TryFrom<Value, Error = WalleError>,
    {
        self.data.get_downcast(key)
    }
    fn remove_downcast<T>(&mut self, key: &str) -> Result<T, WalleError>
    where
        T: TryFrom<Value, Error = WalleError>,
    {
        self.data.remove_downcast(key)
    }
    fn try_get_downcast<T>(&self, key: &str) -> Result<Option<T>, WalleError>
    where
        T: TryFrom<Value, Error = WalleError>,
    {
        self.data.try_get_downcast(key)
    }
    fn try_remove_downcast<T>(&mut self, key: &str) -> Result<Option<T>, WalleError>
    where
        T: TryFrom<Value, Error = WalleError>,
    {
        self.data.try_remove_downcast(key)
    }
    fn push<T>(&mut self, value: T)
    where
        T: crate::util::PushToValueMap,
    {
        value.push_to(&mut self.data)
    }
}

impl MessageSegment {
    pub fn alt(&self) -> String {
        if self.ty == "text" {
            self.data.get_downcast("text").unwrap_or_default()
        } else {
            if self.data.is_empty() {
                format!("[{}]", self.ty)
            } else {
                let mut content = serde_json::to_string(&self.data).unwrap_or_default();
                content.pop();
                content.remove(0);
                format!("[{},{}]", self.ty, content)
            }
        }
    }
}

impl From<MessageSegment> for Value {
    fn from(segment: MessageSegment) -> Self {
        value!({
            "type": segment.ty,
            "data": segment.data
        })
    }
}

impl TryFrom<Value> for MessageSegment {
    type Error = WalleError;
    fn try_from(value: Value) -> Result<Self, Self::Error> {
        if let Value::Map(mut map) = value {
            Ok(Self {
                ty: map.remove_downcast("type")?,
                data: map
                    .remove("data")
                    .ok_or(WalleError::MapMissedKey("data".to_string()))?
                    .downcast_map()?,
            })
        } else {
            Err(WalleError::ValueTypeNotMatch(
                "map".to_string(),
                format!("{:?}", value),
            ))
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct BaseSegment<T> {
    pub segment: T,
    pub extra: ValueMap,
}

impl<T: for<'a> TryFrom<&'a mut MessageSegment, Error = WalleError>> TryFrom<MessageSegment>
    for BaseSegment<T>
{
    type Error = WalleError;
    fn try_from(mut segment: MessageSegment) -> Result<Self, Self::Error> {
        Ok(Self {
            segment: T::try_from(&mut segment)?,
            extra: segment.data,
        })
    }
}

pub trait IntoMessage {
    fn into_message(self) -> Segments;
}

impl IntoMessage for Segments {
    fn into_message(self) -> Segments {
        self
    }
}

impl<T: Into<MessageSegment>> IntoMessage for T {
    fn into_message(self) -> Segments {
        vec![self.into()]
    }
}

impl From<String> for MessageSegment {
    fn from(text: String) -> Self {
        MessageSegment {
            ty: "text".to_string(),
            data: value_map! { "text": text },
        }
    }
}

impl From<&str> for MessageSegment {
    fn from(text: &str) -> Self {
        MessageSegment {
            ty: "text".to_string(),
            data: value_map! { "text": text },
        }
    }
}

pub trait SegmentDeclare {
    fn ty(&self) -> &'static str;
    fn check(segment: &MessageSegment) -> bool;
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Text {
    pub text: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Mention {
    pub user_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct MentionAll {}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Image {
    pub file_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Voice {
    pub file_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Audio {
    pub file_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Video {
    pub file_id: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct File {
    pub file_id: String,
}

#[derive(Debug, Clone, PartialEq, PushToValueMap, OneBot)]
#[segment]
pub struct Location {
    pub latitude: f64,
    pub longitude: f64,
    pub title: String,
    pub content: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PushToValueMap, OneBot)]
#[segment]
pub struct Reply {
    pub message_id: String,
    pub user_id: String,
}

pub trait MessageExt {
    fn extract_plain_text(&self) -> String;
    fn extract<T: TryFrom<MessageSegment>>(self) -> Vec<T>;
}

impl MessageExt for Segments {
    fn extract_plain_text(&self) -> String {
        self.iter()
            .filter_map(|segment| {
                if segment.ty == "text" {
                    Some(segment.alt())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>()
            .join("\n")
    }
    fn extract<T: TryFrom<MessageSegment>>(self) -> Vec<T> {
        self.into_iter()
            .filter_map(|seg| T::try_from(seg).ok())
            .collect()
    }
}