diff --git a/Cargo.lock b/Cargo.lock index 2d72de3..a7af712 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,40 +62,61 @@ dependencies = [ "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]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "article_scraper" version = "2.0.0-alpha.0" @@ -314,6 +335,48 @@ dependencies = [ "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]] name = "codespan-reporting" version = "0.11.1" @@ -330,6 +393,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "core-foundation" version = "0.9.3" @@ -361,11 +430,11 @@ version = "0.1.0" dependencies = [ "ansi-to-html", "anyhow", - "argh", "article_scraper", "axum", "bytes", "chrono", + "clap", "dotenvy", "feed-rs", "maud", @@ -1218,6 +1287,18 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "itertools" version = "0.10.5" @@ -2766,6 +2847,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.2" diff --git a/Cargo.toml b/Cargo.toml index cd91d3a..a6a6f1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "crawlnicle" version = "0.1.0" edition = "2021" default-run = "crawlnicle" +authors = ["Tyler Hallada "] [lib] name = "lib" @@ -13,11 +14,11 @@ path = "src/lib.rs" [dependencies] ansi-to-html = "0.1" anyhow = "1" -argh = "0.1" article_scraper = "2.0.0-alpha.0" axum = "0.6" bytes = "1.4" chrono = { version = "0.4", features = ["serde"] } +clap = { version = "4.3", features = ["derive", "env"] } dotenvy = "0.15" feed-rs = "1.3" maud = { version = "0.25", features = ["axum"] } diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 175ec0a..0c685a5 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use argh::FromArgs; +use clap::{Args, Parser, Subcommand}; use chrono::Utc; use dotenvy::dotenv; 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::uuid::Base62Uuid; -#[derive(FromArgs)] /// CLI for crawlnicle -struct Args { - #[argh(subcommand)] +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] commands: Commands, } -#[derive(FromArgs)] -#[argh(subcommand)] +#[derive(Subcommand)] enum Commands { + /// Fetches new entries from all feeds in the database + Crawl, AddFeed(AddFeed), DeleteFeed(DeleteFeed), AddEntry(AddEntry), DeleteEntry(DeleteEntry), - Crawl(Crawl), } -#[derive(FromArgs)] /// Add a feed to the database -#[argh(subcommand, name = "add-feed")] +#[derive(Args)] struct AddFeed { - #[argh(option)] /// title of the feed (max 255 characters) + #[arg(short, long)] title: Option, - #[argh(option)] /// URL of the feed (max 2048 characters) + #[arg(short, long)] url: String, - #[argh(option, long = "type")] /// type of the feed ('rss' or 'atom') + #[arg(short, long)] feed_type: FeedType, - #[argh(option)] /// description of the feed + #[arg(short, long)] description: Option, } -#[derive(FromArgs)] +#[derive(Args)] /// Delete a feed from the database -#[argh(subcommand, name = "delete-feed")] struct DeleteFeed { - #[argh(positional)] /// id of the feed to delete id: Uuid, } -#[derive(FromArgs)] +#[derive(Args)] /// Add an entry to the database -#[argh(subcommand, name = "add-entry")] struct AddEntry { - #[argh(option)] /// title of the entry (max 255 characters) + #[arg(short, long)] title: Option, - #[argh(option)] /// URL of the entry (max 2048 characters) + #[arg(short, long)] url: String, - #[argh(option)] /// description of the entry + #[arg(short, long)] description: Option, - #[argh(option)] /// source feed for the entry + #[arg(short, long)] feed_id: Uuid, } -#[derive(FromArgs)] +#[derive(Args)] /// Delete an entry from the database -#[argh(subcommand, name = "delete-entry")] struct DeleteEntry { - #[argh(positional)] /// id of the entry to delete id: Uuid, } -#[derive(FromArgs)] -/// Delete an entry from the database -#[argh(subcommand, name = "crawl")] -struct Crawl {} - #[tokio::main] pub async fn main() -> Result<()> { dotenv().ok(); @@ -99,9 +90,9 @@ pub async fn main() -> Result<()> { .connect(&env::var("DATABASE_URL")?) .await?; - let args: Args = argh::from_env(); + let cli: Cli = Cli::parse(); - match args.commands { + match cli.commands { Commands::AddFeed(args) => { let feed = create_feed( &pool, @@ -138,7 +129,7 @@ pub async fn main() -> Result<()> { delete_entry(&pool, args.id).await?; info!("Deleted entry with id {}", Base62Uuid::from(args.id)); } - Commands::Crawl(_) => { + Commands::Crawl => { info!("Crawling..."); crawl(&pool).await?; } diff --git a/src/config.rs b/src/config.rs index edf54a8..680d777 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,31 +1,17 @@ -use anyhow::{Context, Result}; +use clap::Parser; -#[derive(Clone, Debug)] +#[derive(Parser, Clone, Debug)] pub struct Config { + #[clap(long, env)] pub database_url: String, + #[clap(long, env)] pub database_max_connections: u32, + #[clap(long, env)] pub host: String, + #[clap(long, env)] pub port: u16, + #[clap(long, env)] pub title: String, + #[clap(long, env)] pub max_mem_log_size: usize, } - -impl Config { - pub fn new() -> Result { - 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, - }) - } -} diff --git a/src/main.rs b/src/main.rs index eaade8e..65f01e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use axum::{ Router, }; use bytes::Bytes; +use clap::Parser; use dotenvy::dotenv; use notify::Watcher; use sqlx::postgres::PgPoolOptions; @@ -32,7 +33,7 @@ async fn serve(app: Router, addr: SocketAddr) -> Result<()> { async fn main() -> Result<()> { dotenv().ok(); - let config = Config::new()?; + let config = Config::parse(); let (log_sender, log_receiver) = channel::(Bytes::new()); let _guards = init_tracing(&config, log_sender)?; diff --git a/src/models/feed.rs b/src/models/feed.rs index 11e12df..537c1d4 100644 --- a/src/models/feed.rs +++ b/src/models/feed.rs @@ -8,7 +8,7 @@ use validator::Validate; 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")] #[serde(rename_all = "lowercase")] pub enum FeedType {