misskey_util/builder/
me.rs

1use crate::Error;
2
3use misskey_api::model::{
4    drive::DriveFile,
5    page::Page,
6    query::Query,
7    user::{User, UserField},
8};
9use misskey_api::{endpoint, EntityRef};
10use misskey_core::Client;
11
12/// Conversion to fields in users' profile.
13///
14/// The purpose of this trait is to specify the type that the [`set_fields`][set_fields] method
15/// takes as its parameter. This trait is implemented for arrays of length from 1 to 4, whose
16/// elements are name-value string pairs or [`UserField`][user_field] objects.
17///
18/// [set_fields]: MeUpdateBuilder::set_fields
19/// [user_field]: misskey_api::model::user::UserField
20pub trait IntoUserFields {
21    /// Performs the conversion.
22    fn into_user_fields(self) -> [Option<UserField>; 4];
23}
24
25macro_rules! impl_into_field_requests {
26    (expand default) => { None };
27    (expand $i:ident) => { Some($i) };
28    (expand_pair default $name:ident $value:ident) => { None };
29    (expand_pair $i:ident $name:ident $value:ident) => {
30        Some(UserField {
31            name: $name.into(),
32            value: $value.into(),
33        })
34    };
35    ($len:expr; $($in_field:ident),* => $($out_field:ident),*) => {
36        paste::paste! {
37            impl IntoUserFields for [UserField; $len] {
38                fn into_user_fields(self) -> [Option<UserField>; 4] {
39                    let [$($in_field,)*] = self;
40                    [$(impl_into_field_requests!(expand $out_field), )*]
41                }
42            }
43            impl<T, U> IntoUserFields for [(T, U); $len] where T: Into<String>, U: Into<String> {
44                fn into_user_fields(self) -> [Option<UserField>; 4] {
45                    let [$(([<name_ $in_field>], [<value_ $in_field>]),)*] = self;
46                    [$(impl_into_field_requests!(expand_pair $out_field [<name_ $out_field>] [<value_ $out_field>]), )*]
47                }
48            }
49        }
50    };
51}
52
53impl_into_field_requests! { 1; f1 => f1, default, default, default }
54impl_into_field_requests! { 2; f1, f2 => f1, f2, default, default }
55impl_into_field_requests! { 3; f1, f2, f3 => f1, f2, f3, default }
56impl_into_field_requests! { 4; f1, f2, f3, f4 => f1, f2, f3, f4 }
57
58/// Builder for the [`update_me`][`crate::ClientExt::update_me`] method.
59pub struct MeUpdateBuilder<C> {
60    client: C,
61    request: endpoint::i::update::Request,
62}
63
64impl<C> MeUpdateBuilder<C> {
65    /// Creates a builder with the client.
66    pub fn new(client: C) -> Self {
67        MeUpdateBuilder {
68            client,
69            request: endpoint::i::update::Request::default(),
70        }
71    }
72
73    /// Gets the request object for reuse.
74    pub fn as_request(&self) -> &endpoint::i::update::Request {
75        &self.request
76    }
77
78    update_builder_string_option_field! {
79        pub name;
80        pub description;
81        pub language { lang };
82        pub location;
83        pub birthday;
84    }
85
86    update_builder_option_field! {
87        pub avatar: impl EntityRef<DriveFile> { avatar_id = avatar.entity_ref() };
88        pub banner: impl EntityRef<DriveFile> { banner_id = banner.entity_ref() };
89        #[doc_name = "pinned page"]
90        pub pinned_page: impl EntityRef<Page> { pinned_page_id = pinned_page.entity_ref() };
91    }
92
93    /// Sets the fields in this user's profile.
94    ///
95    /// Since the user has four fields, it takes an array of length 1 to 4 as its argument.
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// # use misskey_util::ClientExt;
101    /// # #[tokio::main]
102    /// # async fn main() -> anyhow::Result<()> {
103    /// # let client = misskey_test::test_client().await?;
104    /// client
105    ///     .update_me()
106    ///     .set_fields([
107    ///         ("Website", "https://example.com/"),
108    ///         ("Twitter", "@username"),
109    ///     ])
110    ///     .update()
111    ///     .await?;
112    /// # Ok(())
113    /// # }
114    /// ```
115    pub fn set_fields(&mut self, fields: impl IntoUserFields) -> &mut Self {
116        fn to_request(field: UserField) -> endpoint::i::update::UserFieldRequest {
117            endpoint::i::update::UserFieldRequest {
118                name: Some(field.name),
119                value: Some(field.value),
120            }
121        }
122        let [f1, f2, f3, f4] = fields.into_user_fields();
123        let fields = [
124            f1.map(to_request).unwrap_or_default(),
125            f2.map(to_request).unwrap_or_default(),
126            f3.map(to_request).unwrap_or_default(),
127            f4.map(to_request).unwrap_or_default(),
128        ];
129        self.request.fields.replace(fields);
130        self
131    }
132
133    /// Deletes all the fields in this user's profile.
134    pub fn delete_fields(&mut self) -> &mut Self {
135        self.request.fields.replace(Default::default());
136        self
137    }
138
139    update_builder_bool_field! {
140        /// Sets whether this user is locked or not.
141        pub locked { is_locked };
142
143        /// Sets whether this user is visible in "Explore" section of the instance.
144        #[cfg(feature = "12-63-0")]
145        #[cfg_attr(docsrs, doc(cfg(feature = "12-63-0")))]
146        pub explorable { is_explorable };
147
148        /// Sets whether this user requires a follow request from bots.
149        pub require_follow_request_for_bot { careful_bot };
150
151        /// Sets whether to automatically accept follow requests from following users.
152        pub auto_accept_followed;
153
154        /// Sets whether to display this user as a bot.
155        pub bot { is_bot };
156
157        /// Sets whether to display this user as a cot.
158        pub cat { is_cat };
159
160        /// Sets whether to display featured notes in the timeline.
161        pub inject_featured_note;
162
163        /// Sets whether to mark uploaded media as NSFW by default.
164        pub always_mark_nsfw;
165
166        /// Sets whether to receive notifications about other users' notes that this user has
167        /// reacted to or replied to.
168        #[cfg(any(docsrs, not(feature = "12-55-0")))]
169        #[cfg_attr(docsrs, doc(cfg(not(feature = "12-55-0"))))]
170        pub auto_watch;
171
172        /// Sets whether to ask search engines not to index this user's contents.
173        #[cfg(feature = "12-60-0")]
174        #[cfg_attr(docsrs, doc(cfg(feature = "12-60-0")))]
175        pub no_crawle;
176    }
177
178    /// Sets the muted words for this user.
179    pub fn muted_words(&mut self, muted_words: impl Into<Query<String>>) -> &mut Self {
180        self.request.muted_words.replace(muted_words.into());
181        self
182    }
183}
184
185impl<C: Client> MeUpdateBuilder<C> {
186    /// Updates the user.
187    pub async fn update(&self) -> Result<User, Error<C::Error>> {
188        let response = self
189            .client
190            .request(&self.request)
191            .await
192            .map_err(Error::Client)?
193            .into_result()?;
194        Ok(response)
195    }
196}