Skip to main content

figshare_rs/
endpoint.rs

1//! Endpoint selection for production or custom Figshare deployments.
2
3use url::Url;
4
5/// Base API endpoint used by the client.
6#[derive(Clone, Debug, PartialEq, Eq, Default)]
7pub enum Endpoint {
8    /// The public Figshare production service.
9    #[default]
10    Production,
11    /// A fully custom Figshare-compatible API base URL.
12    Custom(
13        /// Deployment root or base API URL, normalized to end in `/v2/` when
14        /// no path is supplied.
15        Url,
16    ),
17}
18
19impl Endpoint {
20    /// Returns the API base URL for this endpoint.
21    ///
22    /// # Errors
23    ///
24    /// Returns an error if the configured URL cannot be parsed into a valid
25    /// base URL.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use figshare_rs::Endpoint;
31    ///
32    /// let endpoint = Endpoint::Custom("http://localhost:1234".parse()?);
33    /// assert_eq!(endpoint.base_url()?.as_str(), "http://localhost:1234/v2/");
34    /// # Ok::<(), Box<dyn std::error::Error>>(())
35    /// ```
36    pub fn base_url(&self) -> Result<Url, url::ParseError> {
37        match self {
38            Self::Production => Url::parse("https://api.figshare.com/v2/"),
39            Self::Custom(url) => Ok(normalize_base_url(url.clone())),
40        }
41    }
42}
43
44fn normalize_base_url(mut url: Url) -> Url {
45    let path = url.path().trim_end_matches('/');
46    let normalized = if path.is_empty() {
47        "/v2/".to_owned()
48    } else {
49        format!("{path}/")
50    };
51    url.set_path(&normalized);
52    url
53}
54
55#[cfg(test)]
56mod tests {
57    use super::Endpoint;
58    use url::Url;
59
60    #[test]
61    fn uses_expected_production_url() {
62        assert_eq!(
63            Endpoint::Production.base_url().unwrap().as_str(),
64            "https://api.figshare.com/v2/"
65        );
66    }
67
68    #[test]
69    fn preserves_custom_v2_url() {
70        let url = Url::parse("http://localhost:1234/v2/").unwrap();
71        assert_eq!(Endpoint::Custom(url.clone()).base_url().unwrap(), url);
72    }
73
74    #[test]
75    fn normalizes_custom_url_without_trailing_slash() {
76        let normalized = Endpoint::Custom(Url::parse("http://localhost:1234/v2").unwrap())
77            .base_url()
78            .unwrap();
79        assert_eq!(normalized.as_str(), "http://localhost:1234/v2/");
80    }
81
82    #[test]
83    fn normalizes_empty_custom_path_to_v2() {
84        let normalized = Endpoint::Custom(Url::parse("http://localhost:1234").unwrap())
85            .base_url()
86            .unwrap();
87        assert_eq!(normalized.as_str(), "http://localhost:1234/v2/");
88    }
89}