openstreetmap_api/
lib.rs

1#[macro_use]
2extern crate log;
3#[macro_use]
4extern crate serde_derive;
5
6mod api;
7pub mod errors;
8pub mod types;
9
10use errors::OpenstreetmapError;
11use quick_xml::de::from_reader;
12use quick_xml::se::to_string;
13use reqwest::header::CONTENT_TYPE;
14use reqwest::StatusCode;
15use serde::de::DeserializeOwned;
16use serde::ser::Serialize;
17use url::Url;
18
19pub const DEFAULT_VERSION: &str = "0.6";
20
21#[derive(Debug, Clone)]
22pub struct Openstreetmap {
23    pub host: String,
24    api_version: String,
25    credentials: types::Credentials,
26    client: reqwest::Client,
27}
28
29#[derive(Debug, Clone)]
30struct RequestOptions {
31    pub use_version: bool,
32    pub use_auth: bool,
33}
34
35impl RequestOptions {
36    pub fn new() -> Self {
37        Self {
38            use_version: false,
39            use_auth: false,
40        }
41    }
42    pub fn with_version(mut self) -> Self {
43        self.use_version = true;
44        self
45    }
46    pub fn with_auth(mut self) -> Self {
47        self.use_auth = true;
48        self
49    }
50}
51
52impl Openstreetmap {
53    pub fn new<T>(host: T, credentials: types::Credentials) -> Self
54    where
55        T: Into<String>,
56    {
57        Openstreetmap {
58            host: host.into(),
59            api_version: DEFAULT_VERSION.into(),
60            credentials,
61            client: reqwest::Client::new(),
62        }
63    }
64
65    /// creates a new instance of a Openstreetmap client using a specified reqwest client
66    pub fn from_client<H>(host: H, credentials: types::Credentials, client: reqwest::Client) -> Self
67    where
68        H: Into<String>,
69    {
70        Openstreetmap {
71            host: host.into(),
72            api_version: DEFAULT_VERSION.into(),
73            credentials,
74            client,
75        }
76    }
77
78    #[inline]
79    pub async fn versions(&self) -> Result<Vec<String>, OpenstreetmapError> {
80        api::versions::Versions::new(self).get().await
81    }
82
83    #[inline]
84    pub async fn capabilities(&self) -> Result<types::CapabilitiesAndPolicy, OpenstreetmapError> {
85        api::capabilities::Capabilities::new(self).get().await
86    }
87
88    #[inline]
89    pub async fn map(&self, bbox: &types::BoundingBox) -> Result<types::Map, OpenstreetmapError> {
90        api::map::Map::new(self).get(bbox).await
91    }
92
93    #[inline]
94    pub async fn permissions(&self) -> Result<Vec<types::Permission>, OpenstreetmapError> {
95        api::permissions::Permissions::new(self).get().await
96    }
97
98    #[inline]
99    pub fn changeset(&self) -> api::changeset::Changeset {
100        api::changeset::Changeset::new(self)
101    }
102
103    #[inline]
104    pub fn nodes(&self) -> api::elements::Elements<types::Node> {
105        api::elements::Elements::new(self)
106    }
107
108    #[inline]
109    pub fn ways(&self) -> api::elements::Elements<types::Way> {
110        api::elements::Elements::new(self)
111    }
112
113    #[inline]
114    pub fn relations(&self) -> api::elements::Elements<types::Relation> {
115        api::elements::Elements::new(self)
116    }
117
118    #[inline]
119    pub fn user(&self) -> api::user::User {
120        api::user::User::new(self)
121    }
122
123    #[inline]
124    pub fn notes(&self) -> api::notes::Notes {
125        api::notes::Notes::new(self)
126    }
127
128    #[inline]
129    pub async fn changesets(
130        &self,
131        query: types::ChangesetQueryParams,
132    ) -> Result<Vec<types::Changeset>, OpenstreetmapError> {
133        api::changesets::Changesets::new(self).get(query).await
134    }
135
136    async fn request<S, D>(
137        &self,
138        method: reqwest::Method,
139        endpoint: &str,
140        body: types::RequestBody<S>,
141        options: RequestOptions,
142    ) -> Result<D, OpenstreetmapError>
143    where
144        S: Serialize,
145        D: DeserializeOwned,
146    {
147        let mut url = Url::parse(&self.host)?.join("api/")?;
148
149        if options.use_version {
150            let version_path = format!("{}/", self.api_version);
151
152            url = url.join(&version_path)?;
153        }
154
155        url = url.join(endpoint)?;
156        debug!("url -> {:?}", url);
157
158        let mut builder = self.client.request(method, url);
159
160        if options.use_auth {
161            builder = match self.credentials {
162                types::Credentials::Basic(ref user, ref pass) => {
163                    builder.basic_auth(user, Some(pass))
164                }
165                types::Credentials::None => return Err(OpenstreetmapError::CredentialsNeeded),
166            };
167        }
168
169        builder = match body {
170            types::RequestBody::Xml(payload) => builder
171                .body(to_string(&payload)?.into_bytes())
172                .header(CONTENT_TYPE, "text/xml"),
173            types::RequestBody::Form(payload) => builder.form(&payload),
174            types::RequestBody::RawForm(payload) => builder
175                .body(payload)
176                .header(CONTENT_TYPE, "application/x-www-form-urlencoded"),
177            types::RequestBody::None => builder,
178        };
179
180        let res = builder.send().await?;
181
182        match res.status() {
183            StatusCode::UNAUTHORIZED => Err(OpenstreetmapError::Unauthorized),
184            StatusCode::METHOD_NOT_ALLOWED => Err(OpenstreetmapError::MethodNotAllowed),
185            StatusCode::NOT_FOUND => Err(OpenstreetmapError::NotFound),
186            client_err if client_err.is_client_error() => Err(OpenstreetmapError::Client {
187                code: res.status(),
188                error: res.text().await?,
189            }),
190            _ => Ok(from_reader(res.text().await?.as_bytes())?),
191        }
192    }
193}