Skip to main content

canvas_lms_api/resources/
user.rs

1use crate::{
2    error::Result,
3    http::Requester,
4    pagination::PageStream,
5    resources::{
6        communication_channel::CommunicationChannel, course::Course, enrollment::Enrollment,
7    },
8};
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::sync::Arc;
12
13/// A Canvas user.
14#[derive(Debug, Clone, Deserialize, Serialize)]
15pub struct User {
16    pub id: u64,
17    pub name: Option<String>,
18    pub sortable_name: Option<String>,
19    pub short_name: Option<String>,
20    pub sis_user_id: Option<String>,
21    pub login_id: Option<String>,
22    pub email: Option<String>,
23    pub avatar_url: Option<String>,
24    pub locale: Option<String>,
25    pub last_login: Option<DateTime<Utc>>,
26    pub time_zone: Option<String>,
27    pub bio: Option<String>,
28
29    #[serde(skip)]
30    pub(crate) requester: Option<Arc<Requester>>,
31}
32
33impl User {
34    fn req(&self) -> &Arc<Requester> {
35        self.requester.as_ref().expect("requester not initialized")
36    }
37
38    /// Stream all courses for this user.
39    ///
40    /// # Canvas API
41    /// `GET /api/v1/users/:id/courses`
42    pub fn get_courses(&self) -> PageStream<Course> {
43        PageStream::new(
44            Arc::clone(self.req()),
45            &format!("users/{}/courses", self.id),
46            vec![],
47        )
48    }
49
50    /// Stream all enrollments for this user.
51    ///
52    /// # Canvas API
53    /// `GET /api/v1/users/:id/enrollments`
54    pub fn get_enrollments(&self) -> PageStream<Enrollment> {
55        PageStream::new(
56            Arc::clone(self.req()),
57            &format!("users/{}/enrollments", self.id),
58            vec![],
59        )
60    }
61
62    /// Stream all communication channels for this user.
63    ///
64    /// # Canvas API
65    /// `GET /api/v1/users/:id/communication_channels`
66    pub fn get_communication_channels(&self) -> PageStream<CommunicationChannel> {
67        let user_id = self.id;
68        PageStream::new_with_injector(
69            Arc::clone(self.req()),
70            &format!("users/{user_id}/communication_channels"),
71            vec![],
72            |mut c: CommunicationChannel, req| {
73                c.requester = Some(Arc::clone(&req));
74                c
75            },
76        )
77    }
78
79    /// Create a communication channel for this user.
80    ///
81    /// `address` is the email address, phone number, etc.
82    /// `channel_type` is `"email"`, `"sms"`, `"push"`, etc.
83    ///
84    /// # Canvas API
85    /// `POST /api/v1/users/:id/communication_channels`
86    pub async fn create_communication_channel(
87        &self,
88        address: &str,
89        channel_type: &str,
90    ) -> Result<CommunicationChannel> {
91        let params = vec![
92            (
93                "communication_channel[address]".to_string(),
94                address.to_string(),
95            ),
96            (
97                "communication_channel[type]".to_string(),
98                channel_type.to_string(),
99            ),
100        ];
101        let mut channel: CommunicationChannel = self
102            .req()
103            .post(
104                &format!("users/{}/communication_channels", self.id),
105                &params,
106            )
107            .await?;
108        channel.requester = self.requester.clone();
109        Ok(channel)
110    }
111}
112
113/// The currently authenticated user (extends User with additional fields).
114#[derive(Debug, Clone, Deserialize, Serialize)]
115pub struct CurrentUser {
116    pub id: u64,
117    pub name: Option<String>,
118    pub sortable_name: Option<String>,
119    pub short_name: Option<String>,
120    pub sis_user_id: Option<String>,
121    pub login_id: Option<String>,
122    pub email: Option<String>,
123    pub avatar_url: Option<String>,
124    pub locale: Option<String>,
125    pub last_login: Option<DateTime<Utc>>,
126    pub time_zone: Option<String>,
127    pub bio: Option<String>,
128    pub effective_locale: Option<String>,
129}
130
131/// A user display stub (id + name only) used in nested contexts.
132#[derive(Debug, Clone, Deserialize, Serialize)]
133pub struct UserDisplay {
134    pub id: u64,
135    pub display_name: Option<String>,
136    pub avatar_image_url: Option<String>,
137    pub html_url: Option<String>,
138}
139
140/// Identifies a user by numeric ID or as the currently authenticated user.
141pub enum UserId {
142    Id(u64),
143    Current,
144}
145
146impl UserId {
147    pub(crate) fn to_path_segment(&self) -> String {
148        match self {
149            UserId::Id(id) => id.to_string(),
150            UserId::Current => "self".to_string(),
151        }
152    }
153}