Replace argh with clap

Mostly for the more concise Config parsing and error handling.
This commit is contained in:
Tyler Hallada 2023-06-27 14:03:52 -04:00
parent abd540d2ff
commit 7e06d23bba
6 changed files with 153 additions and 87 deletions

145
Cargo.lock generated
View File

@ -62,40 +62,61 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.71" version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "argh"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e"
dependencies = [
"argh_derive",
"argh_shared",
]
[[package]]
name = "argh_derive"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6"
dependencies = [
"argh_shared",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "argh_shared"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
[[package]] [[package]]
name = "article_scraper" name = "article_scraper"
version = "2.0.0-alpha.0" version = "2.0.0-alpha.0"
@ -314,6 +335,48 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "clap"
version = "4.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -330,6 +393,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -361,11 +430,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ansi-to-html", "ansi-to-html",
"anyhow", "anyhow",
"argh",
"article_scraper", "article_scraper",
"axum", "axum",
"bytes", "bytes",
"chrono", "chrono",
"clap",
"dotenvy", "dotenvy",
"feed-rs", "feed-rs",
"maud", "maud",
@ -1218,6 +1287,18 @@ version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
"rustix",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.10.5"
@ -2766,6 +2847,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.3.2" version = "1.3.2"

View File

@ -3,6 +3,7 @@ name = "crawlnicle"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
default-run = "crawlnicle" default-run = "crawlnicle"
authors = ["Tyler Hallada <tyler@hallada.net>"]
[lib] [lib]
name = "lib" name = "lib"
@ -13,11 +14,11 @@ path = "src/lib.rs"
[dependencies] [dependencies]
ansi-to-html = "0.1" ansi-to-html = "0.1"
anyhow = "1" anyhow = "1"
argh = "0.1"
article_scraper = "2.0.0-alpha.0" article_scraper = "2.0.0-alpha.0"
axum = "0.6" axum = "0.6"
bytes = "1.4" bytes = "1.4"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.3", features = ["derive", "env"] }
dotenvy = "0.15" dotenvy = "0.15"
feed-rs = "1.3" feed-rs = "1.3"
maud = { version = "0.25", features = ["axum"] } maud = { version = "0.25", features = ["axum"] }

View File

@ -1,5 +1,5 @@
use anyhow::Result; use anyhow::Result;
use argh::FromArgs; use clap::{Args, Parser, Subcommand};
use chrono::Utc; use chrono::Utc;
use dotenvy::dotenv; use dotenvy::dotenv;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
@ -12,82 +12,73 @@ use lib::models::feed::{create_feed, delete_feed, CreateFeed, FeedType};
use lib::models::entry::{create_entry, delete_entry, CreateEntry}; use lib::models::entry::{create_entry, delete_entry, CreateEntry};
use lib::uuid::Base62Uuid; use lib::uuid::Base62Uuid;
#[derive(FromArgs)]
/// CLI for crawlnicle /// CLI for crawlnicle
struct Args { #[derive(Parser)]
#[argh(subcommand)] #[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
struct Cli {
#[command(subcommand)]
commands: Commands, commands: Commands,
} }
#[derive(FromArgs)] #[derive(Subcommand)]
#[argh(subcommand)]
enum Commands { enum Commands {
/// Fetches new entries from all feeds in the database
Crawl,
AddFeed(AddFeed), AddFeed(AddFeed),
DeleteFeed(DeleteFeed), DeleteFeed(DeleteFeed),
AddEntry(AddEntry), AddEntry(AddEntry),
DeleteEntry(DeleteEntry), DeleteEntry(DeleteEntry),
Crawl(Crawl),
} }
#[derive(FromArgs)]
/// Add a feed to the database /// Add a feed to the database
#[argh(subcommand, name = "add-feed")] #[derive(Args)]
struct AddFeed { struct AddFeed {
#[argh(option)]
/// title of the feed (max 255 characters) /// title of the feed (max 255 characters)
#[arg(short, long)]
title: Option<String>, title: Option<String>,
#[argh(option)]
/// URL of the feed (max 2048 characters) /// URL of the feed (max 2048 characters)
#[arg(short, long)]
url: String, url: String,
#[argh(option, long = "type")]
/// type of the feed ('rss' or 'atom') /// type of the feed ('rss' or 'atom')
#[arg(short, long)]
feed_type: FeedType, feed_type: FeedType,
#[argh(option)]
/// description of the feed /// description of the feed
#[arg(short, long)]
description: Option<String>, description: Option<String>,
} }
#[derive(FromArgs)] #[derive(Args)]
/// Delete a feed from the database /// Delete a feed from the database
#[argh(subcommand, name = "delete-feed")]
struct DeleteFeed { struct DeleteFeed {
#[argh(positional)]
/// id of the feed to delete /// id of the feed to delete
id: Uuid, id: Uuid,
} }
#[derive(FromArgs)] #[derive(Args)]
/// Add an entry to the database /// Add an entry to the database
#[argh(subcommand, name = "add-entry")]
struct AddEntry { struct AddEntry {
#[argh(option)]
/// title of the entry (max 255 characters) /// title of the entry (max 255 characters)
#[arg(short, long)]
title: Option<String>, title: Option<String>,
#[argh(option)]
/// URL of the entry (max 2048 characters) /// URL of the entry (max 2048 characters)
#[arg(short, long)]
url: String, url: String,
#[argh(option)]
/// description of the entry /// description of the entry
#[arg(short, long)]
description: Option<String>, description: Option<String>,
#[argh(option)]
/// source feed for the entry /// source feed for the entry
#[arg(short, long)]
feed_id: Uuid, feed_id: Uuid,
} }
#[derive(FromArgs)] #[derive(Args)]
/// Delete an entry from the database /// Delete an entry from the database
#[argh(subcommand, name = "delete-entry")]
struct DeleteEntry { struct DeleteEntry {
#[argh(positional)]
/// id of the entry to delete /// id of the entry to delete
id: Uuid, id: Uuid,
} }
#[derive(FromArgs)]
/// Delete an entry from the database
#[argh(subcommand, name = "crawl")]
struct Crawl {}
#[tokio::main] #[tokio::main]
pub async fn main() -> Result<()> { pub async fn main() -> Result<()> {
dotenv().ok(); dotenv().ok();
@ -99,9 +90,9 @@ pub async fn main() -> Result<()> {
.connect(&env::var("DATABASE_URL")?) .connect(&env::var("DATABASE_URL")?)
.await?; .await?;
let args: Args = argh::from_env(); let cli: Cli = Cli::parse();
match args.commands { match cli.commands {
Commands::AddFeed(args) => { Commands::AddFeed(args) => {
let feed = create_feed( let feed = create_feed(
&pool, &pool,
@ -138,7 +129,7 @@ pub async fn main() -> Result<()> {
delete_entry(&pool, args.id).await?; delete_entry(&pool, args.id).await?;
info!("Deleted entry with id {}", Base62Uuid::from(args.id)); info!("Deleted entry with id {}", Base62Uuid::from(args.id));
} }
Commands::Crawl(_) => { Commands::Crawl => {
info!("Crawling..."); info!("Crawling...");
crawl(&pool).await?; crawl(&pool).await?;
} }

View File

@ -1,31 +1,17 @@
use anyhow::{Context, Result}; use clap::Parser;
#[derive(Clone, Debug)] #[derive(Parser, Clone, Debug)]
pub struct Config { pub struct Config {
#[clap(long, env)]
pub database_url: String, pub database_url: String,
#[clap(long, env)]
pub database_max_connections: u32, pub database_max_connections: u32,
#[clap(long, env)]
pub host: String, pub host: String,
#[clap(long, env)]
pub port: u16, pub port: u16,
#[clap(long, env)]
pub title: String, pub title: String,
#[clap(long, env)]
pub max_mem_log_size: usize, pub max_mem_log_size: usize,
} }
impl Config {
pub fn new() -> Result<Config> {
let database_url = std::env::var("DATABASE_URL").context("DATABASE_URL not set")?;
let database_max_connections = std::env::var("DATABASE_MAX_CONNECTIONS").context("DATABASE_MAX_CONNECTIONS not set")?.parse()?;
let host = std::env::var("HOST").context("HOST not set")?;
let port = std::env::var("PORT").context("PORT not set")?.parse()?;
let title = std::env::var("TITLE").context("TITLE not set")?;
let max_mem_log_size = std::env::var("MAX_MEM_LOG_SIZE").context("MAX_MEM_LOG_SIZE not set")?.parse()?;
Ok(Config {
database_url,
database_max_connections,
host,
port,
title,
max_mem_log_size,
})
}
}

View File

@ -6,6 +6,7 @@ use axum::{
Router, Router,
}; };
use bytes::Bytes; use bytes::Bytes;
use clap::Parser;
use dotenvy::dotenv; use dotenvy::dotenv;
use notify::Watcher; use notify::Watcher;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
@ -32,7 +33,7 @@ async fn serve(app: Router, addr: SocketAddr) -> Result<()> {
async fn main() -> Result<()> { async fn main() -> Result<()> {
dotenv().ok(); dotenv().ok();
let config = Config::new()?; let config = Config::parse();
let (log_sender, log_receiver) = channel::<Bytes>(Bytes::new()); let (log_sender, log_receiver) = channel::<Bytes>(Bytes::new());
let _guards = init_tracing(&config, log_sender)?; let _guards = init_tracing(&config, log_sender)?;

View File

@ -8,7 +8,7 @@ use validator::Validate;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
#[derive(Debug, Serialize, Deserialize, sqlx::Type)] #[derive(Debug, Serialize, Deserialize, sqlx::Type, Clone)]
#[sqlx(type_name = "feed_type", rename_all = "lowercase")] #[sqlx(type_name = "feed_type", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum FeedType { pub enum FeedType {