diff --git a/Cargo.lock b/Cargo.lock index 93038b09..30532c72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "atom" version = "0.3.6" @@ -87,31 +78,13 @@ checksum = "2da1976d75adbe5fbc88130ecd119529cf1cc6a93ae1546d8696ee66f0d21af1" [[package]] name = "block-buffer" -version = "0.7.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "block-padding", - "byte-tools", - "byteorder", "generic-array", ] -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byteorder" version = "1.4.3" @@ -132,18 +105,40 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.34.0" +version = "4.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "0eb41c13df48950b20eb4cd0eefa618819469df1bffc49d11e8487c4ba0037e5" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_derive", + "clap_lex", + "once_cell", "strsim", - "term_size", - "textwrap", - "unicode-width", - "vec_map", + "termcolor", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -166,6 +161,15 @@ dependencies = [ "xdg", ] +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -176,12 +180,23 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.8.1" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer", + "crypto-common", ] [[package]] @@ -191,10 +206,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" [[package]] -name = "fake-simd" +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] [[package]] name = "fancy-regex" @@ -226,11 +256,12 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "generic-array" -version = "0.12.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", + "version_check", ] [[package]] @@ -261,6 +292,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -280,6 +317,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-lifetimes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + [[package]] name = "itoa" version = "0.4.8" @@ -320,10 +363,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] -name = "maplit" -version = "1.0.2" +name = "linux-raw-sys" +version = "0.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" [[package]] name = "memchr" @@ -388,25 +431,26 @@ dependencies = [ ] [[package]] -name = "opaque-debug" -version = "0.2.3" +name = "os_str_bytes" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" [[package]] name = "pest" -version = "2.1.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "d5fd9bc6500181952d34bd0b2b0163a54d794227b498be0b7afa7698d0a7b18f" dependencies = [ "pest", "pest_generator", @@ -414,9 +458,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "d2610d5ac5156217b4ff8e46ddcef7cdf44b273da2ac5bca2ecbfa86a330e7c4" dependencies = [ "pest", "pest_meta", @@ -427,13 +471,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "824749bf7e21dd66b36fbe26b3f45c713879cccd4a009a917ab8e045ca8246fe" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1", + "sha1", ] [[package]] @@ -462,13 +506,37 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -662,6 +730,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rusty-fork" version = "0.3.0" @@ -724,15 +806,14 @@ dependencies = [ ] [[package]] -name = "sha-1" -version = "0.8.2" +name = "sha1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "block-buffer", + "cfg-if", + "cpufeatures", "digest", - "fake-simd", - "opaque-debug", ] [[package]] @@ -743,9 +824,9 @@ checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -797,23 +878,22 @@ dependencies = [ ] [[package]] -name = "term_size" -version = "0.3.2" +name = "termcolor" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "libc", - "winapi", + "winapi-util", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "terminal_size" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "40ca90c434fd12083d1a6bdcbe9f92a14f96c8a1ba600ba451734ac334521f7a" dependencies = [ - "term_size", - "unicode-width", + "rustix", + "windows-sys", ] [[package]] @@ -875,21 +955,21 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" [[package]] name = "typenum" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] -name = "unicode-width" -version = "0.1.10" +name = "unicode-ident" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-xid" @@ -904,10 +984,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] -name = "vec_map" -version = "0.8.2" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" @@ -972,6 +1052,63 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "xdg" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index 6411d9f0..e7d89413 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ regex = "1.5.5" once_cell = "1.13.0" entities = "1.0.1" unicode_categories = "0.1.1" -clap = { version = "2.34.0", optional = true, features = ["wrap_help"] } memchr = "2" pest = "2" pest_derive = "2" @@ -48,6 +47,8 @@ xdg = { version = "^2.1", optional = true } [target.'cfg(target_arch="wasm32")'.dependencies] syntect = { version = "5.0", optional = true, default-features = false, features = ["default-fancy"] } +clap = { version = "4.0", optional = true, features = ["derive", "string"] } [target.'cfg(not(target_arch="wasm32"))'.dependencies] syntect = { version = "5.0", optional = true, default-features = false, features = ["default-themes", "default-syntaxes", "html", "regex-onig"] } +clap = { version = "4.0", optional = true, features = ["derive", "string", "wrap_help"] } diff --git a/src/main.rs b/src/main.rs index 0a05c9e7..2ce29c42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,261 +17,229 @@ use comrak::{ use comrak::adapters::SyntaxHighlighterAdapter; use comrak::plugins::syntect::SyntectAdapter; use std::boxed::Box; -use std::collections::BTreeSet; use std::env; use std::error::Error; use std::fs; use std::io::Read; +use std::path::PathBuf; use std::process; +use clap::Parser; + const EXIT_SUCCESS: i32 = 0; -const EXIT_UNKNOWN_EXTENSION: i32 = 1; const EXIT_PARSE_CONFIG: i32 = 2; const EXIT_READ_INPUT: i32 = 3; -fn main() -> Result<(), Box> { - let default_config_path = get_default_config_path(); - - let app = clap::App::new(crate_name!()) - .version(crate_version!()) - .author(crate_authors!()) - .about(crate_description!()) - .after_help("\ +#[derive(Debug, Parser)] +#[command(about, author, version)] +#[command(after_help = "\ By default, Comrak will attempt to read command-line options from a config file specified by \ ---config-file. This behaviour can be disabled by passing --config-file none. It is not an error \ +--config-file. This behaviour can be disabled by passing --config-file none. It is not an error \ if the file does not exist.\ - ") - .arg( - clap::Arg::with_name("file") - .value_name("FILE") - .multiple(true) - .help("The CommonMark file to parse; or standard input if none passed"), - ) - .arg( - clap::Arg::with_name("config-file") - .short("c") - .long("config-file") - .help("Path to config file containing command-line arguments, or `none'") - .value_name("PATH") - .takes_value(true) - .default_value(&default_config_path), - ) - .arg( - clap::Arg::with_name("hardbreaks") - .long("hardbreaks") - .help("Treat newlines as hard line breaks"), - ) - .arg( - clap::Arg::with_name("smart") - .long("smart") - .help("Use smart punctuation"), - ) - .arg( - clap::Arg::with_name("github-pre-lang") - .long("github-pre-lang") - .help("Use GitHub-style
 for code blocks"),
-        )
-        .arg(
-            clap::Arg::with_name("gfm")
-                .long("gfm")
-                .help("Enable GitHub-flavored markdown extensions strikethrough, tagfilter, table, autolink, and tasklist. It also enables --github-pre-lang.")
-        )
-        .arg(
-            clap::Arg::with_name("default-info-string")
-                .long("default-info-string")
-                .help("Default value for fenced code block's info strings if none is given")
-                .value_name("INFO")
-                .takes_value(true),
-        )
-        .arg(
-            clap::Arg::with_name("unsafe")
-                .long("unsafe")
-                .help("Allow raw HTML and dangerous URLs"),
-        )
-        .arg(
-            clap::Arg::with_name("escape")
-                .long("escape")
-                .help("Escape raw HTML instead of clobbering it"),
-        )
-        .arg(
-            clap::Arg::with_name("extension")
-                .short("e")
-                .long("extension")
-                .takes_value(true)
-                .number_of_values(1)
-                .multiple(true)
-                .possible_values(&[
-                    "strikethrough",
-                    "tagfilter",
-                    "table",
-                    "autolink",
-                    "tasklist",
-                    "superscript",
-                    "footnotes",
-                    "description-lists",
-                ])
-                .value_name("EXTENSION")
-                .help("Specify an extension name to use"),
-        )
-        .arg(
-            clap::Arg::with_name("format")
-                .short("t")
-                .long("to")
-                .takes_value(true)
-                .possible_values(&["html", "commonmark"])
-                .default_value("html")
-                .value_name("FORMAT")
-                .help("Specify output format"),
-        )
-        .arg(
-            clap::Arg::with_name("output")
-                .short("o")
-                .long("output")
-                .takes_value(true)
-                .value_name("FILE")
-                .help("Write output to FILE instead of stdout"),
-        )
-        .arg(
-            clap::Arg::with_name("width")
-                .long("width")
-                .takes_value(true)
-                .value_name("WIDTH")
-                .default_value("0")
-                .help("Specify wrap width (0 = nowrap)"),
-        )
-        .arg(
-            clap::Arg::with_name("header-ids")
-                .long("header-ids")
-                .takes_value(true)
-                .value_name("PREFIX")
-                .help("Use the Comrak header IDs extension, with the given ID prefix"),
-        )
-        .arg(
-            clap::Arg::with_name("front-matter-delimiter")
-                .long("front-matter-delimiter")
-                .takes_value(true)
-                .value_name("DELIMITER")
-                .help("Ignore front-matter that starts and ends with the given string")
-                .allow_hyphen_values(true),
-        )
-        .arg(
-            clap::Arg::with_name("syntax-highlighting")
-                .long("syntax-highlighting")
-                .takes_value(true)
-                .value_name("THEME")
-                .help("Syntax highlighting for codefence blocks. Choose a theme or 'none' for disabling.")
-                .default_value("base16-ocean.dark"),
-        )
-        .arg(
-            clap::Arg::with_name("list-style")
-                .long("list-style")
-                .takes_value(true)
-                .possible_values(&["dash", "plus", "star"])
-                .default_value("dash")
-                .value_name("LIST_STYLE")
-                .help("Specify bullet character for lists (-, +, *) in CommonMark ouput"),
-        );
-
-    let mut matches = app.clone().get_matches();
-
-    let config_file_path = matches.value_of("config-file").unwrap();
-    if config_file_path != "none" {
-        if let Ok(args) = fs::read_to_string(config_file_path) {
-            match shell_words::split(&args) {
-                Ok(mut args) => {
-                    for (i, arg) in env::args_os().enumerate() {
-                        if let Some(s) = arg.to_str() {
-                            args.insert(i, s.into());
-                        }
+        ")]
+struct Cli {
+    /// CommonMark file(s) to parse; or standard input if none passed
+    #[arg(value_name = "FILE")]
+    files: Option>,
+
+    /// Path to config file containing command-line arguments, or 'none'
+    #[arg(short, long, value_name = "PATH", default_value = get_default_config_path())]
+    config_file: String,
+
+    /// Treat newlines as hard line breaks
+    #[arg(long)]
+    hardbreaks: bool,
+
+    /// Use smart punctuation
+    #[arg(long)]
+    smart: bool,
+
+    /// Use GitHub-style 
 for code blocks
+    #[arg(long)]
+    github_pre_lang: bool,
+
+    /// Enable GitHub-flavored markdown extensions: strikethrough, tagfilter, table, autolink, and tasklist.
+    /// Also enables --github-pre-lang.
+    #[arg(long)]
+    gfm: bool,
+
+    /// Default value for fenced code block's info strings if none is given
+    #[arg(long, value_name = "INFO")]
+    default_info_string: Option,
+
+    /// Allow raw HTML and dangerous URLs
+    #[arg(long = "unsafe")]
+    unsafe_: bool,
+
+    /// Escape raw HTML instead of clobbering it
+    #[arg(long)]
+    escape: bool,
+
+    /// Specify extension name(s) to use
+    ///
+    /// Multiple extensions can be delimited with ",", e.g. --extension strikethrough,table
+    #[arg(
+        short,
+        long = "extension",
+        value_name = "EXTENSION",
+        value_delimiter = ',',
+        value_enum
+    )]
+    extensions: Vec,
+
+    /// Specify output format
+    #[arg(short = 't', long = "to", value_enum, default_value_t = Format::Html)]
+    format: Format,
+
+    /// Write output to FILE instead of stdout
+    #[arg(short, long, value_name = "FILE")]
+    output: Option,
+
+    /// Specify wrap width (0 = nowrap)
+    #[arg(long, default_value_t = 0)]
+    width: usize,
+
+    /// Use the Comrak header IDs extension, with the given ID prefix
+    #[arg(long, value_name = "PREFIX")]
+    header_ids: Option,
+
+    /// Ignore front-matter that starts and ends with the given string
+    #[arg(long, value_name = "DELIMITER", allow_hyphen_values = true)]
+    front_matter_delimiter: Option,
+
+    /// Syntax highlighting for codefence blocks. Choose a theme or 'none' for disabling.
+    #[arg(long, value_name = "THEME", default_value = "base16-ocean.dark")]
+    syntax_highlighting: String,
+
+    /// Specify bullet character for lists (-, +, *) in CommonMark ouput
+    #[arg(long, value_enum, default_value_t = ListStyle::Dash)]
+    list_style: ListStyle,
+}
+
+#[derive(Clone, Copy, Debug, ValueEnum)]
+enum Format {
+    Html,
+
+    #[value(name = "commonmark")]
+    CommonMark,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
+enum Extension {
+    Strikethrough,
+    Tagfilter,
+    Table,
+    Autolink,
+    Tasklist,
+    Superscript,
+    Footnotes,
+    DescriptionLists,
+}
+
+#[derive(Clone, Copy, Debug, ValueEnum)]
+enum ListStyle {
+    Dash,
+    Plus,
+    Star,
+}
+
+impl From for ListStyleType {
+    fn from(style: ListStyle) -> Self {
+        match style {
+            ListStyle::Dash => Self::Dash,
+            ListStyle::Plus => Self::Plus,
+            ListStyle::Star => Self::Star,
+        }
+    }
+}
+
+fn cli_with_config() -> Cli {
+    let cli = Cli::parse();
+    let config_file_path = &cli.config_file;
+
+    if config_file_path == "none" {
+        return cli;
+    }
+
+    if let Ok(args) = fs::read_to_string(config_file_path) {
+        match shell_words::split(&args) {
+            Ok(mut args) => {
+                for (i, arg) in env::args_os().enumerate() {
+                    if let Some(s) = arg.to_str() {
+                        args.insert(i, s.into());
                     }
-                    matches = app.get_matches_from(args);
-                }
-                Err(e) => {
-                    eprintln!("failed to parse {}: {}", config_file_path, e);
-                    process::exit(EXIT_PARSE_CONFIG);
                 }
+
+                Cli::parse_from(args)
+            }
+            Err(e) => {
+                eprintln!("failed to parse {}: {}", config_file_path, e);
+                process::exit(EXIT_PARSE_CONFIG);
             }
         }
+    } else {
+        cli
     }
+}
 
-    let mut exts = matches
-        .values_of("extension")
-        .map_or(BTreeSet::new(), |vals| vals.collect());
+fn main() -> Result<(), Box> {
+    let cli = cli_with_config();
+
+    let exts = &cli.extensions;
 
     let options = ComrakOptions {
         extension: ComrakExtensionOptions {
-            strikethrough: exts.remove("strikethrough") || matches.is_present("gfm"),
-            tagfilter: exts.remove("tagfilter") || matches.is_present("gfm"),
-            table: exts.remove("table") || matches.is_present("gfm"),
-            autolink: exts.remove("autolink") || matches.is_present("gfm"),
-            tasklist: exts.remove("tasklist") || matches.is_present("gfm"),
-            superscript: exts.remove("superscript"),
-            header_ids: matches.value_of("header-ids").map(|s| s.to_string()),
-            footnotes: exts.remove("footnotes"),
-            description_lists: exts.remove("description-lists"),
-            front_matter_delimiter: matches
-                .value_of("front-matter-delimiter")
-                .map(|s| s.to_string()),
+            strikethrough: exts.contains(&Extension::Strikethrough) || cli.gfm,
+            tagfilter: exts.contains(&Extension::Tagfilter) || cli.gfm,
+            table: exts.contains(&Extension::Table) || cli.gfm,
+            autolink: exts.contains(&Extension::Autolink) || cli.gfm,
+            tasklist: exts.contains(&Extension::Tasklist) || cli.gfm,
+            superscript: exts.contains(&Extension::Superscript),
+            header_ids: cli.header_ids,
+            footnotes: exts.contains(&Extension::Footnotes),
+            description_lists: exts.contains(&Extension::DescriptionLists),
+            front_matter_delimiter: cli.front_matter_delimiter,
         },
         parse: ComrakParseOptions {
-            smart: matches.is_present("smart"),
-            default_info_string: matches
-                .value_of("default-info-string")
-                .map(|e| e.to_owned()),
+            smart: cli.smart,
+            default_info_string: cli.default_info_string,
         },
         render: ComrakRenderOptions {
-            hardbreaks: matches.is_present("hardbreaks"),
-            github_pre_lang: matches.is_present("github-pre-lang") || matches.is_present("gfm"),
-            width: matches
-                .value_of("width")
-                .unwrap_or("0")
-                .parse()
-                .unwrap_or(0),
-            unsafe_: matches.is_present("unsafe"),
-            escape: matches.is_present("escape"),
-            list_style: matches
-                .value_of("list-style")
-                .unwrap_or("dash")
-                .parse::()
-                .expect("unknown list style"),
+            hardbreaks: cli.hardbreaks,
+            github_pre_lang: cli.github_pre_lang || cli.gfm,
+            width: cli.width,
+            unsafe_: cli.unsafe_,
+            escape: cli.escape,
+            list_style: cli.list_style.into(),
         },
     };
 
     let syntax_highlighter: Option<&dyn SyntaxHighlighterAdapter>;
-    let theme: &str = match matches.value_of("syntax-highlighting") {
-        Some(theme) => theme,
-        None => "",
-    };
-
     let mut plugins: ComrakPlugins = ComrakPlugins::default();
     let adapter: SyntectAdapter;
 
+    let theme = cli.syntax_highlighting;
     if theme.is_empty() || theme == "none" {
         syntax_highlighter = None;
     } else {
-        adapter = SyntectAdapter::new(theme);
+        adapter = SyntectAdapter::new(&theme);
         syntax_highlighter = Some(&adapter);
     }
 
-    if !exts.is_empty() {
-        eprintln!("unknown extensions: {:?}", exts);
-        process::exit(EXIT_UNKNOWN_EXTENSION);
-    }
-
     let mut s: Vec = Vec::with_capacity(2048);
 
-    match matches.values_of("file") {
+    match cli.files {
         None => {
             std::io::stdin().read_to_end(&mut s)?;
         }
         Some(fs) => {
-            for f in fs {
+            for f in &fs {
                 match fs::File::open(f) {
                     Ok(mut io) => {
                         io.read_to_end(&mut s)?;
                     }
                     Err(e) => {
-                        eprintln!("failed to read {}: {}", f, e);
+                        eprintln!("failed to read {}: {}", f.display(), e);
                         process::exit(EXIT_READ_INPUT);
                     }
                 }
@@ -282,16 +250,15 @@ if the file does not exist.\
     let arena = Arena::new();
     let root = comrak::parse_document(&arena, &String::from_utf8(s)?, &options);
 
-    let formatter = match matches.value_of("format") {
-        Some("html") => {
+    let formatter = match cli.format {
+        Format::Html => {
             plugins.render.codefence_syntax_highlighter = syntax_highlighter;
             comrak::format_html_with_plugins
         }
-        Some("commonmark") => comrak::format_commonmark_with_plugins,
-        _ => panic!("unknown format"),
+        Format::CommonMark => comrak::format_commonmark_with_plugins,
     };
 
-    if let Some(output_filename) = matches.value_of("output") {
+    if let Some(output_filename) = cli.output {
         formatter(
             root,
             &options,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 4582748c..fd18e209 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -19,7 +19,7 @@ use std::cmp::min;
 use std::collections::HashMap;
 use std::fmt::{Debug, Formatter};
 use std::mem;
-use std::str::{self, FromStr};
+use std::str;
 use strings;
 use typed_arena::Arena;
 
@@ -1931,16 +1931,3 @@ impl Default for ListStyleType {
         ListStyleType::Dash
     }
 }
-
-impl FromStr for ListStyleType {
-    type Err = ();
-
-    fn from_str(input: &str) -> Result {
-        match input {
-            "dash" => Ok(ListStyleType::Dash),
-            "plus" => Ok(ListStyleType::Plus),
-            "star" => Ok(ListStyleType::Star),
-            _ => Err(()),
-        }
-    }
-}