Compare commits

...

10 commits

Author SHA1 Message Date
823319dd02
update 2024-07-15 11:48:17 +02:00
e0e9c3e4a0
update 2024-06-06 19:19:46 +02:00
b35c8aff9a
merge 2024-03-08 21:16:03 +01:00
4bb15f3a1d
dont search inline tags by default 2024-03-08 13:42:19 +01:00
7be5c026b0
fix panic with '.' as dir arg 2024-03-08 13:35:03 +01:00
b0cd5b2890
Merge branch 'master' of https://git.hydrar.de/mdtools/mdq 2024-02-14 12:08:26 +01:00
38ff163eff
docs 2024-02-13 09:24:22 +01:00
8ea359d6df
refactor 2024-02-13 09:12:16 +01:00
09d8249cec
update 2024-02-09 18:12:17 +01:00
8d8d90efd1
update 2024-02-09 13:50:58 +01:00
6 changed files with 573 additions and 417 deletions

460
Cargo.lock generated
View file

@ -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"

View file

@ -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" }
jsonfilter = "0.2"

View file

@ -1,3 +1,35 @@
# Markdown Query
MDQ is a command line tool to query markdown documents which have yaml frontmatter.
## Usage
Usage: `mdq [OPTIONS] <dir>`
### Options
| Option | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-j, --json` | Output result as JSON |
| `-l, --limit <LIMIT>` | Limit number of results returned [default: 0] |
| `--offset <OFFSET>` | Offset results by a factor. Useful when used with `--limit` [default: 0] |
| `-f, --filter <FILTER>` | Filter to apply to the documents. See filter section below. |
| `-c, --column <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 <KEY>` | Sort results based on specified key |
| `-g, --groupby <KEY>` | 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
```

View file

@ -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<String>,
pub group_by: Option<String>,
pub reversed: bool,
pub columns: Vec<String>,
pub headers: Vec<String>,
pub filters: serde_json::Value,
}
pub fn get_args() -> Args {
let args = get_args_match();
let root_dir = args.get_one::<String>("dir").unwrap();
let output_json = args.get_flag("json");
let no_header = args.get_flag("noheader");
let limit: usize = args
.get_one::<String>("limit")
.unwrap()
.parse()
.unwrap_or_else(|e| quit_err(e, "Limit is not a number"));
let offset: usize = args
.get_one::<String>("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::<String>("sortby")
.map(std::borrow::ToOwned::to_owned);
let group_by = args
.get_one::<String>("groupby")
.map(std::borrow::ToOwned::to_owned);
let reversed = args.get_flag("reverse");
let columns: Vec<_> = args
.get_many::<String>("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::<String>("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::<serde_json::Value>(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> "Limit number of results returned")
@ -27,6 +138,6 @@ pub fn get_args() -> ArgMatches {
.arg(arg!(-g --groupby <KEY> "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()
}

View file

@ -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<String>` 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<String> {
let frontmatter_regex = regex::Regex::new(r"(?s)^---\s*\n(.*?)\n---").unwrap();
@ -12,29 +48,38 @@ pub fn get_frontmatter(markdown: &str) -> Option<String> {
})
}
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<String>` 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<String> {
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<chrono
chrono::TimeZone::timestamp_opt(&chrono::Utc, sec, nsec).unwrap()
}
/// Represents a Markdown document with a file path and front matter metadata.
///
/// The `Document` struct encapsulates the essential properties of a Markdown document,
/// including its file path and the parsed front matter. The front matter is typically
/// represented in YAML format and stored as a `serde_yaml::Value`.
#[derive(Debug, Clone)]
pub struct Document {
/// The file path of the Markdown document.
pub path: String,
/// The parsed front matter metadata in YAML format.
pub frontmatter: serde_yaml::Value,
}
@ -79,9 +131,30 @@ pub struct Index {
type Table = Vec<Vec<String>>;
/// 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<String>, 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<String, Self> {
let mut grouped_items: HashMap<String, Vec<Document>> = 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"));

View file

@ -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::<String>("dir").unwrap();
let output_json = args.get_flag("json");
let no_header = args.get_flag("noheader");
let limit: usize = args.get_one::<String>("limit").unwrap().parse().unwrap();
let offset: usize = args.get_one::<String>("offset").unwrap().parse().unwrap();
let ignoretags: bool = args.get_flag("ignoretags");
let sort_by = args
.get_one::<String>("sortby")
.map(std::borrow::ToOwned::to_owned);
let group_by = args
.get_one::<String>("groupby")
.map(std::borrow::ToOwned::to_owned);
let reversed = args.get_flag("reverse");
let columns: Vec<_> = args
.get_many::<String>("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::<String>("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::<serde_json::Value>(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)
},
);
}
}