stac_api/url_builder.rs
1use std::str::FromStr;
2use url::{ParseError, Url};
3
4/// Builds urls on a root url.
5///
6/// # Examples
7///
8/// ```
9/// # use stac_api::UrlBuilder;
10/// let url_builder = UrlBuilder::new("http://stac-api.test/api/v1").unwrap();
11/// assert_eq!(
12/// url_builder.items("my-great-collection").unwrap().as_str(),
13/// "http://stac-api.test/api/v1/collections/my-great-collection/items"
14/// );
15/// ```
16#[derive(Clone, Debug)]
17pub struct UrlBuilder {
18 root: Url,
19 collections: Url,
20 collections_with_slash: Url,
21 conformance: Url,
22 service_desc: Url,
23 search: Url,
24}
25
26impl UrlBuilder {
27 /// Creates a new url builder.
28 ///
29 /// # Examples
30 ///
31 /// ```
32 /// # use stac_api::UrlBuilder;
33 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
34 /// ```
35 pub fn new(url: &str) -> Result<UrlBuilder, ParseError> {
36 let root: Url = if url.ends_with('/') {
37 url.parse()?
38 } else {
39 format!("{url}/").parse()?
40 };
41 Ok(UrlBuilder {
42 collections: root.join("collections")?,
43 collections_with_slash: root.join("collections/")?,
44 conformance: root.join("conformance")?,
45 service_desc: root.join("api")?,
46 search: root.join("search")?,
47 root,
48 })
49 }
50
51 /// Returns the root url.
52 ///
53 /// The root url always has a trailing slash, even if the builder was
54 /// created without one.
55 ///
56 /// # Examples
57 ///
58 /// ```
59 /// # use stac_api::UrlBuilder;
60 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
61 /// assert_eq!(url_builder.root().as_str(), "http://stac-api.test/");
62 /// ```
63 pub fn root(&self) -> &Url {
64 &self.root
65 }
66
67 /// Returns the collections url.
68 ///
69 /// This url is created when the builder is created, so it can't error.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// # use stac_api::UrlBuilder;
75 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
76 /// assert_eq!(url_builder.collections().as_str(), "http://stac-api.test/collections");
77 /// ```
78 pub fn collections(&self) -> &Url {
79 &self.collections
80 }
81
82 /// Returns a collection url.
83 ///
84 /// # Examples
85 ///
86 /// ```
87 /// # use stac_api::UrlBuilder;
88 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
89 /// assert_eq!(
90 /// url_builder.collection("a-collection").unwrap().as_str(),
91 /// "http://stac-api.test/collections/a-collection"
92 /// );
93 /// ```
94 pub fn collection(&self, id: &str) -> Result<Url, ParseError> {
95 self.collections_with_slash.join(id)
96 }
97
98 /// Returns an items url.
99 ///
100 /// # Examples
101 ///
102 /// ```
103 /// # use stac_api::UrlBuilder;
104 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
105 /// assert_eq!(
106 /// url_builder.items("a-collection").unwrap().as_str(),
107 /// "http://stac-api.test/collections/a-collection/items"
108 /// );
109 /// ```
110 pub fn items(&self, id: &str) -> Result<Url, ParseError> {
111 self.collections_with_slash.join(&format!("{id}/items"))
112 }
113
114 /// Returns a item url.
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// # use stac_api::UrlBuilder;
120 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
121 /// assert_eq!(
122 /// url_builder.item("a-collection", "an-item").unwrap().as_str(),
123 /// "http://stac-api.test/collections/a-collection/items/an-item"
124 /// );
125 /// ```
126 pub fn item(&self, collection_id: &str, id: &str) -> Result<Url, ParseError> {
127 self.collections_with_slash
128 .join(&format!("{collection_id}/items/{id}"))
129 }
130
131 /// Returns the conformance url.
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// # use stac_api::UrlBuilder;
137 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
138 /// assert_eq!(
139 /// url_builder.conformance().as_str(),
140 /// "http://stac-api.test/conformance"
141 /// );
142 /// ```
143 pub fn conformance(&self) -> &Url {
144 &self.conformance
145 }
146
147 /// Returns the service-desc url.
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// # use stac_api::UrlBuilder;
153 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
154 /// assert_eq!(
155 /// url_builder.service_desc().as_str(),
156 /// "http://stac-api.test/api"
157 /// );
158 /// ```
159 pub fn service_desc(&self) -> &Url {
160 &self.service_desc
161 }
162
163 /// Returns the search url.
164 ///
165 /// # Examples
166 ///
167 /// ```
168 /// # use stac_api::UrlBuilder;
169 /// let url_builder = UrlBuilder::new("http://stac-api.test").unwrap();
170 /// assert_eq!(
171 /// url_builder.search().as_str(),
172 /// "http://stac-api.test/search"
173 /// );
174 /// ```
175 pub fn search(&self) -> &Url {
176 &self.search
177 }
178}
179
180impl FromStr for UrlBuilder {
181 type Err = ParseError;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 UrlBuilder::new(s)
185 }
186}