|
|
|
mod configs;
|
|
|
|
mod error;
|
|
|
|
mod handlers;
|
|
|
|
mod models;
|
|
|
|
mod repo;
|
|
|
|
|
|
|
|
use std::{fs, sync::Arc};
|
|
|
|
|
|
|
|
use axum::{routing::get, Router};
|
|
|
|
use configs::Config;
|
|
|
|
use error::{AppError, AppResult};
|
|
|
|
use models::AppState;
|
|
|
|
use reqwest::Url;
|
|
|
|
use tokio::{net::TcpListener, sync::RwLock};
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
|
|
|
|
/// Parse urls and return 2 lists: valid urls and errors
|
|
|
|
fn parse_urls(url_list: &[String]) -> (Vec<Url>, Vec<String>) {
|
|
|
|
let (valid, invalid): (Vec<_>, Vec<_>) = url_list
|
|
|
|
.into_iter()
|
|
|
|
.map(|url| Url::parse(&url).map_err(|e| AppError::InvalidUrl(url.to_string(), e)))
|
|
|
|
.partition(Result::is_ok);
|
|
|
|
|
|
|
|
(
|
|
|
|
valid.into_iter().map(Result::unwrap).collect(),
|
|
|
|
invalid
|
|
|
|
.into_iter()
|
|
|
|
.map(|e| e.unwrap_err().to_string())
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Extract raw urls from different sources:
|
|
|
|
/// - from args list
|
|
|
|
/// - from file
|
|
|
|
fn parse_urls_from_args(
|
|
|
|
args: impl IntoIterator<Item = String>,
|
|
|
|
) -> AppResult<(Vec<Url>, Vec<String>)> {
|
|
|
|
let mut args_iter = args.into_iter();
|
|
|
|
|
|
|
|
match args_iter.nth(1) {
|
|
|
|
Some(action) => match action.as_str() {
|
|
|
|
"-f" => {
|
|
|
|
let Some(source) = args_iter.next() else {
|
|
|
|
return Err(AppError::UsageError);
|
|
|
|
};
|
|
|
|
println!("Reading file '{source}'");
|
|
|
|
let raw_urls = fs::read_to_string(source)?
|
|
|
|
.split('\n')
|
|
|
|
.map(|s| s.to_owned())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
Ok(parse_urls(&raw_urls))
|
|
|
|
}
|
|
|
|
"-u" => {
|
|
|
|
let urls_list = args_iter.collect::<Vec<_>>();
|
|
|
|
Ok(parse_urls(&urls_list))
|
|
|
|
}
|
|
|
|
_ => return Err(AppError::UsageError),
|
|
|
|
},
|
|
|
|
None => return Err(AppError::UsageError),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn process_urls(config: &Config, urls: &[Url]) {}
|
|
|
|
|
|
|
|
async fn run() -> AppResult<()> {
|
|
|
|
dotenv::dotenv().ok();
|
|
|
|
|
|
|
|
let cfg = configs::get_configs()?;
|
|
|
|
info!("{cfg:?}");
|
|
|
|
|
|
|
|
// let (valid_urls, errors) = parse_urls_from_args(args)?;
|
|
|
|
let state = Arc::new(RwLock::new(AppState::new(cfg.url_handlers_pool_size)));
|
|
|
|
|
|
|
|
let app = Router::new()
|
|
|
|
.route(
|
|
|
|
"/urls",
|
|
|
|
get(handlers::get_urls)
|
|
|
|
.post(handlers::upload_urls)
|
|
|
|
.delete(handlers::delete_urls),
|
|
|
|
)
|
|
|
|
.with_state(state);
|
|
|
|
let listener = TcpListener::bind("0.0.0.0:8000").await?;
|
|
|
|
|
|
|
|
axum::serve(listener, app).await.map_err(AppError::from)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
if let Err(e) = run().await {
|
|
|
|
println!("{e}");
|
|
|
|
}
|
|
|
|
}
|