171 lines
4.8 KiB
Rust
171 lines
4.8 KiB
Rust
use axum::{
|
|
http::{HeaderValue, Response, StatusCode, header},
|
|
{
|
|
body::Body,
|
|
extract::{Path, State},
|
|
},
|
|
};
|
|
|
|
use crate::prelude::*;
|
|
use crate::{
|
|
graph::{Format, Graph, SerialErrorCause},
|
|
router::{GlobalState, handlers},
|
|
router::handlers::mime::Mime,
|
|
};
|
|
|
|
pub async fn file(
|
|
Path(path): Path<String>,
|
|
State(state): State<GlobalState>,
|
|
) -> Response<Body> {
|
|
let instant = now();
|
|
let target = format!("static/public/{path}");
|
|
let content = match std::fs::read(&target) {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
let mut error_message = String::from(
|
|
"The requested file does not exist, the server does not have \
|
|
permission to access it or a filesystem error ocurred.",
|
|
);
|
|
if log::env_level() >= DEBUG {
|
|
error_message = format!(
|
|
"<p>{error_message}</p>\
|
|
<p>Targeted path: <code>{target}</code></p>\
|
|
<p>Error message:</p> <pre>{e}</pre>"
|
|
);
|
|
}
|
|
log!(ERROR, "{error_message}");
|
|
return super::error::by_code(
|
|
Some(404),
|
|
Some(&error_message),
|
|
&state.graph,
|
|
);
|
|
},
|
|
};
|
|
|
|
let mut response = Response::new(Body::from(content));
|
|
*response.status_mut() = StatusCode::OK;
|
|
let header = header::CONTENT_TYPE;
|
|
|
|
let content_type = Mime::guess(&path);
|
|
|
|
if let Ok(header_value) =
|
|
HeaderValue::from_str(&String::from(content_type.clone()))
|
|
{
|
|
response.headers_mut().append(header, header_value);
|
|
} else {
|
|
log!(
|
|
WARN,
|
|
"Failed to create content type header value from {content_type:?}"
|
|
);
|
|
}
|
|
|
|
tlog!(&instant, "Assembled response for {content_type:?} {path}");
|
|
response
|
|
}
|
|
|
|
pub async fn serial(
|
|
Path(format): Path<String>,
|
|
State(state): State<GlobalState>,
|
|
) -> Response<Body> {
|
|
let config = &state.graph.meta.config;
|
|
|
|
let make_error = |code: u16, message: &str| -> Response<Body> {
|
|
handlers::error::by_code(
|
|
Some(code),
|
|
Some(
|
|
format!(
|
|
"<p>{message}</p>\n\
|
|
<p>Check the <a href=/data>data</a> \n\
|
|
page for the available formats.</p>"
|
|
)
|
|
.as_str(),
|
|
),
|
|
&state.graph,
|
|
)
|
|
};
|
|
|
|
let forbidden_response =
|
|
make_error(403, "This graph format is not available.");
|
|
let unsupported_response =
|
|
make_error(400, "This graph format is not supported.");
|
|
let parse_failure = make_error(505, "The graph has failed to parse.");
|
|
|
|
let body =
|
|
match Graph::to_serial(&state.graph, &Format::from(format.as_str())) {
|
|
Ok(serial) => serial,
|
|
Err(error) => match error.cause {
|
|
SerialErrorCause::MalformedInput => return parse_failure,
|
|
SerialErrorCause::UnsupportedFormat => {
|
|
return unsupported_response;
|
|
},
|
|
},
|
|
};
|
|
|
|
match Format::from(format.as_str()) {
|
|
Format::TOML => {
|
|
if config.raw && config.raw_toml {
|
|
handlers::raw::make_response(
|
|
&body,
|
|
200,
|
|
&[(header::CONTENT_TYPE, "text/plain")],
|
|
)
|
|
} else {
|
|
forbidden_response
|
|
}
|
|
},
|
|
Format::JSON => {
|
|
if config.raw && config.raw_json {
|
|
handlers::raw::make_response(
|
|
&body,
|
|
200,
|
|
&[(header::CONTENT_TYPE, "application/json")],
|
|
)
|
|
} else {
|
|
forbidden_response
|
|
}
|
|
},
|
|
Format::Unsupported => unsupported_response,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
async fn wrap_serial(format: &str) -> Response<Body> {
|
|
let state = GlobalState {
|
|
graph: Graph::load(),
|
|
};
|
|
serial(Path(format.to_string()), State(state)).await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn serial_toml() {
|
|
let response = wrap_serial("toml").await;
|
|
assert!(response.status() == 200);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn serial_json() {
|
|
let response = wrap_serial("json").await;
|
|
assert!(response.status() == 200);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn serial_toml_content_type() {
|
|
let response = wrap_serial("TOML").await;
|
|
assert!(
|
|
response.headers().get(header::CONTENT_TYPE).unwrap()
|
|
== "text/plain"
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn serial_json_content_type() {
|
|
let response = wrap_serial("json").await;
|
|
assert!(
|
|
response.headers().get(header::CONTENT_TYPE).unwrap()
|
|
== "application/json"
|
|
);
|
|
}
|
|
}
|