1#[macro_use]
4extern crate log;
5extern crate reqwest;
6extern crate serde;
7#[macro_use]
8extern crate serde_derive;
9extern crate serde_json;
10extern crate url;
11
12use std::io::Read;
13
14use reqwest::header::CONTENT_TYPE;
15use reqwest::{blocking::Client, Method, StatusCode};
16use serde::de::DeserializeOwned;
17use serde::Serialize;
18
19mod builder;
20mod errors;
21pub mod issues;
22mod rep;
23mod search;
24mod transitions;
25
26pub mod worklogs;
27
28pub use crate::builder::*;
29pub use crate::errors::*;
30pub use crate::issues::*;
31pub use crate::rep::*;
32pub use crate::search::Search;
33pub use crate::transitions::*;
34pub use crate::worklogs::*;
35pub mod boards;
36pub mod resolution;
37pub use crate::boards::*;
38pub mod sprints;
39pub use crate::sprints::*;
40
41#[derive(Deserialize, Debug)]
42pub struct EmptyResponse;
43
44pub type Result<T> = std::result::Result<T, Error>;
45
46#[derive(Clone, Debug)]
48pub enum Credentials {
49 Basic(String, String), }
52
53#[derive(Clone, Debug)]
56pub struct Jira {
57 host: String,
58 credentials: Credentials,
59 client: Client,
60}
61
62impl Jira {
63 pub fn new<H>(host: H, credentials: Credentials) -> Result<Jira>
65 where
66 H: Into<String>,
67 {
68 Ok(Jira {
69 host: host.into(),
70 client: Client::new(),
71 credentials,
72 })
73 }
74
75 pub fn from_client<H>(host: H, credentials: Credentials, client: Client) -> Result<Jira>
77 where
78 H: Into<String>,
79 {
80 Ok(Jira {
81 host: host.into(),
82 credentials,
83 client,
84 })
85 }
86
87 pub fn transitions<K>(&self, key: K) -> Transitions
89 where
90 K: Into<String>,
91 {
92 Transitions::new(self, key)
93 }
94
95 pub fn search(&self) -> Search {
97 Search::new(self)
98 }
99
100 pub fn issues(&self) -> Issues {
102 Issues::new(self)
103 }
104
105 pub fn boards(&self) -> Boards {
107 Boards::new(self)
108 }
109
110 pub fn sprints(&self) -> Sprints {
112 Sprints::new(self)
113 }
114
115 pub fn worklogs(&self) -> Worklogs {
117 Worklogs::new(self)
118 }
119
120 fn post<D, S>(&self, api_name: &str, endpoint: &str, body: S) -> Result<D>
121 where
122 D: DeserializeOwned,
123 S: Serialize,
124 {
125 let data = serde_json::to_string::<S>(&body)?;
126 debug!("Json request: {}", data);
127 self.request::<D>(Method::POST, api_name, endpoint, Some(data.into_bytes()))
128 }
129
130 fn get<D>(&self, api_name: &str, endpoint: &str) -> Result<D>
131 where
132 D: DeserializeOwned,
133 {
134 self.request::<D>(Method::GET, api_name, endpoint, None)
135 }
136
137 fn request<D>(
138 &self,
139 method: Method,
140 api_name: &str,
141 endpoint: &str,
142 body: Option<Vec<u8>>,
143 ) -> Result<D>
144 where
145 D: DeserializeOwned,
146 {
147 let url = format!("{}/rest/{}/latest{}", self.host, api_name, endpoint);
148 debug!("url -> {:?}", url);
149
150 let req = self.client.request(method, &url);
151 let builder = match self.credentials {
152 Credentials::Basic(ref user, ref pass) => req
153 .basic_auth(user.to_owned(), Some(pass.to_owned()))
154 .header(CONTENT_TYPE, "application/json"),
155 };
156
157 let mut res = match body {
158 Some(bod) => builder.body(bod).send()?,
159 _ => builder.send()?,
160 };
161
162 let mut body = String::new();
163 res.read_to_string(&mut body)?;
164 debug!("status {:?} body '{:?}'", res.status(), body);
165 match res.status() {
166 StatusCode::UNAUTHORIZED => Err(Error::Unauthorized),
167 StatusCode::METHOD_NOT_ALLOWED => Err(Error::MethodNotAllowed),
168 StatusCode::NOT_FOUND => Err(Error::NotFound),
169 client_err if client_err.is_client_error() => Err(Error::Fault {
170 code: res.status(),
171 errors: serde_json::from_str::<Errors>(&body)?,
172 }),
173 _ => {
174 let data = if body == "" { "null" } else { &body };
175 Ok(serde_json::from_str::<D>(data)?)
176 }
177 }
178 }
179}