use actix_web::{App, HttpServer, middleware::Logger, web::Data}; use clap::Parser; use utoipa::OpenApi; use crate::cli::{CliArgs, Command}; mod api; mod app; mod cli; mod storage; #[actix_web::main] #[expect( clippy::needless_for_each, reason = "utoipa generates this, we can't change it" )] async fn main() -> Result<(), std::io::Error> { #[derive(OpenApi)] #[openapi( paths( api::get::product::product_by_id, api::get::product::products, api::get::unit::units, api::get::unit::unit_by_id, api::get::unit_property::unit_property_by_id, api::get::unit_property::unit_properties, api::get::inventory::amount_by_id, api::set::product::register_product, api::set::product::associate_barcode, api::set::unit::register_unit, api::set::unit_property::register_unit_property, api::set::barcode::buy_barcode, api::set::barcode::consume_barcode, ), // security( // (), // ("my_auth" = ["read:items", "edit:items"]), // ("token_jwt" = []) // ), )] struct ApiDoc; env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); let args = CliArgs::parse(); match args.command { Command::Serve { host, port, db_path, } => { let data = Data::new( app::App::new(db_path) .await .map_err(|err| std::io::Error::other(main::Error::AppInit(err)))?, ); eprintln!("Serving at http://{host}:{port}"); HttpServer::new(move || { App::new() .wrap(Logger::new( r#"%a "%r" -> %s %b ("%{Referer}i" "%{User-Agent}i" %T s)"#, )) .app_data(Data::clone(&data)) .configure(api::get::register_paths) .configure(api::set::register_paths) }) .bind((host, port))? .run() .await } Command::OpenApi => { let openapi = ApiDoc::openapi(); println!("{}", openapi.to_pretty_json().expect("Comp-time constant")); Ok(()) } } } pub(crate) mod main { use crate::app::app_create; #[derive(thiserror::Error, Debug)] pub(crate) enum Error { #[error("Failed to initialize shared application state: {0}")] AppInit(#[from] app_create::Error), } }