Compare commits

..

No commits in common. "main" and "1.1.2" have entirely different histories.
main ... 1.1.2

37 changed files with 453 additions and 809 deletions

3
.envrc
View File

@ -1,3 +0,0 @@
#!/usr/bin/env bash
use flake

10
.gitignore vendored
View File

@ -1,4 +1,8 @@
.direnv/
coverage/
debug/
# Compiled files and executables.
target/
# Backup files generated by rustfmt.
**/*.rs.bk
# Code coverage reports
.coverage/

460
Cargo.lock generated
View File

@ -3,60 +3,20 @@
version = 3
[[package]]
name = "anstream"
version = "0.6.11"
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
"winapi",
]
[[package]]
name = "assert_cmd"
version = "2.0.13"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467"
checksum = "b800c4403e8105d959595e1f88119e78bc12bc874c4336973658b648a746ba93"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
"predicates",
@ -66,84 +26,46 @@ dependencies = [
]
[[package]]
name = "bitflags"
version = "1.3.2"
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bstr"
version = "1.9.0"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.4.18"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"ansi_term",
"atty",
"bitflags",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "console"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"windows-sys",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
@ -159,66 +81,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "encode_unicode"
version = "0.3.6"
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "hard-xml"
version = "1.34.0"
name = "hermit-abi"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fafc2bcb74049535eb6fab49eb20164a427867a9e809516ef95a98e961164432"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
dependencies = [
"hard-xml-derive",
"jetscii",
"lazy_static",
"memchr",
"xmlparser",
"libc",
]
[[package]]
name = "hard-xml-derive"
version = "1.34.0"
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a345b327da51b997c94f841d9db6b2d292c7632713bd8a1b8b191e8b819df7"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"bitflags",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "insta"
version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc"
dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"similar",
"yaml-rust",
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "jetscii"
version = "0.5.3"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e"
checksum = "c9447923c57a8a2d5c1b0875cdf96a6324275df728b498f2ede0e5cbde088a15"
[[package]]
name = "lazy_static"
@ -228,126 +124,118 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.152"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "memchr"
version = "2.7.1"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "opml"
version = "1.1.6"
version = "1.1.2"
dependencies = [
"hard-xml",
"serde",
"strong-xml",
"thiserror",
]
[[package]]
name = "opml_cli"
version = "1.1.6"
version = "1.1.2"
dependencies = [
"assert_cmd",
"clap",
"insta",
"opml",
"serde",
"serde_json",
"test-case",
]
[[package]]
name = "predicates"
version = "3.1.0"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
checksum = "c143348f141cc87aab5b950021bac6145d0e5ae754b0591de23244cee42c9308"
dependencies = [
"anstyle",
"difflib",
"itertools",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.6"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178"
[[package]]
name = "predicates-tree"
version = "1.0.9"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124"
dependencies = [
"predicates-core",
"termtree",
"treeline",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-ident",
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "ryu"
version = "1.0.16"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "serde"
version = "1.0.195"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.195"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.111"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
@ -355,109 +243,98 @@ dependencies = [
]
[[package]]
name = "similar"
version = "2.4.0"
name = "strong-xml"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21"
checksum = "1d19fb3a618e2f1039e32317c9f525e6d45c55af704ec7c429aa74412419bebf"
dependencies = [
"jetscii",
"lazy_static",
"memchr",
"strong-xml-derive",
"xmlparser",
]
[[package]]
name = "strong-xml-derive"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c781f499321613b112be5d9338189ef1ed19689a01edd23d923ea57ad5c7e1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.10.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.109"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
"unicode-xid",
]
[[package]]
name = "syn"
version = "2.0.48"
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termtree"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "test-case"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"test-case-core",
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.56"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "utf8parse"
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "wait-timeout"
@ -469,82 +346,29 @@ dependencies = [
]
[[package]]
name = "windows-sys"
version = "0.52.0"
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"windows-targets",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
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",
]
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xmlparser"
version = "0.13.6"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
checksum = "52613e655f6f11f63c0fe7d1c3b5ef69e44d96df9b65dab296b441ed0e1125f5"

View File

@ -5,4 +5,3 @@ members = [
"opml_api",
"opml_cli"
]
resolver = "2"

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2020-2024 Holllo <helllo@holllo.org>
Copyright (c) 2020-2021 Holllo <helllo@holllo.cc>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,30 +1,42 @@
[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
# Do a full check of everything.
[tasks.complete-check]
dependencies = [
"clean",
"format",
"check",
"clippy",
"test",
"code-coverage",
"docs",
"build",
"audit-flow",
"outdated-flow"
]
[tasks.fmt]
command = "cargo"
args = ["fmt", "${@}"]
[tasks.check]
command = "cargo"
args = ["check", "${@}"]
[tasks.clippy]
command = "cargo"
args = ["clippy", "${@}"]
[tasks.test]
command = "cargo"
args = ["test", "${@}"]
[tasks.doc]
command = "cargo"
args = ["doc", "${@}"]
[tasks.build]
command = "cargo"
args = ["build", "${@}"]
[tasks.complete-check]
dependencies = ["fmt", "check", "clippy", "test", "doc", "build"]
# Run cargo-tarpaulin and output the test coverage.
[tasks.code-coverage]
workspace = false
install_crate = "cargo-tarpaulin"
command = "cargo"
args = [
"tarpaulin",
"--exclude-files=target/*",
"--out=html",
"--output-dir=coverage",
"--output-dir=.coverage",
"--skip-clean",
"--target-dir=target/tarpaulin",
"--workspace"

View File

@ -1,31 +1,21 @@
# OPML 📄
# OPML
> **OPML library for Rust & standalone CLI.**
> An OPML parser for Rust and the command-line.
## API
For API documentation and examples see [docs.rs](https://docs.rs/opml).
For the API documentation, see [docs.rs](https://docs.rs/opml).
## CLI
### Cargo
To install the OPML command-line parser, run `cargo install opml_cli` or download a precompiled executable directly from [the Releases page](https://github.com/Holllo/opml/releases/latest).
With a working [Rust and Cargo](https://www.rust-lang.org/learn/get-started) installation, you can install the OPML CLI from [Crates.io](https://crates.io/crates/opml_cli).
```
cargo install opml_cli
```
### Binaries
Precompiled `x86_64-unknown-linux-gnu` binaries are available on the [Releases page](https://git.bauke.xyz/Holllo/opml/releases).
## Development
With [Nix flakes](https://nixos.wiki/wiki/Flakes) and [direnv](https://direnv.net/) installed and enabled, all the required dependencies are automatically loaded from [`shell.nix`](./shell.nix). Then [cargo-make](https://sagiegurari.github.io/cargo-make/) can be used to build, deploy and lint the code. The available tasks are all described in the [`Makefile.toml`](Makefile.toml) configuration.
Then run `opml --help` to see all the available options.
## License
Distributed under the [Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html) and [MIT](https://spdx.org/licenses/MIT.html) licenses, see [LICENSE-Apache](https://git.bauke.xyz/Holllo/opml/src/branch/main/LICENSE-Apache) and [LICENSE-MIT](https://git.bauke.xyz/Holllo/opml/src/branch/main/LICENSE-MIT) for more information.
Both the API and command-line parser are licensed under either of [Apache License, Version 2.0](https://github.com/Holllo/opml/blob/main/LICENSE-Apache) or [MIT license](https://github.com/Holllo/opml/blob/main/LICENSE-MIT) at your option.
The samples located in `opml_api/tests/spec_samples`) were [taken from the OPML 2.0 spec](http://dev.opml.org/spec2.html#examples) and are subject to [their own license](https://git.bauke.xyz/Holllo/opml/src/branch/main/opml_api/tests/spec_samples/LICENSE).
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in either crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
The samples [located in `opml_api/tests/spec_samples`](https://github.com/Holllo/opml/tree/main/opml_api/tests/spec_samples) were [taken from the OPML 2.0 spec](http://dev.opml.org/spec2.html#examples) and are subject to [their own license](https://github.com/Holllo/opml/blob/main/opml_api/tests/spec_samples/LICENSE).

View File

@ -1,128 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1705697961,
"narHash": "sha256-XepT3WS516evSFYkme3GrcI3+7uwXHqtHbip+t24J7E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e5d1c87f5813afde2dda384ac807c57a105721cc",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1681358109,
"narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1705803528,
"narHash": "sha256-nChqKQPRXxmGBEkHse39LjNpkNKk4U1xPQ4a4oYlUdw=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "bd7e8f4e122e11c934a576abc04327764f9bf19b",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,17 +0,0 @@
{
inputs = {
flake-utils.url = "github:numtide/flake-utils";
rust-overlay.url = "github:oxalica/rust-overlay";
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit system overlays; };
in
{
devShells.default = import ./shell.nix { inherit pkgs; };
}
);
}

View File

@ -2,22 +2,22 @@
[package]
name = "opml"
description = "OPML library for Rust."
repository = "https://git.bauke.xyz/Holllo/opml"
description = "An OPML parser for Rust."
authors = ["Holllo <helllo@holllo.cc>"]
version = "1.1.2"
license = "MIT OR Apache-2.0"
version = "1.1.6"
authors = ["Holllo <helllo@holllo.org>"]
edition = "2021"
repository = "https://github.com/Holllo/opml"
readme = "../README.md"
edition = "2018"
keywords = ["xml", "opml"]
[lib]
path = "source/lib.rs"
[dependencies]
hard-xml = "1.34.0"
thiserror = "1.0.56"
strong-xml = "0.6.3"
thiserror = "1.0.29"
[dependencies.serde]
version = "1.0.195"
version = "1.0.130"
features = ["derive"]

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2020-2024 Holllo <helllo@holllo.org>
Copyright (c) 2020-2021 Holllo <helllo@holllo.cc>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,31 +1,26 @@
use std::{error::Error, fs};
use opml::OPML;
const SAMPLE: &str = r#"<opml version="2.0">
<head>
<title>Rust Feeds</title>
</head>
<body>
<outline text="Rust Blog" xmlUrl="https://blog.rust-lang.org/feed.xml" />
<outline text="Inside Rust" xmlUrl="https://blog.rust-lang.org/inside-rust/feed.xml" />
</body>
</opml>"#;
fn main() -> Result<(), Box<dyn Error>> {
let xml = fs::read_to_string("examples/opml_samples/rust_feeds.opml")?;
/// Run this example using `cargo run --example rss`.
///
/// Output:
/// Rust Feeds
/// ──────────
/// Rust Blog https://blog.rust-lang.org/feed.xml
/// Inside Rust https://blog.rust-lang.org/inside-rust/feed.xml
fn main() {
let subscriptions = OPML::from_str(SAMPLE).unwrap();
let subscriptions = OPML::from_str(&xml)?;
let head = subscriptions.head.unwrap();
let title = head.title.unwrap();
if let Some(title) = subscriptions.head.and_then(|head| head.title) {
println!("{}", title);
println!("{}", "-".repeat(title.len()));
}
println!(" {}", title);
println!(" {}", "".repeat(title.len()));
for outline in subscriptions.body.outlines {
println!("{}\t{}", outline.text, outline.xml_url.unwrap());
println!(" {}\t{}", outline.text, outline.xml_url.unwrap());
}
Ok(())
}
// Output:
// Rust Feeds
// ──────────
// Rust Blog https://blog.rust-lang.org/feed.xml
// Inside Rust https://blog.rust-lang.org/inside-rust/feed.xml

View File

@ -29,11 +29,8 @@
//! To create an OPML document from scratch, use [`OPML::default()`] or the good
//! old `OPML { /* ... */ }` syntax.
#![forbid(unsafe_code)]
#![warn(missing_docs, clippy::missing_docs_in_private_items)]
use hard_xml::{XmlRead, XmlWrite};
use serde::{Deserialize, Serialize};
use strong_xml::{XmlRead, XmlWrite};
use thiserror::Error;
/// All possible errors.
@ -55,12 +52,12 @@ pub enum Error {
/// The input string is not valid XML.
#[error("Failed to process XML file")]
XmlError(#[from] hard_xml::XmlError),
XmlError(#[from] strong_xml::XmlError),
}
/// The top-level [`OPML`] element.
#[derive(
XmlWrite, XmlRead, PartialEq, Eq, Debug, Clone, Serialize, Deserialize,
XmlWrite, XmlRead, PartialEq, Debug, Clone, Serialize, Deserialize,
)]
#[xml(tag = "opml")]
pub struct OPML {
@ -109,7 +106,7 @@ impl OPML {
// SPEC: The version attribute is a version string, of the form, x.y, where
// x and y are both numeric strings.
let valid_versions = ["1.0", "1.1", "2.0"];
let valid_versions = vec!["1.0", "1.1", "2.0"];
if !valid_versions.contains(&opml.version.as_str()) {
return Err(Error::UnsupportedVersion(opml.version));
@ -129,8 +126,9 @@ impl OPML {
///
/// ```rust,no_run
/// use opml::OPML;
/// use std::fs::File;
///
/// let mut file = std::fs::File::open("file.opml").unwrap();
/// let mut file = File::open("file.opml").unwrap();
/// let document = OPML::from_reader(&mut file).unwrap();
/// ```
pub fn from_reader<R>(reader: &mut R) -> Result<Self, Error>
@ -202,9 +200,10 @@ impl OPML {
///
/// ```rust,no_run
/// use opml::OPML;
/// use std::fs::File;
///
/// let opml = OPML::default();
/// let mut file = std::fs::File::create("file.opml").unwrap();
/// let mut file = File::create("file.opml").unwrap();
/// let xml = opml.to_writer(&mut file).unwrap();
/// ```
pub fn to_writer<W>(&self, writer: &mut W) -> Result<(), Error>
@ -230,15 +229,7 @@ impl Default for OPML {
/// The [`Head`] child element of [`OPML`]. Contains the metadata of the OPML
/// document.
#[derive(
XmlWrite,
XmlRead,
PartialEq,
Eq,
Debug,
Clone,
Default,
Serialize,
Deserialize,
XmlWrite, XmlRead, PartialEq, Debug, Clone, Default, Serialize, Deserialize,
)]
#[xml(tag = "head")]
pub struct Head {
@ -303,15 +294,7 @@ pub struct Head {
/// The [`Body`] child element of [`OPML`]. Contains all the [`Outline`]
/// elements.
#[derive(
XmlWrite,
XmlRead,
PartialEq,
Eq,
Debug,
Clone,
Default,
Serialize,
Deserialize,
XmlWrite, XmlRead, PartialEq, Debug, Clone, Default, Serialize, Deserialize,
)]
#[xml(tag = "body")]
pub struct Body {
@ -322,15 +305,7 @@ pub struct Body {
/// The [`Outline`] element.
#[derive(
XmlWrite,
XmlRead,
PartialEq,
Eq,
Debug,
Clone,
Default,
Serialize,
Deserialize,
XmlWrite, XmlRead, PartialEq, Debug, Clone, Default, Serialize, Deserialize,
)]
#[xml(tag = "outline")]
pub struct Outline {
@ -415,7 +390,7 @@ impl Outline {
/// # Example
///
/// ```rust
/// use opml::Outline;
/// use opml::{Outline};
///
/// let mut group = Outline::default();
/// group.add_feed("Feed Name", "https://example.com/");

View File

@ -1,8 +0,0 @@
<opml version="2.0">
<head>
<docs />
</head>
<body>
<outline text="Outline Text"/>
</body>
</opml>

View File

@ -14,7 +14,7 @@ pub fn test_spec_samples() -> Result<(), Box<dyn Error>> {
];
for sample in samples {
let sample_content = fs::read_to_string(sample)?;
let sample_content = fs::read_to_string(&sample)?;
OPML::from_str(&sample_content)?;
}

View File

@ -20,26 +20,6 @@ fn test_minimum_valid_opml() {
);
}
#[test]
fn test_valid_empty_docs() {
assert_eq!(
OPML::from_str(&read("tests/samples/empty_docs.opml").unwrap()).unwrap(),
OPML {
version: "2.0".to_string(),
head: Some(Head {
docs: Some("".to_string()),
..Head::default()
}),
body: Body {
outlines: vec![Outline {
text: "Outline Text".to_string(),
..Outline::default()
}]
},
}
)
}
#[test]
fn test_valid_opml_with_everything() {
assert_eq!(

View File

@ -2,13 +2,13 @@
[package]
name = "opml_cli"
description = "OPML for the command-line."
repository = "https://git.bauke.xyz/Holllo/opml"
description = "An OPML parser for the command-line."
version = "1.1.2"
authors = ["Holllo <helllo@holllo.cc>"]
license = "MIT OR Apache-2.0"
version = "1.1.6"
authors = ["Holllo <helllo@holllo.org>"]
edition = "2021"
repository = "https://github.com/Holllo/opml"
readme = "../README.md"
edition = "2018"
keywords = ["xml", "opml"]
[[bin]]
@ -16,21 +16,15 @@ name = "opml"
path = "source/main.rs"
[dependencies]
serde_json = "1.0.111"
[dependencies.clap]
version = "4.4.18"
features = ["derive"]
clap = "2.33.3"
serde_json = "1.0.68"
[dependencies.opml]
path = "../opml_api"
version = "1.1.5"
[dependencies.serde]
version = "1.0.195"
version = "1.0.130"
features = ["derive"]
[dev-dependencies]
assert_cmd = "2.0.13"
insta = "1.34.0"
test-case = "3.3.1"
assert_cmd = "2.0.1"

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2020-2024 Holllo <helllo@holllo.org>
Copyright (c) 2020-2021 Holllo <helllo@holllo.cc>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,43 +1,70 @@
use std::{fs::read_to_string, path::PathBuf};
use std::fs::read_to_string;
use clap::Parser;
use clap::{
crate_authors, crate_description, crate_version, App, Arg, ArgGroup,
};
use opml::{Outline, OPML};
#[derive(Debug, Parser)]
#[clap(about, author, version)]
struct Args {
/// The OPML file to parse.
#[clap(short, long)]
file: PathBuf,
/// Output the OPML as JSON.
#[clap(long, group = "format", required = true)]
json: bool,
/// Output the OPML as pretty-printed JSON.
#[clap(long, group = "format", required = true)]
json_pretty: bool,
/// Only output the outline text and xmlUrl attributes when both are present
/// in the outline element.
#[clap(long, group = "format", required = true)]
rss: bool,
/// Print extra information while running.
#[clap(long)]
verbose: bool,
}
fn main() {
let args = Args::parse();
let cli = App::new("OPML CLI")
.about(crate_description!())
.author(crate_authors!())
.version(crate_version!())
.args(&[
// Format flags.
Arg::with_name("json")
.long("json")
.long_help("Output the OPML as JSON.")
.takes_value(false),
Arg::with_name("json pretty")
.long("json-pretty")
.long_help("Output the OPML as pretty-printed JSON.")
.takes_value(false),
Arg::with_name("rss")
.long("rss")
.long_help(
"Only output the outline text and xmlUrl attributes \
when both are present in the outline element.",
)
.takes_value(false),
// Boolean flags.
Arg::with_name("verbose")
.long("verbose")
.long_help("Print extra information while running.")
.takes_value(false),
// Options that are only allowed once.
Arg::with_name("file")
.long("file")
.long_help("The OPML file to parse.")
.required(true)
.short("f")
.takes_value(true),
])
.group(
ArgGroup::with_name("format")
.args(&["json", "json pretty", "rss"])
.required(true),
)
.get_matches();
// Extract format flags.
let json = cli.is_present("json");
let json_pretty = cli.is_present("json pretty");
let rss = cli.is_present("rss");
// Extract boolean flags.
let verbose = cli.is_present("verbose");
// Extract the various options.
let file = cli.value_of("file").unwrap();
// Read the file to string.
let xml = read_to_string(args.file).expect("Failed to read OPML file");
let xml = read_to_string(file).expect("Failed to read OPML file");
// Parse the OPML from the read file.
let opml = OPML::from_str(&xml).expect("Failed to parse OPML file");
if args.rss {
if rss {
// Get all the outlines from the OPML document.
let outlines = extract_all_outlines(&opml.body.outlines);
@ -46,19 +73,19 @@ fn main() {
if let Some(xml_url) = outline.xml_url {
println!("{}", outline.text);
println!("{}", xml_url);
} else if args.verbose {
} else if verbose {
println!(
"Skipping \"{}\" because it did not have an xmlUrl attribute.",
outline.text
);
}
}
} else if args.json {
} else if json {
println!(
"{}",
serde_json::to_string(&opml).expect("Failed to convert OPML to JSON")
);
} else if args.json_pretty {
} else if json_pretty {
println!(
"{}",
serde_json::to_string_pretty(&opml)

View File

@ -1,25 +0,0 @@
use {
assert_cmd::Command, insta::assert_display_snapshot, test_case::test_case,
};
const SAMPLE: &str = "tests/sample.opml";
#[test_case(&["--file", SAMPLE, "--json"], "json" ; "json")]
#[test_case(&["--file", SAMPLE, "--json-pretty"], "json_pretty" ; "json_pretty")]
#[test_case(&["--file", SAMPLE, "--rss"], "rss" ; "rss")]
fn test_valid(args: &[&str], name: &str) {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(args).assert().success().code(0);
let output = String::from_utf8(assert.get_output().stdout.clone()).unwrap();
assert_display_snapshot!(name, output);
}
#[test_case(&["--rss"], "missing_file" ; "missing_file")]
#[test_case(&["--file", SAMPLE], "missing_format" ; "missing_format")]
#[test_case(&["--rss", "--json"], "multiple_formats" ; "multiple_formats")]
fn test_invalid(args: &[&'static str], name: &str) {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(args).assert().failure().code(2);
let output = String::from_utf8(assert.get_output().stderr.clone()).unwrap();
assert_display_snapshot!(name, output);
}

27
opml_cli/tests/invalid.rs Normal file
View File

@ -0,0 +1,27 @@
use std::fs::read_to_string;
use assert_cmd::Command;
const SAMPLE: &str = "tests/samples/youtube.opml";
#[test]
fn test_missing_file() {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(&["--rss"]).assert();
assert
.failure()
.code(1)
.stderr(read_to_string("tests/snapshots/missing-file.txt").unwrap());
}
#[test]
fn test_missing_format() {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(&["--file", SAMPLE]).assert();
assert
.failure()
.code(1)
.stderr(read_to_string("tests/snapshots/missing-format.txt").unwrap());
}

View File

@ -0,0 +1,15 @@
<opml version="1.1">
<body>
<outline text="YouTube Subscriptions"
title="YouTube Subscriptions">
<outline text="A YouTube Channel"
title="A YouTube Channel"
type="rss"
xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz1" />
<outline text="Another YouTube Channel"
title="Another YouTube Channel"
type="rss"
xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz2" />
</outline>
</body>
</opml>

View File

@ -1,6 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
{"version":"2.0","head":{"title":"Rust Feeds","date_created":null,"date_modified":null,"owner_name":null,"owner_email":null,"owner_id":null,"docs":null,"expansion_state":null,"vert_scroll_state":null,"window_top":null,"window_left":null,"window_bottom":null,"window_right":null},"body":{"outlines":[{"text":"Rust Blog","type":null,"is_comment":null,"is_breakpoint":null,"created":null,"category":null,"outlines":[],"xml_url":"https://blog.rust-lang.org/feed.xml","description":null,"html_url":null,"language":null,"title":null,"version":null,"url":null},{"text":"Inside Rust","type":null,"is_comment":null,"is_breakpoint":null,"created":null,"category":null,"outlines":[],"xml_url":"https://blog.rust-lang.org/inside-rust/feed.xml","description":null,"html_url":null,"language":null,"title":null,"version":null,"url":null}]}}

View File

@ -1,59 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
{
"version": "2.0",
"head": {
"title": "Rust Feeds",
"date_created": null,
"date_modified": null,
"owner_name": null,
"owner_email": null,
"owner_id": null,
"docs": null,
"expansion_state": null,
"vert_scroll_state": null,
"window_top": null,
"window_left": null,
"window_bottom": null,
"window_right": null
},
"body": {
"outlines": [
{
"text": "Rust Blog",
"type": null,
"is_comment": null,
"is_breakpoint": null,
"created": null,
"category": null,
"outlines": [],
"xml_url": "https://blog.rust-lang.org/feed.xml",
"description": null,
"html_url": null,
"language": null,
"title": null,
"version": null,
"url": null
},
{
"text": "Inside Rust",
"type": null,
"is_comment": null,
"is_breakpoint": null,
"created": null,
"category": null,
"outlines": [],
"xml_url": "https://blog.rust-lang.org/inside-rust/feed.xml",
"description": null,
"html_url": null,
"language": null,
"title": null,
"version": null,
"url": null
}
]
}
}

View File

@ -1,13 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
error: the following required arguments were not provided:
--file <FILE>
--json
--json-pretty
Usage: opml --file <FILE> --json --json-pretty --rss
For more information, try '--help'.

View File

@ -1,13 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
error: the following required arguments were not provided:
--json
--json-pretty
--rss
Usage: opml --file <FILE> --json --json-pretty --rss
For more information, try '--help'.

View File

@ -1,10 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
error: the argument '--rss' cannot be used with '--json'
Usage: opml --file <FILE> --json --json-pretty --rss
For more information, try '--help'.

View File

@ -1,9 +0,0 @@
---
source: opml_cli/tests/cli.rs
expression: output
---
Rust Blog
https://blog.rust-lang.org/feed.xml
Inside Rust
https://blog.rust-lang.org/inside-rust/feed.xml

View File

@ -0,0 +1,57 @@
{
"version": "1.1",
"head": null,
"body": {
"outlines": [
{
"text": "YouTube Subscriptions",
"type": null,
"is_comment": null,
"is_breakpoint": null,
"created": null,
"category": null,
"outlines": [
{
"text": "A YouTube Channel",
"type": "rss",
"is_comment": null,
"is_breakpoint": null,
"created": null,
"category": null,
"outlines": [],
"xml_url": "https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz1",
"description": null,
"html_url": null,
"language": null,
"title": "A YouTube Channel",
"version": null,
"url": null
},
{
"text": "Another YouTube Channel",
"type": "rss",
"is_comment": null,
"is_breakpoint": null,
"created": null,
"category": null,
"outlines": [],
"xml_url": "https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz2",
"description": null,
"html_url": null,
"language": null,
"title": "Another YouTube Channel",
"version": null,
"url": null
}
],
"xml_url": null,
"description": null,
"html_url": null,
"language": null,
"title": "YouTube Subscriptions",
"version": null,
"url": null
}
]
}
}

View File

@ -0,0 +1 @@
{"version":"1.1","head":null,"body":{"outlines":[{"text":"YouTube Subscriptions","type":null,"is_comment":null,"is_breakpoint":null,"created":null,"category":null,"outlines":[{"text":"A YouTube Channel","type":"rss","is_comment":null,"is_breakpoint":null,"created":null,"category":null,"outlines":[],"xml_url":"https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz1","description":null,"html_url":null,"language":null,"title":"A YouTube Channel","version":null,"url":null},{"text":"Another YouTube Channel","type":"rss","is_comment":null,"is_breakpoint":null,"created":null,"category":null,"outlines":[],"xml_url":"https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz2","description":null,"html_url":null,"language":null,"title":"Another YouTube Channel","version":null,"url":null}],"xml_url":null,"description":null,"html_url":null,"language":null,"title":"YouTube Subscriptions","version":null,"url":null}]}}

View File

@ -0,0 +1,7 @@
error: The following required arguments were not provided:
--file <file>
USAGE:
opml --file <file> <--json|--json-pretty|--rss>
For more information try --help

View File

@ -0,0 +1,7 @@
error: The following required arguments were not provided:
<--json|--json-pretty|--rss>
USAGE:
opml [FLAGS] --file <file> <--json|--json-pretty|--rss>
For more information try --help

View File

@ -0,0 +1,4 @@
A YouTube Channel
https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz1
Another YouTube Channel
https://www.youtube.com/feeds/videos.xml?channel_id=abcdefghijklmnopqrstuvwxyz2

38
opml_cli/tests/valid.rs Normal file
View File

@ -0,0 +1,38 @@
use std::fs::read_to_string;
use assert_cmd::Command;
const SAMPLE: &str = "tests/samples/youtube.opml";
#[test]
fn test_valid_rss() {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(&["--file", SAMPLE, "--rss"]).assert();
assert
.success()
.code(0)
.stdout(read_to_string("tests/snapshots/rss.txt").unwrap());
}
#[test]
fn test_valid_json() {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(&["--file", SAMPLE, "--json"]).assert();
assert
.success()
.code(0)
.stdout(read_to_string("tests/snapshots/json.json").unwrap());
}
#[test]
fn test_valid_json_pretty() {
let mut cmd = Command::cargo_bin("opml").unwrap();
let assert = cmd.args(&["--file", SAMPLE, "--json-pretty"]).assert();
assert
.success()
.code(0)
.stdout(read_to_string("tests/snapshots/json-pretty.json").unwrap());
}

View File

@ -1,3 +0,0 @@
[toolchain]
channel = "stable"
components = ["cargo", "clippy", "rustfmt", "rust-src"]

View File

@ -1,18 +0,0 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
let
rustup-toolchain = rust-bin.fromRustupToolchainFile ./rustup-toolchain.toml;
in
mkShell rec {
packages = [
cargo-audit
cargo-edit
cargo-insta
cargo-make
cargo-outdated
cargo-tarpaulin
rustup-toolchain
];
}