pocketbase_rs/records/crud/
get_one.rs

1use serde::{Deserialize, de::DeserializeOwned};
2
3use crate::error::RequestError;
4use crate::{Collection, PocketBase};
5
6pub struct CollectionGetOneBuilder<'a, T: Send + Deserialize<'a>> {
7    client: &'a PocketBase,
8    collection_name: &'a str,
9    record_id: &'a str,
10    expand: Option<&'a str>,
11    _marker: std::marker::PhantomData<T>,
12}
13
14impl<'a> Collection<'a> {
15    /// Fetch a single record.
16    ///
17    /// # Example
18    /// ```rust,ignore
19    /// #[derive(Default, Deserialize, Clone)]
20    /// struct Article {
21    ///     id: String,
22    ///     title: String,
23    ///     content: String,
24    /// }
25    ///
26    /// let article = pb
27    ///     .collection("articles")
28    ///     .get_one::<Article>("record_id_123")
29    ///     .call()
30    ///     .await?;
31    /// ```
32    #[must_use]
33    pub const fn get_one<T: Default + DeserializeOwned + Clone + Send>(
34        self,
35        record_id: &'a str,
36    ) -> CollectionGetOneBuilder<'a, T> {
37        CollectionGetOneBuilder {
38            client: self.client,
39            collection_name: self.name,
40            record_id,
41            expand: None,
42            _marker: std::marker::PhantomData,
43        }
44    }
45}
46
47impl<'a, T: Default + DeserializeOwned + Clone + Send> CollectionGetOneBuilder<'a, T> {
48    /// Auto expand record relations (up to 6-levels deep).
49    ///
50    /// Expanded relations are appended under the `expand` property.
51    /// Only relations the user has view permissions for will be expanded.
52    ///
53    /// # Example
54    /// ```rust,ignore
55    /// .expand("author")
56    /// ```
57    pub const fn expand(mut self, expand: &'a str) -> Self {
58        self.expand = Some(expand);
59        self
60    }
61
62    /// Execute the request and return the record.
63    pub async fn call(self) -> Result<T, RequestError> {
64        let url = format!(
65            "{}/api/collections/{}/records/{}",
66            self.client.base_url, self.collection_name, self.record_id
67        );
68
69        let request = self.expand.map_or_else(
70            || self.client.request_get(&url, None),
71            |expand_value| {
72                let expand_params = vec![("expand", expand_value)];
73
74                self.client.request_get(&url, Some(expand_params))
75            },
76        );
77
78        let request = request.send().await;
79
80        let response = match request {
81            Ok(response) => response
82                .error_for_status()
83                .map_err(|err| match err.status() {
84                    Some(reqwest::StatusCode::FORBIDDEN) => RequestError::Forbidden,
85                    Some(reqwest::StatusCode::NOT_FOUND) => RequestError::NotFound,
86                    Some(reqwest::StatusCode::TOO_MANY_REQUESTS) => RequestError::TooManyRequests,
87                    _ => RequestError::Unhandled,
88                })?,
89            Err(error) => {
90                return Err(match error.status() {
91                    Some(reqwest::StatusCode::FORBIDDEN) => RequestError::Forbidden,
92                    Some(reqwest::StatusCode::NOT_FOUND) => RequestError::NotFound,
93                    Some(reqwest::StatusCode::TOO_MANY_REQUESTS) => RequestError::TooManyRequests,
94                    _ => RequestError::Unhandled,
95                });
96            }
97        };
98
99        // Parse JSON response
100        let record = response
101            .json::<T>()
102            .await
103            .map_err(|error| RequestError::ParseError(error.to_string()))?;
104
105        Ok(record)
106    }
107}