diff --git a/Cargo.lock b/Cargo.lock index 4e8bc9d..514e5f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -28,84 +28,76 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" [[package]] name = "cfg-if" @@ -115,32 +107,32 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] name = "clap" -version = "4.4.10" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -150,21 +142,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comfy-table" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", "strum", @@ -174,9 +166,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossterm" @@ -184,7 +176,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.1", + "bitflags", "crossterm_winapi", "libc", "parking_lot", @@ -222,16 +214,26 @@ dependencies = [ ] [[package]] -name = "env_logger" -version = "0.10.1" +name = "env_filter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ - "humantime", - "is-terminal", "log", "regex", - "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", ] [[package]] @@ -240,33 +242,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "humantime" @@ -276,9 +262,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -299,43 +285,40 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] [[package]] -name = "is-terminal" -version = "0.4.9" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonfilter" -version = "0.1.0" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e46f0c1b9ad5f49d868ccd9210aa5739361d110902169c441cacc1ec2f090b" dependencies = [ "regex", "serde", @@ -344,21 +327,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "linux-raw-sys" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -366,13 +343,13 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "mdq" -version = "0.1.0" +version = "0.3.1" dependencies = [ "chrono", "clap", @@ -390,30 +367,30 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -421,49 +398,49 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -473,9 +450,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -484,34 +461,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rustix" -version = "0.38.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -530,18 +494,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -550,9 +514,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -561,9 +525,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -574,27 +538,27 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", @@ -605,24 +569,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - [[package]] name = "unicode-ident" version = "1.0.12" @@ -631,27 +586,27 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe-libyaml" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -659,9 +614,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -669,9 +624,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -684,9 +639,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -694,9 +649,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -707,9 +662,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "winapi" @@ -729,11 +684,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -744,20 +699,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -766,119 +712,69 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_gnu" -version = "0.52.0" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index c190f89..8520e5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mdq" -version = "0.1.0" +version = "0.3.1" edition = "2021" [profile.release] @@ -17,8 +17,8 @@ serde_yaml = "0.9.25" walkdir = "2.4.0" serde_json = "1.0.107" comfy-table = "7.1.0" -env_logger = "0.10.0" +env_logger = "0.11" log = "0.4.20" chrono = "0.4.31" csv = "1.3.0" -jsonfilter = { path = "../../Gitea/jsonfilter" } \ No newline at end of file +jsonfilter = "0.2" diff --git a/README.md b/README.md index ef5bcfe..4917b84 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,35 @@ # Markdown Query MDQ is a command line tool to query markdown documents which have yaml frontmatter. + +## Usage +Usage: `mdq [OPTIONS] ` + +### Options +| Option | Description | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `-j, --json` | Output result as JSON | +| `-l, --limit ` | Limit number of results returned [default: 0] | +| `--offset ` | Offset results by a factor. Useful when used with `--limit` [default: 0] | +| `-f, --filter ` | Filter to apply to the documents. See filter section below. | +| `-c, --column ` | Specify output columns. You can rename the text displayed in the header using the `:` character like this: `VariableName:OutputName` [default: file.title:Title] | +| `-s, --sortby ` | Sort results based on specified key | +| `-g, --groupby ` | Group results based on specified key | +| `-r, --reverse` | Reverse the results | +| `--noheader` | Dont print header in CSV mode. Useful for scripting | +| `-t, --inline-tags` | Search for inline `#tags` and include them in frontmatter | + +## Filters +You can query your document using filters. MDQ uses [jsonfilter](https://git.hydrar.de/jmarya/jsonfilter), so you can query similiar to the `find()` function of MongoDB. + +Examples: +```shell +# Select documents with version 1.0 +mdq -c file.title:Title -f '{"version": "1.0"}' ./docs + +# Select documents which are high priority and assigned to me +mdq -c file.title:Title -f '{"priority": "high"}' -f '{"assigned": "me"}' ./docs + +# Select documents which are assigned to names starting with A or B +mdq -c file.title:Title -f '{"$or": [{"assigned": {"$regex": "^A"}}, {"assigned": {"$regex": "^B}}]}' ./docs +``` \ No newline at end of file diff --git a/src/args.rs b/src/args.rs index f93717e..481f8c1 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,9 +1,120 @@ use clap::{arg, command, ArgMatches}; -pub fn get_args() -> ArgMatches { +use crate::quit_err; + +pub struct Args { + pub root_dir: String, + pub output_json: bool, + pub no_header: bool, + pub limit: usize, + pub offset: usize, + pub use_inline_tags: bool, + pub sort_by: Option, + pub group_by: Option, + pub reversed: bool, + pub columns: Vec, + pub headers: Vec, + pub filters: serde_json::Value, +} + +pub fn get_args() -> Args { + let args = get_args_match(); + + let root_dir = args.get_one::("dir").unwrap(); + + let output_json = args.get_flag("json"); + + let no_header = args.get_flag("noheader"); + + let limit: usize = args + .get_one::("limit") + .unwrap() + .parse() + .unwrap_or_else(|e| quit_err(e, "Limit is not a number")); + + let offset: usize = args + .get_one::("offset") + .unwrap() + .parse() + .unwrap_or_else(|e| quit_err(e, "Offset is not a number")); + + let use_inline_tags: bool = args.get_flag("inline-tags"); + + let sort_by = args + .get_one::("sortby") + .map(std::borrow::ToOwned::to_owned); + + let group_by = args + .get_one::("groupby") + .map(std::borrow::ToOwned::to_owned); + + let reversed = args.get_flag("reverse"); + + let columns: Vec<_> = args + .get_many::("column") + .unwrap() + .cloned() + .collect(); + log::debug!("columns: {columns:?}"); + + let (columns, headers): (Vec<_>, Vec<_>) = columns + .into_iter() + .map(|x| { + let (column, header_rename) = x.split_once(':').unwrap_or((&x, &x)); + + (column.to_owned(), header_rename.to_owned()) + }) + .unzip(); + + if columns != headers { + log::debug!("renamed headers: {headers:?}"); + } + + let filters = args + .get_many::("filter") + .map_or_else(std::vec::Vec::new, std::iter::Iterator::collect); + + log::debug!("raw filters: {filters:?}"); + + let filters = if filters.len() == 1 { + let filter = filters.first().unwrap(); + serde_json::from_str(filter) + .unwrap_or_else(|e| quit_err(e, &format!("filter '{filter}' could not be parsed"))) + } else { + let filters: Vec<_> = filters + .iter() + .map(|x| { + serde_json::from_str::(x) + .unwrap_or_else(|e| quit_err(e, &format!("filter '{x}' could not be parsed"))) + }) + .collect(); + serde_json::json!({ + "$and": filters + }) + }; + + log::debug!("parsed filters: {filters:?}"); + + Args { + root_dir: root_dir.to_string(), + output_json, + no_header, + limit, + offset, + use_inline_tags, + sort_by, + group_by, + reversed, + columns, + headers, + filters, + } +} + +fn get_args_match() -> ArgMatches { command!() .about("Query markdown files") - .arg(arg!([dir] "Directory to scan").required(true)) + .arg(arg!([dir] "Directory to scan").required(false).default_value(".")) .arg(arg!(-j --json "Output result as JSON").required(false)) .arg( arg!(-l --limit "Limit number of results returned") @@ -27,6 +138,6 @@ pub fn get_args() -> ArgMatches { .arg(arg!(-g --groupby "Group results based on specified key").required(false)) .arg(arg!(-r --reverse "Reverse the results").required(false)) .arg(arg!(--noheader "Dont print header in CSV mode. Useful for scripting").required(false)) - .arg(arg!(--ignoretags "Don't add inline #tags to tags frontmatter").required(false)) + .arg(clap::Arg::new("inline-tags").short('t').long("inline-tags").help("Include inline #tags in tags frontmatter").required(false).num_args(0)) .get_matches() } diff --git a/src/lib.rs b/src/lib.rs index c9712e5..762ba75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,42 @@ use std::collections::{HashMap, HashSet}; -/// get frontmatter from markdown document +/// Extracts the front matter from a Markdown document. +/// +/// This function scans the input `markdown` string for a YAML front matter block, +/// which is expected to be enclosed within triple dashes (`---`). The front matter +/// is typically used in Markdown files to specify metadata such as title, date, tags, +/// etc. +/// +/// # Arguments +/// +/// * `markdown` - A string slice that holds the content of the Markdown document. +/// +/// # Returns +/// +/// An `Option` which contains the front matter if found, or `None` if the +/// front matter block is not present in the provided `markdown`. +/// +/// # Examples +/// +/// ``` +/// use mdq::get_frontmatter; +/// +/// let markdown = r#"--- +/// title: "Sample Document" +/// date: "2024-06-06" +/// tags: ["rust", "markdown"] +/// --- +/// +/// # Introduction +/// +/// This is the content of the Markdown document. +/// "#; +/// +/// let frontmatter = get_frontmatter(markdown); +/// assert_eq!(frontmatter, Some(String::from(r#"title: "Sample Document" +/// date: "2024-06-06" +/// tags: ["rust", "markdown"]"#))); +/// ``` #[must_use] pub fn get_frontmatter(markdown: &str) -> Option { let frontmatter_regex = regex::Regex::new(r"(?s)^---\s*\n(.*?)\n---").unwrap(); @@ -12,29 +48,38 @@ pub fn get_frontmatter(markdown: &str) -> Option { }) } -trait ToYaml { - fn to_yaml(&self) -> serde_yaml::Value; -} - -impl ToYaml for serde_json::Value { - fn to_yaml(&self) -> serde_yaml::Value { - let str = serde_yaml::to_string(self).unwrap(); - serde_yaml::from_str(&str).unwrap() - } -} - -trait ToJson { - fn to_json(&self) -> serde_json::Value; -} - -impl ToJson for serde_yaml::Value { - fn to_json(&self) -> serde_json::Value { - let str = serde_json::to_string(self).unwrap(); - serde_json::from_str(&str).unwrap() - } -} - -/// get inline #tags from markdown file +/// Extracts inline `#tags` from a Markdown document. +/// +/// This function scans the input `markdown` string for inline tags prefixed with a +/// hash (`#`) symbol. Inline tags are commonly used in Markdown documents to +/// categorize content or add metadata within the text body. +/// +/// # Arguments +/// +/// * `markdown` - A string slice that holds the content of the Markdown document. +/// +/// # Returns +/// +/// A `Vec` containing all the tags found in the Markdown document. Each tag +/// is represented without the leading `#` symbol. If no tags are found, an empty +/// vector is returned. +/// +/// # Examples +/// +/// ``` +/// use mdq::get_inline_tags; +/// +/// let markdown = r#" +/// # Introduction +/// This document is a sample for #Rust and #Markdown. +/// +/// # Content +/// Here we have some #examples and #code snippets. +/// "#; +/// +/// let tags = get_inline_tags(markdown); +/// assert_eq!(tags, vec!["Rust", "Markdown", "examples", "code"]); +/// ``` #[must_use] pub fn get_inline_tags(markdown: &str) -> Vec { let tag_regex = regex::Regex::new(r"#(\w+)").unwrap(); @@ -66,9 +111,16 @@ fn system_time_to_date_time(t: std::time::SystemTime) -> chrono::DateTime>; +/// Markdown Document Index impl Index { - /// Create a markdown document index over `dir` - pub fn new(dir: &str, ignore_inline_tags: bool) -> Self { + /// Creates a new markdown document index for a given directory. + /// + /// This method scans the specified directory recursively for Markdown files + /// (`.md` extension) and constructs an index of `Document` instances. Optionally, + /// it can also extract inline tags from the document content and add them to the + /// front matter metadata. + /// + /// # Arguments + /// + /// * `dir` - A string slice representing the directory to scan for Markdown files. + /// * `inline_tags` - A boolean indicating whether to extract inline tags from the + /// document content and add them to the front matter. + /// + /// # Returns + /// + /// An `Index` instance containing the indexed documents. + /// + /// # Panics + /// + /// This method will panic if it fails to read a file or if the front matter + /// cannot be parsed as valid YAML. + pub fn new(dir: &str, inline_tags: bool) -> Self { let mut i = Self { documents: vec![] }; for e in walkdir::WalkDir::new(dir) @@ -94,6 +167,7 @@ impl Index { if e.path().extension().is_none() { continue; } + if e.path().extension().unwrap().to_str().unwrap() == "md" { let path = e.path().to_str().unwrap().to_owned(); let content = std::fs::read_to_string(&path).unwrap(); @@ -101,12 +175,12 @@ impl Index { let mut frontmatter: serde_yaml::Value = serde_yaml::from_str(&frontmatter).unwrap(); - if !ignore_inline_tags { + if inline_tags { let mut tags = frontmatter .as_mapping() .unwrap() .get("tags") - .map(|x| x.as_sequence().unwrap().clone()) + .map(|x| x.as_sequence().unwrap_or(&Vec::new()).clone()) .unwrap_or_default(); let inline_tags = get_inline_tags(&content); @@ -123,6 +197,7 @@ impl Index { .insert("tags".into(), unique_tags.into_iter().collect()); } + log::trace!("Adding {path} to Index"); let doc = Document { path, frontmatter }; i.documents.push(doc); } @@ -131,7 +206,21 @@ impl Index { i } - /// Build a table with specified columns from index within specified scope + /// Builds a table with specified columns from the index within a specified scope. + /// + /// This method allows you to apply a limit, offset, and sorting to the documents + /// in the index, returning a new `Index` with the resulting documents. + /// + /// # Arguments + /// + /// * `limit` - The maximum number of documents to include in the resulting index. + /// * `offset` - The number of documents to skip before starting to include documents in the resulting index. + /// * `sort` - An optional string specifying the key to sort the documents by. + /// * `reverse` - A boolean indicating whether to reverse the sort order. + /// + /// # Returns + /// + /// A new `Index` containing the documents within the specified scope. #[must_use] pub fn apply(&self, limit: usize, offset: usize, sort: Option, reverse: bool) -> Self { let mut scope = self.documents.clone(); @@ -149,34 +238,64 @@ impl Index { scope.reverse(); } - let scope: Vec<_> = scope.into_iter().skip(offset).collect(); + let scope = scope.into_iter().skip(offset); - let scope = if limit == 0 { - scope + if limit == 0 { + Self { + documents: scope.collect(), + } } else { - scope.into_iter().take(limit).collect() - }; - - Self { documents: scope } + Self { + documents: scope.take(limit).collect(), + } + } } + /// Groups the documents in the index by a specified key. + /// + /// This method groups the documents based on the value of a specified key in the + /// front matter, returning a `HashMap` where the keys are the unique values of the + /// specified key, and the values are new `Index` instances containing the grouped documents. + /// + /// # Arguments + /// + /// * `key` - A string slice representing the key to group the documents by. + /// + /// # Returns + /// + /// A `HashMap` where each key is a unique value of the specified key in the front matter, + /// and each value is an `Index` containing the documents that share that key. #[must_use] pub fn group_by(&self, key: &str) -> HashMap { let mut grouped_items: HashMap> = HashMap::new(); for doc in self.documents.clone() { grouped_items - .entry(stringify(&doc.get_key(key).to_yaml())) + .entry(stringify(&serde_yaml::to_value(doc.get_key(key)).unwrap())) .or_default() .push(doc); } grouped_items .into_iter() - .map(|(key, item)| (key, Index { documents: item })) + .map(|(key, item)| (key, Self { documents: item })) .collect() } + /// Creates a table data representation of the documents with specified columns. + /// + /// This method constructs a table where each row represents a document and each + /// column corresponds to a specified key in the front matter. The resulting table + /// can be used for display or further processing. + /// + /// # Arguments + /// + /// * `col` - A slice of strings representing the keys to include as columns in the table. + /// + /// # Returns + /// + /// A `Table` (vector of vectors of strings) where each inner vector represents a row of + /// the table, and each string represents a cell in the row. #[must_use] pub fn create_table_data(&self, col: &[String]) -> Table { let mut rows = vec![]; @@ -184,7 +303,7 @@ impl Index { for doc in &self.documents { let mut rcol = vec![]; for c in col { - rcol.push(stringify(&doc.get_key(c).to_yaml())); + rcol.push(stringify(&serde_yaml::to_value(doc.get_key(c)).unwrap())); } rows.push(rcol); } @@ -192,7 +311,18 @@ impl Index { rows } - /// Apply filters to the documents of the index returning a new filtered index + /// Applies filters to the documents of the index, returning a new filtered index. + /// + /// This method filters the documents based on the specified [JSON filter](https://crates.io/crates/jsonfilter) criteria, + /// returning a new `Index` instance containing only the documents that match the filter. + /// + /// # Arguments + /// + /// * `filters` - A `serde_json::Value` representing the filter criteria. + /// + /// # Returns + /// + /// A new `Index` containing the filtered documents. #[must_use] pub fn filter_documents(&self, filters: &serde_json::Value) -> Self { let docs: Vec<_> = self @@ -218,8 +348,22 @@ impl Index { } impl Document { - /// Get a key from document. - /// This will return internal properties first, then it will search the document frontmatter for the key and return it. If nothing was found an empty string is returned. + /// Retrieves the value of a specified key from the document. + /// + /// This method first checks internal properties such as file metadata and path information. + /// If the key does not match any internal properties, it searches the document's front matter. + /// If the key is not found, it returns a JSON null value. + /// + /// # Arguments + /// + /// * `key` - A string slice representing the key to retrieve the value for. The key can be + /// either an internal property or a front matter field. Nested front matter fields can be + /// accessed using dot notation. + /// + /// # Returns + /// + /// A `serde_json::Value` representing the value associated with the specified key. If the key + /// is not found, it returns `serde_json::Value::Null`. fn get_key(&self, key: &str) -> serde_json::Value { match key { "file.title" => { @@ -319,19 +463,28 @@ impl Document { data = data_opt.unwrap(); } - data.to_json() + serde_json::to_value(data).unwrap() } else { - self.frontmatter - .as_mapping() - .unwrap() - .get(key) - .map_or_else(|| serde_json::Value::Null, ToJson::to_json) + self.frontmatter.as_mapping().unwrap().get(key).map_or_else( + || serde_json::Value::Null, + |x| serde_json::to_value(x).unwrap(), + ) } } + /// Retrieves the complete front matter of the document, including additional file metadata. + /// + /// This method returns the full front matter of the document as a JSON object, with added + /// metadata fields such as file name, title, parent directory, folder, extension, size, + /// creation time, modification time, and path. + /// + /// # Returns + /// + /// A `serde_json::Value` representing the full front matter of the document, enriched with + /// additional file metadata. #[must_use] pub fn get_full_frontmatter(&self) -> serde_json::Value { - let mut frontmatter = self.frontmatter.to_json(); + let mut frontmatter = serde_json::to_value(&self.frontmatter).unwrap(); let frontmatter_obj = frontmatter.as_object_mut().unwrap(); frontmatter_obj.insert("file.title".into(), self.get_key("file.title")); frontmatter_obj.insert("file.name".into(), self.get_key("file.name")); diff --git a/src/main.rs b/src/main.rs index f520ba4..b45ec8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,98 +4,48 @@ use mdq::Index; mod args; +pub fn quit_err(e: impl std::error::Error, msg: &str) -> ! { + eprintln!("Error: {msg}. {e}"); + std::process::exit(1); +} + fn main() { env_logger::init(); let args = args::get_args(); - let root_dir = args.get_one::("dir").unwrap(); - - let output_json = args.get_flag("json"); - - let no_header = args.get_flag("noheader"); - - let limit: usize = args.get_one::("limit").unwrap().parse().unwrap(); - - let offset: usize = args.get_one::("offset").unwrap().parse().unwrap(); - - let ignoretags: bool = args.get_flag("ignoretags"); - - let sort_by = args - .get_one::("sortby") - .map(std::borrow::ToOwned::to_owned); - - let group_by = args - .get_one::("groupby") - .map(std::borrow::ToOwned::to_owned); - - let reversed = args.get_flag("reverse"); - - let columns: Vec<_> = args - .get_many::("column") - .unwrap() - .cloned() - .collect(); - log::debug!("columns: {columns:?}"); - - let (columns, headers): (Vec<_>, Vec<_>) = columns - .into_iter() - .map(|x| { - let (column, header_rename) = x.split_once(':').unwrap_or((&x, &x)); - - (column.to_owned(), header_rename.to_owned()) - }) - .unzip(); - - if columns != headers { - log::debug!("renamed headers: {headers:?}"); - } - - let filters = args - .get_many::("filter") - .map_or_else(std::vec::Vec::new, std::iter::Iterator::collect); - - log::debug!("raw filters: {filters:?}"); - - let filters = if filters.len() == 1 { - serde_json::from_str(filters.first().unwrap()).unwrap() + let root_dir = if args.root_dir == "." { + let cwd = std::env::current_dir().unwrap(); + cwd.to_str().unwrap().to_string() } else { - let filters: Vec<_> = filters - .iter() - .map(|x| serde_json::from_str::(x).unwrap()) - .collect(); - serde_json::json!({ - "$and": filters - }) + args.root_dir }; - log::debug!("parsed filters: {filters:?}"); - - let mut i = Index::new(root_dir, ignoretags); - if !filters.is_null() { - i = i.filter_documents(&filters); + let mut i = Index::new(&root_dir, args.use_inline_tags); + if !args.filters.is_null() { + i = i.filter_documents(&args.filters); } - i = i.apply(limit, offset, sort_by, reversed); + i = i.apply(args.limit, args.offset, args.sort_by, args.reversed); - if group_by.is_some() { - let grouped = i.group_by(&group_by.clone().unwrap()); + if args.group_by.is_some() { + let grouped = i.group_by(&args.group_by.clone().unwrap()); let grouped: HashMap<_, _> = grouped .into_iter() - .map(|(key, val)| (key, val.create_table_data(&columns))) + .map(|(key, val)| (key, val.create_table_data(&args.columns))) .collect(); - if output_json { + if args.output_json { let mut data = serde_json::json!( { - "columns": columns, - "groupby": group_by.unwrap(), + "columns": args.columns, + "groupby": args.group_by.unwrap(), "results": grouped } ); - if columns != headers { + if args.columns != args.headers { data.as_object_mut() .unwrap() - .insert("headers".into(), headers.into()); + .insert("headers".into(), args.headers.into()); } println!("{}", serde_json::to_string(&data).unwrap()); return; @@ -111,13 +61,20 @@ fn main() { }); for group in grouped_keys { println!("# {group}"); - print_result(grouped.get(group).unwrap().clone(), &headers); + print_result(grouped.get(group).unwrap().clone(), &args.headers); } } else { let mut first = true; for (_, val) in grouped { if first { - print_csv(val, if no_header { None } else { Some(&headers) }); + print_csv( + val, + if args.no_header { + None + } else { + Some(&args.headers) + }, + ); first = false; continue; } @@ -127,27 +84,34 @@ fn main() { return; } - let data = i.create_table_data(&columns); + let data = i.create_table_data(&args.columns); - if output_json { + if args.output_json { let mut data = serde_json::json!( { - "columns": columns, + "columns": args.columns, "results": data } ); - if columns != headers { + if args.columns != args.headers { data.as_object_mut() .unwrap() - .insert("headers".into(), headers.into()); + .insert("headers".into(), args.headers.into()); } println!("{}", serde_json::to_string(&data).unwrap()); return; } if std::io::stdout().is_terminal() { - print_result(data, &headers); + print_result(data, &args.headers); } else { - print_csv(data, if no_header { None } else { Some(&headers) }); + print_csv( + data, + if args.no_header { + None + } else { + Some(&args.headers) + }, + ); } }