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}