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 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}