mirror of
https://github.com/Xevion/time-banner.git
synced 2025-12-06 01:16:36 -06:00
feat: re-implement route rendering, use duration parsing, absolute timestamps
This commit is contained in:
132
src/routes.rs
132
src/routes.rs
@@ -1,10 +1,13 @@
|
||||
use crate::duration::parse_duration;
|
||||
use crate::error::{get_error_response, TimeBannerError};
|
||||
use axum::body::Bytes;
|
||||
use axum::extract::Path;
|
||||
use axum::response::{IntoResponse, Redirect};
|
||||
use chrono::{DateTime, Utc};
|
||||
use axum::http::{header, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use chrono::{DateTime, FixedOffset, Utc};
|
||||
|
||||
use crate::raster::Rasterizer;
|
||||
use crate::template::{render_template, OutputForm, RenderContext};
|
||||
|
||||
pub fn split_on_extension(path: &str) -> Option<(&str, &str)> {
|
||||
let split = path.rsplit_once('.')?;
|
||||
@@ -33,7 +36,7 @@ fn handle_rasterize(data: String, extension: &str) -> Result<(&str, Bytes), Time
|
||||
));
|
||||
}
|
||||
|
||||
Ok(("image/x-png", Bytes::from(raw_image.unwrap())))
|
||||
Ok(("image/png", Bytes::from(raw_image.unwrap())))
|
||||
}
|
||||
_ => Err(TimeBannerError::RasterizeError(format!(
|
||||
"Unsupported extension: {}",
|
||||
@@ -42,35 +45,118 @@ fn handle_rasterize(data: String, extension: &str) -> Result<(&str, Bytes), Time
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_epoch_into_datetime(epoch: i64) -> Option<DateTime<Utc>> {
|
||||
DateTime::from_timestamp(epoch, 0)
|
||||
}
|
||||
|
||||
fn parse_time_value(raw_time: &str) -> Result<DateTime<Utc>, TimeBannerError> {
|
||||
// Handle relative time values (starting with + or -, or duration strings like "1y2d")
|
||||
if raw_time.starts_with('+') || raw_time.starts_with('-') {
|
||||
let now = Utc::now();
|
||||
|
||||
// Try parsing as simple offset seconds first
|
||||
if let Ok(offset_seconds) = raw_time.parse::<i64>() {
|
||||
return Ok(now + chrono::Duration::seconds(offset_seconds));
|
||||
}
|
||||
|
||||
// Try parsing as duration string (e.g., "+1y2d", "-3h30m")
|
||||
if let Ok(duration) = parse_duration(raw_time) {
|
||||
return Ok(now + duration);
|
||||
}
|
||||
|
||||
return Err(TimeBannerError::ParseError(format!(
|
||||
"Could not parse relative time: {}",
|
||||
raw_time
|
||||
)));
|
||||
}
|
||||
|
||||
// Try to parse as epoch timestamp
|
||||
if let Ok(epoch) = raw_time.parse::<i64>() {
|
||||
return parse_epoch_into_datetime(epoch)
|
||||
.ok_or_else(|| TimeBannerError::ParseError("Invalid timestamp".to_string()));
|
||||
}
|
||||
|
||||
Err(TimeBannerError::ParseError(format!(
|
||||
"Could not parse time value: {}",
|
||||
raw_time
|
||||
)))
|
||||
}
|
||||
|
||||
fn render_time_response(
|
||||
time: DateTime<Utc>,
|
||||
output_form: OutputForm,
|
||||
extension: &str,
|
||||
) -> impl IntoResponse {
|
||||
// Build context for rendering
|
||||
let context = RenderContext {
|
||||
output_form,
|
||||
value: time,
|
||||
tz_offset: FixedOffset::east_opt(0).unwrap(), // UTC offset
|
||||
tz_name: "UTC",
|
||||
view: "basic",
|
||||
};
|
||||
|
||||
// Render template
|
||||
let rendered_template = match render_template(context) {
|
||||
Ok(template) => template,
|
||||
Err(e) => {
|
||||
return get_error_response(TimeBannerError::RenderError(format!(
|
||||
"Template rendering failed: {}",
|
||||
e
|
||||
)))
|
||||
.into_response()
|
||||
}
|
||||
};
|
||||
|
||||
// Handle rasterization
|
||||
match handle_rasterize(rendered_template, extension) {
|
||||
Ok((mime_type, bytes)) => {
|
||||
(StatusCode::OK, [(header::CONTENT_TYPE, mime_type)], bytes).into_response()
|
||||
}
|
||||
Err(e) => get_error_response(e).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn index_handler() -> impl IntoResponse {
|
||||
let epoch_now = Utc::now().timestamp();
|
||||
|
||||
Redirect::temporary(&format!("/relative/{epoch_now}")).into_response()
|
||||
axum::response::Redirect::temporary(&format!("/relative/{epoch_now}")).into_response()
|
||||
}
|
||||
|
||||
pub async fn relative_handler(Path(path): Path<String>) -> impl IntoResponse {
|
||||
let (_raw_time, _extension) = parse_path(path.as_str());
|
||||
let (raw_time, extension) = parse_path(&path);
|
||||
|
||||
get_error_response(TimeBannerError::NotFound).into_response()
|
||||
let time = match parse_time_value(raw_time) {
|
||||
Ok(t) => t,
|
||||
Err(e) => return get_error_response(e).into_response(),
|
||||
};
|
||||
|
||||
render_time_response(time, OutputForm::Relative, extension).into_response()
|
||||
}
|
||||
|
||||
pub async fn absolute_handler(Path(path): Path<String>) -> impl IntoResponse {
|
||||
let (raw_time, extension) = parse_path(&path);
|
||||
|
||||
let time = match parse_time_value(raw_time) {
|
||||
Ok(t) => t,
|
||||
Err(e) => return get_error_response(e).into_response(),
|
||||
};
|
||||
|
||||
render_time_response(time, OutputForm::Absolute, extension).into_response()
|
||||
}
|
||||
|
||||
// Handler for implicit absolute time (no /absolute/ prefix)
|
||||
pub async fn implicit_handler(Path(path): Path<String>) -> impl IntoResponse {
|
||||
let (raw_time, extension) = parse_path(&path);
|
||||
|
||||
let time = match parse_time_value(raw_time) {
|
||||
Ok(t) => t,
|
||||
Err(e) => return get_error_response(e).into_response(),
|
||||
};
|
||||
|
||||
render_time_response(time, OutputForm::Absolute, extension).into_response()
|
||||
}
|
||||
|
||||
pub async fn fallback_handler() -> impl IntoResponse {
|
||||
get_error_response(TimeBannerError::NotFound).into_response()
|
||||
}
|
||||
|
||||
pub async fn absolute_handler(Path(path): Path<String>) -> impl IntoResponse {
|
||||
let (_raw_time, _extension) = parse_path(path.as_str());
|
||||
|
||||
get_error_response(TimeBannerError::NotFound).into_response()
|
||||
}
|
||||
|
||||
// basic handler that responds with a static string
|
||||
pub async fn implicit_handler(Path(path): Path<String>) -> impl IntoResponse {
|
||||
let (_raw_time, _extension) = parse_path(path.as_str());
|
||||
|
||||
get_error_response(TimeBannerError::NotFound).into_response()
|
||||
}
|
||||
|
||||
fn parse_epoch_into_datetime(epoch: i64) -> Option<DateTime<Utc>> {
|
||||
DateTime::from_timestamp(epoch, 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user