use js_int::UInt;
use ruma_macros::{EventContent, StringEnum};
use serde::{Deserialize, Serialize};
mod zoomlevel_serde;
use ruma_common::MilliSecondsSinceUnixEpoch;
use super::{message::TextContentBlock, room::message::Relation};
use crate::PrivOwnedStr;
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.location", kind = MessageLike, without_relation)]
pub struct LocationEventContent {
#[serde(rename = "org.matrix.msc1767.text")]
pub text: TextContentBlock,
#[serde(rename = "m.location")]
pub location: LocationContent,
#[serde(default, rename = "m.asset", skip_serializing_if = "ruma_common::serde::is_default")]
pub asset: AssetContent,
#[serde(rename = "m.ts", skip_serializing_if = "Option::is_none")]
pub ts: Option<MilliSecondsSinceUnixEpoch>,
#[cfg(feature = "unstable-msc3955")]
#[serde(
default,
skip_serializing_if = "ruma_common::serde::is_default",
rename = "org.matrix.msc1767.automated"
)]
pub automated: bool,
#[serde(
flatten,
skip_serializing_if = "Option::is_none",
deserialize_with = "crate::room::message::relation_serde::deserialize_relation"
)]
pub relates_to: Option<Relation<LocationEventContentWithoutRelation>>,
}
impl LocationEventContent {
pub fn new(text: TextContentBlock, location: LocationContent) -> Self {
Self {
text,
location,
asset: Default::default(),
ts: None,
#[cfg(feature = "unstable-msc3955")]
automated: false,
relates_to: None,
}
}
pub fn with_plain_text(plain_text: impl Into<String>, location: LocationContent) -> Self {
Self {
text: TextContentBlock::plain(plain_text),
location,
asset: Default::default(),
ts: None,
#[cfg(feature = "unstable-msc3955")]
automated: false,
relates_to: None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct LocationContent {
pub uri: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub zoom_level: Option<ZoomLevel>,
}
impl LocationContent {
pub fn new(uri: String) -> Self {
Self { uri, description: None, zoom_level: None }
}
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum ZoomLevelError {
#[error("value too high")]
TooHigh,
}
#[derive(Clone, Debug, Serialize)]
pub struct ZoomLevel(UInt);
impl ZoomLevel {
pub const MIN: u8 = 0;
pub const MAX: u8 = 20;
pub fn new(value: u8) -> Option<Self> {
if value > Self::MAX {
None
} else {
Some(Self(value.into()))
}
}
pub fn get(&self) -> UInt {
self.0
}
}
impl TryFrom<u8> for ZoomLevel {
type Error = ZoomLevelError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::new(value).ok_or(ZoomLevelError::TooHigh)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct AssetContent {
#[serde(rename = "type")]
pub type_: AssetType,
}
impl AssetContent {
pub fn new() -> Self {
Self::default()
}
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
#[ruma_enum(rename_all = "m.snake_case")]
#[non_exhaustive]
pub enum AssetType {
#[default]
#[ruma_enum(rename = "m.self")]
Self_,
Pin,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}