use axum::{response::Html, routing::get, Router};
use clap::Parser;
use simple_gallery::ImageDir;
use simple_gallery::TransitionConfig;
use std::net::SocketAddr;
use std::path::PathBuf;
use tower_http::services::ServeDir;
use tower_http::services::ServeFile;
use rand::thread_rng;
use rand::seq::SliceRandom;
#[macro_use]
extern crate log;
use env_logger::Env;
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(long, value_parser, default_value = "img")]
directory: PathBuf,
#[clap(long, value_parser, default_value = "127.0.0.1:3000")]
bind_address: SocketAddr,
#[clap(long, value_parser, default_value = "simple-gallery")]
title: String,
#[clap(long, value_parser, default_value_t = false)]
generate: bool,
#[clap(long, value_parser, default_value_t = true)]
shuffle: bool,
#[clap(long, default_value = "png")]
file_extension: String,
#[clap(long, default_value_t = 5)]
duration: usize,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
let env = Env::default().filter_or("RUST_LOG", "debug,hyper=info");
env_logger::init_from_env(env);
let i = ImageDir {
path: args.directory.clone(),
file_extension: args.file_extension,
};
let mut imgs = i.find_images();
let n_imgs = imgs.len();
if n_imgs == 0 {
warn!("found {} images in {}", n_imgs, &args.directory.display());
} else {
debug!("found {} images in {}", n_imgs, &args.directory.display());
}
if args.shuffle {
let mut rng = thread_rng();
imgs.shuffle(&mut rng);
}
let c = TransitionConfig::new(imgs, args.title, args.duration, args.shuffle);
let html = c.generate_html()?;
if args.generate {
debug!(
"generating HTML, finding images in {}",
&args.directory.display()
);
println!("{}", html);
} else {
let app = Router::new()
.route("/", get(move || async { Html(html) }))
.nest_service(
format!("/{}", c.static_route).as_str(),
ServeDir::new(&args.directory),
)
.route_service("/random", RandomFileServer::new(i));
info!("starting webserver, binding to {}", args.bind_address);
let listener = tokio::net::TcpListener::bind(args.bind_address).await?;
axum::serve(listener, app.into_make_service()).await?;
}
Ok(())
}
#[derive(Clone)]
pub struct RandomFileServer(ImageDir);
impl RandomFileServer {
pub fn new(image_dir: ImageDir) -> Self {
Self(image_dir)
}
}
use http::Request;
use std::task::{Context, Poll};
use tower_service::Service;
impl<ReqBody> Service<Request<ReqBody>> for RandomFileServer
where
ReqBody: Send + 'static,
{
type Error = <ServeDir as Service<Request<ReqBody>>>::Error;
type Response = <ServeDir as Service<Request<ReqBody>>>::Response;
type Future = <ServeDir as Service<Request<ReqBody>>>::Future;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let r = self.0.get_random_image();
debug!("looked up fresh random image {} (in call)", r.display());
let mut file_server = ServeFile::new(r);
file_server.call(req)
}
}