plex_api/myplex/sharing/
friend.rs

1use crate::{
2    isahc_compat::StatusCodeExt, myplex::account::RestrictionProfile, url::MYPLEX_INVITES_FRIENDS,
3    Error, HttpClient, Result,
4};
5use http::StatusCode;
6use isahc::AsyncReadResponseExt;
7use serde::{Deserialize, Serialize};
8use serde_plain::derive_display_from_serialize;
9use time::OffsetDateTime;
10
11#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
12#[serde(rename_all = "snake_case")]
13pub enum InviteStatus {
14    PendingSent,
15    Accepted,
16    PendingReceived,
17    Pending,
18    #[cfg(not(feature = "tests_deny_unknown_fields"))]
19    #[serde(other)]
20    Unknown,
21}
22
23derive_display_from_serialize!(InviteStatus);
24
25#[derive(Serialize, Deserialize, Debug, Clone)]
26#[serde(rename_all = "camelCase")]
27#[cfg_attr(feature = "tests_deny_unknown_fields", serde(deny_unknown_fields))]
28pub struct Friend {
29    /// The global Plex.tv ID of the friend
30    pub id: u64,
31    /// Plex.tv user's uuid ??
32    pub uuid: String,
33    /// How the user should be displayed in the interface.
34    /// Seems to be generated on the backed from either username or friendly_name.
35    pub title: String,
36    /// Non-managed user's username
37    pub username: Option<String>,
38    /// Non-managed user's email
39    pub email: Option<String>,
40    /// Is it a managed user?
41    pub restricted: bool,
42    /// Restrictions on the managed user (if any)
43    pub restriction_profile: Option<RestrictionProfile>,
44    /// Managed user's username
45    pub friendly_name: Option<String>,
46    #[serde(default, with = "time::serde::rfc3339::option")]
47    pub friendship_created_at: Option<OffsetDateTime>,
48    /// User's avatar picture URL
49    #[serde(with = "http_serde::uri")]
50    pub thumb: http::Uri,
51    /// Does the user has access to Plex Home?
52    pub home: bool,
53    /// Status of the friendship
54    pub status: Option<InviteStatus>,
55    /// Server sharing preferences
56    pub sharing_settings: Option<super::server::Settings>,
57    /// List of the shared servers
58    #[serde(default)]
59    pub shared_servers: Vec<super::server::SharedServer>,
60    /// No idea what this is
61    #[serde(default)]
62    pub shared_sources: Vec<String>,
63    #[serde(skip)]
64    pub(crate) client: Option<HttpClient>,
65}
66
67impl Friend {
68    /// Accept friendship.
69    #[tracing::instrument(level = "debug", skip(self))]
70    pub async fn accept(self) -> Result<Friend> {
71        if !matches!(
72            self.status,
73            Some(InviteStatus::PendingReceived) | Some(InviteStatus::Pending)
74        ) {
75            return Err(Error::InviteAcceptingNotPendingReceived);
76        }
77
78        let mut friend: Friend = self
79            .client()
80            .post(format!("{}/{}/accept", MYPLEX_INVITES_FRIENDS, self.id))
81            .json()
82            .await?;
83        friend.client.clone_from(&self.client);
84        Ok(friend)
85    }
86
87    /// Shorthand for self.client.as_ref().unwrap(). Unwrap must be safe at this stage
88    /// since the client must be set for a valid invite.
89    fn client(&self) -> &HttpClient {
90        self.client.as_ref().unwrap()
91    }
92
93    /// Delete the friend or friendship request.
94    #[tracing::instrument(level = "debug", skip(self))]
95    pub async fn delete(self) -> Result<()> {
96        let mut response = self
97            .client()
98            .delete(format!("{}/{}", MYPLEX_INVITES_FRIENDS, self.id))
99            .send()
100            .await?;
101
102        match response.status().as_http_status() {
103            StatusCode::OK | StatusCode::NO_CONTENT => {
104                response.consume().await?;
105                Ok(())
106            }
107            _ => Err(Error::from_response(response).await),
108        }
109    }
110}