use std::collections::BTreeMap;
use salvo::oapi::{ToParameters, ToSchema};
use serde::{Deserialize, Serialize};
use crate::PrivOwnedStr;
use crate::client::filter::RoomEventFilter;
use crate::events::{AnyStateEvent, AnyTimelineEvent};
use crate::{
OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId,
serde::{RawJson, StringEnum},
};
#[derive(ToParameters, Deserialize, Debug)]
pub struct SearchReqArgs {
#[salvo(parameter(parameter_in = Query))]
pub next_batch: Option<String>,
}
#[derive(ToSchema, Deserialize, Debug)]
pub struct SearchReqBody {
pub search_categories: Categories,
}
#[derive(ToSchema, Serialize, Debug)]
pub struct SearchResBody {
pub search_categories: ResultCategories,
}
impl SearchResBody {
pub fn new(search_categories: ResultCategories) -> Self {
Self { search_categories }
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Categories {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub room_events: Option<Criteria>,
}
impl Categories {
pub fn new() -> Self {
Default::default()
}
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
pub struct Criteria {
pub search_term: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub keys: Option<Vec<SearchKeys>>,
#[serde(skip_serializing_if = "RoomEventFilter::is_empty")]
pub filter: RoomEventFilter,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub order_by: Option<OrderBy>,
#[serde(default, skip_serializing_if = "EventContext::is_default")]
pub event_context: EventContext,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub include_state: Option<bool>,
#[serde(default, skip_serializing_if = "Groupings::is_empty")]
pub groupings: Groupings,
}
impl Criteria {
pub fn new(search_term: String) -> Self {
Self {
search_term,
keys: None,
filter: RoomEventFilter::default(),
order_by: None,
event_context: Default::default(),
include_state: None,
groupings: Default::default(),
}
}
}
#[derive(ToSchema, Deserialize, Serialize, Clone, Debug)]
pub struct EventContext {
#[serde(
default = "default_event_context_limit",
skip_serializing_if = "is_default_event_context_limit"
)]
pub before_limit: u64,
#[serde(
default = "default_event_context_limit",
skip_serializing_if = "is_default_event_context_limit"
)]
pub after_limit: u64,
#[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub include_profile: bool,
}
fn default_event_context_limit() -> u64 {
5
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_default_event_context_limit(val: &u64) -> bool {
*val == default_event_context_limit()
}
impl EventContext {
pub fn new() -> Self {
Self {
before_limit: default_event_context_limit(),
after_limit: default_event_context_limit(),
include_profile: false,
}
}
pub fn is_default(&self) -> bool {
self.before_limit == default_event_context_limit()
&& self.after_limit == default_event_context_limit()
&& !self.include_profile
}
}
impl Default for EventContext {
fn default() -> Self {
Self::new()
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct EventContextResult {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events_after: Vec<RawJson<AnyTimelineEvent>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events_before: Vec<RawJson<AnyTimelineEvent>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub profile_info: BTreeMap<OwnedUserId, UserProfile>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start: Option<String>,
}
impl EventContextResult {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.end.is_none()
&& self.events_after.is_empty()
&& self.events_before.is_empty()
&& self.profile_info.is_empty()
&& self.start.is_none()
}
}
#[derive(ToSchema, Clone, Default, Debug, Deserialize, Serialize)]
pub struct Grouping {
pub key: Option<GroupingKey>,
}
impl Grouping {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.key.is_none()
}
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(ToSchema, Clone, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
#[palpo_enum(rename_all = "snake_case")]
#[non_exhaustive]
pub enum GroupingKey {
RoomId,
Sender,
#[doc(hidden)]
#[salvo(schema(skip))]
_Custom(PrivOwnedStr),
}
#[derive(ToSchema, Clone, Default, Debug, Deserialize, Serialize)]
pub struct Groupings {
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub group_by: Vec<Grouping>,
}
impl Groupings {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.group_by.is_empty()
}
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(ToSchema, Clone, PartialEq, Eq, StringEnum)]
#[non_exhaustive]
pub enum SearchKeys {
#[palpo_enum(rename = "content.body")]
ContentBody,
#[palpo_enum(rename = "content.name")]
ContentName,
#[palpo_enum(rename = "content.topic")]
ContentTopic,
#[doc(hidden)]
#[salvo(schema(skip))]
_Custom(PrivOwnedStr),
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(ToSchema, Clone, PartialEq, Eq, StringEnum)]
#[palpo_enum(rename_all = "snake_case")]
pub enum OrderBy {
Recent,
Rank,
#[doc(hidden)]
#[salvo(schema(skip))]
_Custom(PrivOwnedStr),
}
#[derive(ToSchema, Clone, Default, Debug, Deserialize, Serialize)]
pub struct ResultCategories {
#[serde(default, skip_serializing_if = "ResultRoomEvents::is_empty")]
pub room_events: ResultRoomEvents,
}
impl ResultCategories {
pub fn new() -> Self {
Default::default()
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct ResultRoomEvents {
#[serde(skip_serializing_if = "Option::is_none")]
pub count: Option<u64>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub groups: BTreeMap<GroupingKey, BTreeMap<OwnedRoomIdOrUserId, ResultGroup>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_batch: Option<String>,
#[serde(default)]
pub results: Vec<SearchResult>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub state: BTreeMap<OwnedRoomId, Vec<RawJson<AnyStateEvent>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub highlights: Vec<String>,
}
impl ResultRoomEvents {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.count.is_none()
&& self.groups.is_empty()
&& self.next_batch.is_none()
&& self.results.is_empty()
&& self.state.is_empty()
&& self.highlights.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct ResultGroup {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next_batch: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub order: Option<u64>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub results: Vec<OwnedEventId>,
}
impl ResultGroup {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.next_batch.is_none() && self.order.is_none() && self.results.is_empty()
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct SearchResult {
#[serde(default, skip_serializing_if = "EventContextResult::is_empty")]
pub context: EventContextResult,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rank: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub result: Option<RawJson<AnyTimelineEvent>>,
}
impl SearchResult {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.context.is_empty() && self.rank.is_none() && self.result.is_none()
}
}
#[derive(ToSchema, Clone, Debug, Default, Deserialize, Serialize)]
pub struct UserProfile {
#[serde(
skip_serializing_if = "Option::is_none",
default,
deserialize_with = "crate::serde::empty_string_as_none"
)]
pub avatar_url: Option<OwnedMxcUri>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
}
impl UserProfile {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.avatar_url.is_none() && self.display_name.is_none()
}
}
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[allow(clippy::exhaustive_enums)]
pub enum OwnedRoomIdOrUserId {
RoomId(OwnedRoomId),
UserId(OwnedUserId),
}