misskey_api/model/
note.rs

1use std::collections::HashMap;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5use crate::model::{channel::Channel, drive::DriveFile, id::Id, user::User};
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10use url::Url;
11
12#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
13#[serde(transparent)]
14pub struct Tag(pub String);
15
16impl Display for Tag {
17    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18        use std::fmt::Write;
19        if !self.0.starts_with('#') {
20            f.write_char('#')?;
21        }
22        Display::fmt(&self.0, f)
23    }
24}
25
26impl FromStr for Tag {
27    type Err = std::convert::Infallible;
28    fn from_str(s: &str) -> Result<Tag, Self::Err> {
29        Ok(Tag(s.to_string()))
30    }
31}
32
33impl<S: Into<String>> From<S> for Tag {
34    fn from(s: S) -> Tag {
35        Tag(s.into())
36    }
37}
38
39#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
40#[serde(transparent)]
41pub struct Reaction(pub String);
42
43impl Display for Reaction {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        Display::fmt(&self.0, f)
46    }
47}
48
49impl FromStr for Reaction {
50    type Err = std::convert::Infallible;
51    fn from_str(s: &str) -> Result<Reaction, Self::Err> {
52        Ok(Reaction(s.to_string()))
53    }
54}
55
56impl<S: Into<String>> From<S> for Reaction {
57    fn from(s: S) -> Reaction {
58        Reaction(s.into())
59    }
60}
61
62#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy)]
63#[serde(rename_all = "camelCase")]
64pub enum Visibility {
65    Public,
66    Home,
67    Followers,
68    Specified,
69}
70
71#[derive(Debug, Error, Clone)]
72#[error("invalid note visibility")]
73pub struct ParseVisibilityError {
74    _priv: (),
75}
76
77impl std::str::FromStr for Visibility {
78    type Err = ParseVisibilityError;
79
80    fn from_str(s: &str) -> Result<Visibility, Self::Err> {
81        match s {
82            "public" | "Public" => Ok(Visibility::Public),
83            "home" | "Home" => Ok(Visibility::Home),
84            "followers" | "Followers" => Ok(Visibility::Followers),
85            "specified" | "Specified" => Ok(Visibility::Specified),
86            _ => Err(ParseVisibilityError { _priv: () }),
87        }
88    }
89}
90
91#[derive(Serialize, Deserialize, Debug, Clone)]
92#[serde(rename_all = "camelCase")]
93pub struct PollChoice {
94    pub is_voted: bool,
95    pub text: String,
96    pub votes: u64,
97}
98
99#[derive(Serialize, Deserialize, Debug, Clone)]
100#[serde(rename_all = "camelCase")]
101pub struct Poll {
102    pub choices: Vec<PollChoice>,
103    pub multiple: bool,
104    pub expires_at: Option<DateTime<Utc>>,
105}
106
107// packed `Emoji` for `Note`
108#[derive(Serialize, Deserialize, Debug, Clone)]
109pub struct NoteEmoji {
110    pub name: String,
111    pub url: Url,
112}
113
114// packed `Channel` for `Note`
115#[derive(Serialize, Deserialize, Debug, Clone)]
116pub struct NoteChannel {
117    pub id: Id<Channel>,
118    pub name: String,
119}
120
121#[derive(Serialize, Deserialize, Debug, Clone)]
122#[serde(rename_all = "camelCase")]
123pub struct Note {
124    pub id: Id<Note>,
125    pub created_at: DateTime<Utc>,
126    pub text: Option<String>,
127    #[serde(default)]
128    pub cw: Option<String>,
129    pub user_id: Id<User>,
130    pub user: User,
131    #[serde(default)]
132    pub reply_id: Option<Id<Note>>,
133    #[serde(default)]
134    pub renote_id: Option<Id<Note>>,
135    #[serde(default)]
136    pub reply: Option<Box<Note>>,
137    #[serde(default)]
138    pub renote: Option<Box<Note>>,
139    #[serde(default = "default_false")]
140    pub via_mobile: bool,
141    #[serde(default = "default_false")]
142    pub is_hidden: bool,
143    #[serde(default = "default_false")]
144    pub local_only: bool,
145    pub visibility: Visibility,
146    #[serde(default)]
147    pub mentions: Vec<Id<User>>,
148    #[serde(default)]
149    pub visible_user_ids: Vec<Id<User>>,
150    pub file_ids: Vec<Id<DriveFile>>,
151    pub files: Vec<DriveFile>,
152    #[serde(default)]
153    pub tags: Vec<Tag>,
154    #[serde(default)]
155    pub poll: Option<Poll>,
156    pub reactions: HashMap<Reaction, u64>,
157    pub emojis: Vec<NoteEmoji>,
158    pub renote_count: u64,
159    pub replies_count: u64,
160    #[cfg(feature = "12-47-0")]
161    #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
162    #[serde(default)]
163    pub channel_id: Option<Id<Channel>>,
164    #[cfg(feature = "12-47-0")]
165    #[cfg_attr(docsrs, doc(cfg(feature = "12-47-0")))]
166    #[serde(default)]
167    pub channel: Option<NoteChannel>,
168}
169
170fn default_false() -> bool {
171    false
172}
173
174impl_entity!(Note);