pocketbase_rs/records/crud/
update.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4use crate::error::{BadRequestError, BadRequestResponse};
5use crate::{Collection, PocketBase};
6
7/// Represents the various errors that can be obtained after a `update` request.
8#[derive(Error, Debug)]
9pub enum UpdateError {
10    /// Communication with the `PocketBase` API was successful,
11    /// but returned a [400 Bad Request]("https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400") HTTP error response.
12    ///
13    /// One or more fields were not validated `PocketBase`.
14    #[error("One or more fields were not validated : {0:?}")]
15    BadRequest(Vec<BadRequestError>),
16    /// Communication with the `PocketBase` API was successful,
17    /// but returned a [403 Forbidden]("https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403") HTTP error response.
18    ///
19    /// The authorized record is not allowed to perform this action.
20    #[error("The authorized record is not allowed to perform this action.")]
21    Forbidden,
22    /// Communication with the `PocketBase` API was successful,
23    /// but returned a [404 Not Found]("https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404") HTTP error response.
24    ///
25    /// The requested resource wasn't found. Missing collection context.
26    #[error("The requested resource wasn't found. Missing collection context.")]
27    NotFound,
28    /// Communication with the `PocketBase` API failed.
29    ///
30    /// This could be caused by an internet outage, an error in the link given to the `PocketBase` SDK
31    /// and similar errors.
32    #[error("The communication with the PocketBase API failed: {0}")]
33    Unreachable(String),
34    /// The response could not be parsed into the expected data structure.
35    #[error(
36        "Could not parse response into the expected data structure. It usually means that there is a missmatch between the provided Generic Type Parameter and your Collection definition: {0}"
37    )]
38    ParseError(String),
39    /// The response from the `PocketBase` instance API was unexpected.
40    /// If you think its an error, please [open an issue on GitHub]("https://github.com/fromhorizons/pocketbase-rs/issues").
41    #[error("An unhandled status code was returned by the PocketBase API: {0}")]
42    UnexpectedResponse(String),
43}
44
45pub struct CollectionUpdateBuilder<'a, T: Send + Serialize + Deserialize<'a>> {
46    client: &'a PocketBase,
47    collection_name: &'a str,
48    record_id: &'a str,
49    data: T,
50    _marker: std::marker::PhantomData<T>,
51}
52
53// TODO: Include the actual record data based on Generic type parameter.
54// 
55// pub struct UpdateResponse<T> {
56//     pub collection_name: String,
57//     pub collection_id: String,
58//     pub id: String,
59//     pub updated: String,
60//     pub created: String,
61//     #[serde(flatten)]
62//     pub record: T, // The actual record data
63// }
64
65/// Contains information about the successfully updated Record
66#[derive(Deserialize, Clone, Debug)]
67#[serde(rename_all = "camelCase")]
68pub struct UpdateResponse {
69    pub collection_name: String,
70    pub collection_id: String,
71    pub id: String,
72    pub updated: String,
73    pub created: String,
74}
75
76impl<'a> Collection<'a> {
77    /// Update a single record.
78    ///
79    /// # Example
80    /// ```rust,ignore
81    /// #[derive(Default, Serialize, Clone, Debug)]
82    /// struct Article {
83    ///     name: String,
84    ///     content: String,
85    /// }
86    ///
87    /// let updated_article = Article {
88    ///     name: String::from("Updated Article Title"),
89    ///     content: String::from("Updated article content"),
90    /// };
91    ///
92    /// let response = pb
93    ///     .collection("articles")
94    ///     .update::<Article>("record_id_123", updated_article)
95    ///     .await?;
96    /// ```
97    pub async fn update<T: Default + Serialize + Clone + Send>(
98        self,
99        record_id: &'a str,
100        record: T,
101    ) -> Result<UpdateResponse, UpdateError> {
102        let collection_name = self.name;
103
104        let endpoint = format!(
105            "{}/api/collections/{}/records/{}",
106            self.client.base_url, collection_name, record_id
107        );
108
109        let request = self
110            .client
111            .request_patch_json(&endpoint, &record)
112            .send()
113            .await;
114
115        match request {
116            Ok(response) => match response.status() {
117                reqwest::StatusCode::OK => {
118                    let data = response.json::<UpdateResponse>().await;
119
120                    match data {
121                        Ok(data) => Ok(data),
122                        Err(error) => Err(UpdateError::ParseError(error.to_string())),
123                    }
124                }
125
126                reqwest::StatusCode::BAD_REQUEST => {
127                    let data = response.json::<BadRequestResponse>().await;
128
129                    match data {
130                        Ok(bad_response) => {
131                            let mut errors: Vec<BadRequestError> = vec![];
132
133                            for (error_name, error_data) in bad_response.data {
134                                errors.push(BadRequestError {
135                                    name: error_name,
136                                    code: error_data.code,
137                                    message: error_data.message,
138                                });
139                            }
140
141                            Err(UpdateError::BadRequest(errors))
142                        }
143                        Err(error) => Err(UpdateError::ParseError(error.to_string())),
144                    }
145                }
146
147                reqwest::StatusCode::FORBIDDEN => Err(UpdateError::Forbidden),
148                reqwest::StatusCode::NOT_FOUND => Err(UpdateError::NotFound),
149
150                _ => Err(UpdateError::UnexpectedResponse(
151                    response.status().to_string(),
152                )),
153            },
154
155            Err(error) => Err(UpdateError::Unreachable(error.to_string())),
156        }
157    }
158}