1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
use bytes::Bytes;
use http_body_util::combinators::BoxBody;
use hyper::{server::conn::http1, service::service_fn, Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use log::{error, info};
use responses::{html_response, html_response_status, html_response_status_content_type};
use tokio::net::TcpListener;
use std::{convert::Infallible, net::SocketAddr, path::PathBuf, sync::Arc};
use crate::{config::BackConfig, error, git_bug::issue::Status};
mod generate;
mod responses;
async fn match_uri(
config: Arc<BackConfig>,
req: Request<hyper::body::Incoming>,
) -> Result<Response<BoxBody<Bytes, Infallible>>, hyper::Error> {
if req.method() != Method::GET {
return Ok(html_response_status(
"Only get requests are supported",
StatusCode::NOT_ACCEPTABLE,
));
}
let output = || -> Result<Response<BoxBody<Bytes, Infallible>>, error::Error> {
match req.uri().path().trim_end_matches("/") {
"" => Ok(html_response(generate::repos(&config)?)),
"/style.css" => Ok(responses::html_response_status_content_type(
include_str!("../../assets/style.css"),
StatusCode::OK,
"text/css",
)),
path if path.ends_with("/issues/open") => {
let repo_path = PathBuf::from(
path.strip_suffix("/issues/open")
.expect("This suffix exists")
.strip_prefix("/")
.expect("This also exists"),
);
let issues = generate::issues(&config, Status::Open, Status::Closed, &repo_path)?;
Ok(html_response(issues))
}
path if path.ends_with("/issues/closed") => {
let repo_path = PathBuf::from(
path.strip_suffix("/issues/closed")
.expect("This suffix exists")
.strip_prefix("/")
.expect("This also exists"),
);
let issues = generate::issues(&config, Status::Closed, Status::Open, &repo_path)?;
Ok(html_response(issues))
}
path if path.ends_with("/issues/feed") => {
let repo_path = PathBuf::from(
path.strip_suffix("/issues/feed")
.expect("This suffix exists")
.strip_prefix("/")
.expect("This also exists"),
);
let feed = generate::feed(&config, &repo_path)?;
Ok(html_response_status_content_type(
feed,
StatusCode::OK,
"text/xml",
))
}
path if path.contains("/issue/") => {
let (repo_path, prefix) = {
let split: Vec<&str> = path.split("/issue/").collect();
let prefix =
gix::hash::Prefix::from_hex(split[1]).map_err(error::Error::from)?;
let repo_path =
PathBuf::from(split[0].strip_prefix("/").expect("This prefix exists"));
(repo_path, prefix)
};
Ok(html_response(generate::issue(&config, &repo_path, prefix)?))
}
other => Ok(responses::html_response_status_content_type(
format!("'{}' not found", other),
StatusCode::NOT_FOUND,
"text/plain",
)),
}
};
match output() {
Ok(response) => Ok(response),
Err(err) => Ok(err.into_response()),
}
}
pub async fn main(config: Arc<BackConfig>) -> Result<(), error::Error> {
let addr: SocketAddr = ([127, 0, 0, 1], 8000).into();
let listener = TcpListener::bind(addr)
.await
.map_err(|err| error::Error::TcpBind { addr, err })?;
info!("Listening on http://{}", addr);
loop {
let (stream, _) = listener
.accept()
.await
.map_err(|err| error::Error::TcpAccept { err })?;
let io = TokioIo::new(stream);
let local_config = Arc::clone(&config);
let service = service_fn(move |req| match_uri(Arc::clone(&local_config), req));
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
error!("Error serving connection: {:?}", err);
}
});
}
}
|