Use config file instead of params.
This commit is contained in:
commit
5bcc37f32c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
439
Cargo.lock
generated
Normal file
439
Cargo.lock
generated
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is-terminal",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"bitflags",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.147"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown"
|
||||||
|
version = "1.0.0-alpha.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1bd98c3b68451b0390a289c58c856adb4e2b50cc40507ce2a105d5b00eafc80"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-id",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-docs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"markdown",
|
||||||
|
"toml",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.37.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.164"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.19.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-id"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "md-docs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# misti = { path = "../compiler"}
|
||||||
|
clap = { version = "4.2.0", features = ["derive"] }
|
||||||
|
markdown = "1.0.0-alpha.7"
|
||||||
|
toml = "0.7.3"
|
||||||
|
yaml-rust = "0.4.5"
|
||||||
|
|
16
md-docs.md
Normal file
16
md-docs.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# MD-DOCS
|
||||||
|
|
||||||
|
All configuration is done via the `md-docs.config.yaml` file.
|
||||||
|
|
||||||
|
## md-docs.config.yaml
|
||||||
|
|
||||||
|
Values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{
|
||||||
|
String input # Path to the input folder
|
||||||
|
String output # Path to the output folder
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
88
src/generator/code.rs
Normal file
88
src/generator/code.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use markdown::mdast::Code;
|
||||||
|
|
||||||
|
// use super::highlighter::highlight;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Code {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let code = &self.value; // highlight(&self.value);
|
||||||
|
|
||||||
|
if let Some(lang) = &self.lang {
|
||||||
|
if lang == "nav" {
|
||||||
|
return generate_nav_html(&self.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("<pre class=\"language-{}\">{}</pre>", lang, code)
|
||||||
|
} else {
|
||||||
|
format!("<pre class=\"language-none\">{}</pre>", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
panic!("Code cannot return its raw text")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_nav_html(data: &String) -> String {
|
||||||
|
use toml::{Table, Value};
|
||||||
|
|
||||||
|
let table = data.parse::<Table>().unwrap();
|
||||||
|
|
||||||
|
let previous = match table.get("previous") {
|
||||||
|
Some(Value::Table(t)) => match (t.get("href"), t.get("title")) {
|
||||||
|
(Some(Value::String(href)), Some(Value::String(title))) => {
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
<a
|
||||||
|
class=\"inline-block px-4 py-2 transition-colors
|
||||||
|
border border-border-color hover:border-c2-primary
|
||||||
|
hover:text-c2-primary
|
||||||
|
rounded-md\"
|
||||||
|
href=\"{}\"
|
||||||
|
>
|
||||||
|
<span class=\"text-xs\">Previous</span>
|
||||||
|
<br>
|
||||||
|
<span class=\"font-bold\">{}</span>
|
||||||
|
</a>
|
||||||
|
",
|
||||||
|
href, title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => panic!("TOML error: `previous` doesn't have a href and title string."),
|
||||||
|
},
|
||||||
|
Some(_) => panic!("TOML error: `previous` is not a table."),
|
||||||
|
_ => String::from("<div></div>"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let next = match table.get("next") {
|
||||||
|
Some(Value::Table(t)) => match (t.get("href"), t.get("title")) {
|
||||||
|
(Some(Value::String(href)), Some(Value::String(title))) => {
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
<a
|
||||||
|
class=\"inline-block px-4 py-2 transition-colors
|
||||||
|
border border-border-color hover:border-c2-primary
|
||||||
|
hover:text-c2-primary
|
||||||
|
rounded-md text-right\"
|
||||||
|
href=\"{}\"
|
||||||
|
>
|
||||||
|
<span class=\"text-xs\">Next</span>
|
||||||
|
<br>
|
||||||
|
<span class=\"font-bold\">{}</span>
|
||||||
|
</a>
|
||||||
|
",
|
||||||
|
href, title
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => panic!("TOML error: `next` doesn't have a href and title string."),
|
||||||
|
},
|
||||||
|
Some(_) => panic!("TOML error: `next` is not a table."),
|
||||||
|
_ => String::from("<div></div>"),
|
||||||
|
};
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"<div class=\"grid grid-cols-2 gap-4 my-16\">{}{}</div>",
|
||||||
|
previous, next
|
||||||
|
)
|
||||||
|
}
|
17
src/generator/emphasis.rs
Normal file
17
src/generator/emphasis.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use markdown::mdast::Emphasis;
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Emphasis {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let html = utils::collect_children_html(&self.children);
|
||||||
|
|
||||||
|
format!("<em>{}</em>", html)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
utils::collect_children_text(&self.children)
|
||||||
|
}
|
||||||
|
}
|
38
src/generator/heading.rs
Normal file
38
src/generator/heading.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use markdown::mdast::Heading;
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Heading {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(node.to_html())
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: String = result.into_iter().collect();
|
||||||
|
|
||||||
|
if self.depth < 4 {
|
||||||
|
let html_fragment_text = utils::to_html_fragment(&self.get_text());
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"<h{} id=\"{}\" class=\"heading-linked\"><a href=\"#{}\">{}</a></h{}>",
|
||||||
|
self.depth, html_fragment_text, html_fragment_text, text, self.depth
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("<h{}>{}</h{}>", self.depth, text, self.depth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(node.get_text())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.join("-")
|
||||||
|
}
|
||||||
|
}
|
94
src/generator/highlighter/mod.rs
Normal file
94
src/generator/highlighter/mod.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use misti::TokenType;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! replace {
|
||||||
|
($classes:literal, $token:ident, $offset:ident, $output:ident) => {{
|
||||||
|
let start_pos = $token.position;
|
||||||
|
let end_pos = $token.get_end_position();
|
||||||
|
|
||||||
|
let range = (start_pos + $offset)..(end_pos + $offset);
|
||||||
|
let html = format!("<span class=\"token {}\">{}</span>", $classes, $token.value);
|
||||||
|
|
||||||
|
$offset += 28 + $classes.len();
|
||||||
|
|
||||||
|
$output.replace_range(range, html.as_str());
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn highlight(input: &String) -> String {
|
||||||
|
// The tokens come in order
|
||||||
|
let tokens = misti::tokenize(&input);
|
||||||
|
|
||||||
|
if tokens.is_err() {
|
||||||
|
// eprintln!("Found a lexical error processing code.\n{:?}", tokens);
|
||||||
|
return input.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = input.clone();
|
||||||
|
// Offset to the position of the tokens in the string, to allow
|
||||||
|
// several tokens to be highlighted
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
for token in tokens.unwrap() {
|
||||||
|
match &token.token_type {
|
||||||
|
TokenType::Datatype => replace!("class-name", token, offset, output),
|
||||||
|
TokenType::Number => replace!("number", token, offset, output),
|
||||||
|
TokenType::Identifier if token.value == "true" || token.value == "false" => {
|
||||||
|
replace!("keyword", token, offset, output)
|
||||||
|
}
|
||||||
|
TokenType::String => replace!("string", token, offset, output),
|
||||||
|
TokenType::Comment => replace!("comment", token, offset, output),
|
||||||
|
TokenType::VAL | TokenType::VAR => replace!("keyword", token, offset, output),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_simple_string() {
|
||||||
|
assert_eq!("sample", highlight(&String::from("sample")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_highlight_datatype() {
|
||||||
|
assert_eq!(
|
||||||
|
"<span class=\"token class-name\">Num</span>",
|
||||||
|
highlight(&String::from("Num"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_highlight_number() {
|
||||||
|
assert_eq!(
|
||||||
|
"<span class=\"token number\">322</span>",
|
||||||
|
highlight(&String::from("322"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_highlight_string() {
|
||||||
|
assert_eq!(
|
||||||
|
"<span class=\"token string\">\"Hello\"</span>",
|
||||||
|
highlight(&String::from("\"Hello\""))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_highlight_multiple_tokens() {
|
||||||
|
assert_eq!(
|
||||||
|
"<span class=\"token class-name\">Str</span> x = <span class=\"token number\">322</span>",
|
||||||
|
highlight(&String::from("Str x = 322"))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"<span class=\"token class-name\">Str</span> x = <span class=\"token string\">\"hello\"</span> <span class=\"token number\">322</span>",
|
||||||
|
highlight(&String::from("Str x = \"hello\" 322"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
src/generator/inline_code.rs
Normal file
26
src/generator/inline_code.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use markdown::mdast::InlineCode;
|
||||||
|
|
||||||
|
// use super::highlighter::highlight;
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for InlineCode {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
/*
|
||||||
|
let tokens = misti::tokenize(&self.value);
|
||||||
|
println!("INLINE CODE ==== tokens ====\n\n{:?}\n\n==== code ====\n\n{}\n\n", tokens, self.value);
|
||||||
|
|
||||||
|
let s = self.value
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">");
|
||||||
|
*/
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"<code class=\"border border-border-color dark:border-transparent\">{}</code>",
|
||||||
|
&self.value // highlight(&self.value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
self.value.clone()
|
||||||
|
}
|
||||||
|
}
|
47
src/generator/list.rs
Normal file
47
src/generator/list.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use markdown::mdast::{List, ListItem, Node};
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for List {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(format!("<li class=\"py-2\">{}</li>", node.to_html()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let str: String = result.into_iter().collect();
|
||||||
|
|
||||||
|
if self.ordered {
|
||||||
|
format!("<ol class=\"list-decimal list-inside\">{}</ol>", str)
|
||||||
|
} else {
|
||||||
|
format!("<ul class=\"list-disc list-inside\">{}</ul>", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
panic!("List cannot return it's raw text")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printable for ListItem {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
let s = match node {
|
||||||
|
Node::Paragraph(p) => utils::collect_children_html(&p.children),
|
||||||
|
_ => panic!("A thing other than Paragraph inside ListItem (?)"),
|
||||||
|
};
|
||||||
|
result.push(format!("{}", s))
|
||||||
|
}
|
||||||
|
|
||||||
|
result.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
panic!("ListItem cannot return it's raw text")
|
||||||
|
}
|
||||||
|
}
|
59
src/generator/mod.rs
Normal file
59
src/generator/mod.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use markdown::mdast::Node;
|
||||||
|
|
||||||
|
mod code;
|
||||||
|
mod emphasis;
|
||||||
|
mod heading;
|
||||||
|
mod inline_code;
|
||||||
|
mod list;
|
||||||
|
mod paragraph;
|
||||||
|
mod root;
|
||||||
|
mod strong;
|
||||||
|
mod text;
|
||||||
|
|
||||||
|
// mod highlighter;
|
||||||
|
|
||||||
|
pub trait Printable {
|
||||||
|
fn to_html(&self) -> String;
|
||||||
|
fn get_text(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printable for Node {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Node::Root(root) => root.to_html(),
|
||||||
|
Node::Heading(heading) => heading.to_html(),
|
||||||
|
Node::Text(text) => text.to_html(),
|
||||||
|
Node::Paragraph(p) => p.to_html(),
|
||||||
|
Node::ThematicBreak(_) => String::from("<hr />"),
|
||||||
|
Node::InlineCode(i) => i.to_html(),
|
||||||
|
Node::Code(c) => c.to_html(),
|
||||||
|
Node::Html(h) => h.value.clone(),
|
||||||
|
Node::Strong(s) => s.to_html(),
|
||||||
|
Node::Emphasis(e) => e.to_html(),
|
||||||
|
Node::List(l) => l.to_html(),
|
||||||
|
Node::ListItem(l) => l.to_html(),
|
||||||
|
_ => format!(
|
||||||
|
"<div style=\"background-color: red\">Not implemented<br>{:?}</div>",
|
||||||
|
self
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Node::Root(root) => root.get_text(),
|
||||||
|
Node::Heading(heading) => heading.get_text(),
|
||||||
|
Node::Text(text) => text.get_text(),
|
||||||
|
Node::Paragraph(p) => p.get_text(),
|
||||||
|
Node::ThematicBreak(_) => panic!("<hr> cannot return its raw text"),
|
||||||
|
Node::InlineCode(i) => i.get_text(),
|
||||||
|
Node::Code(c) => c.get_text(),
|
||||||
|
Node::Html(_) => panic!("Html cannot return its raw text"),
|
||||||
|
Node::Strong(s) => s.get_text(),
|
||||||
|
Node::Emphasis(e) => e.get_text(),
|
||||||
|
Node::List(l) => l.get_text(),
|
||||||
|
Node::ListItem(l) => l.get_text(),
|
||||||
|
_ => String::from(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/generator/paragraph.rs
Normal file
21
src/generator/paragraph.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use markdown::mdast::Paragraph;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Paragraph {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(node.to_html())
|
||||||
|
}
|
||||||
|
|
||||||
|
let text: String = result.into_iter().collect();
|
||||||
|
|
||||||
|
format!("<p>{}</p>", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
panic!("Paragraph cannot return its raw text")
|
||||||
|
}
|
||||||
|
}
|
25
src/generator/root.rs
Normal file
25
src/generator/root.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use markdown::mdast;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for mdast::Root {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(node.to_html())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in &self.children {
|
||||||
|
result.push(node.get_text())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.join("-")
|
||||||
|
}
|
||||||
|
}
|
17
src/generator/strong.rs
Normal file
17
src/generator/strong.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use markdown::mdast::Strong;
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Strong {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
let text = utils::collect_children_html(&self.children);
|
||||||
|
|
||||||
|
format!("<b>{}</b>", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
utils::collect_children_text(&self.children)
|
||||||
|
}
|
||||||
|
}
|
13
src/generator/text.rs
Normal file
13
src/generator/text.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use markdown::mdast::Text;
|
||||||
|
|
||||||
|
use super::Printable;
|
||||||
|
|
||||||
|
impl Printable for Text {
|
||||||
|
fn to_html(&self) -> String {
|
||||||
|
self.value.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text(&self) -> String {
|
||||||
|
self.value.clone()
|
||||||
|
}
|
||||||
|
}
|
103
src/main.rs
Normal file
103
src/main.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use std::{fs, path::Path};
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
mod generator;
|
||||||
|
mod pages;
|
||||||
|
mod processor;
|
||||||
|
mod sidebar;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
const CONFIG_NAME: &str = "md-docs.config.yaml";
|
||||||
|
const INPUT_KEY: &str = "input";
|
||||||
|
const OUTPUT_KEY: &str = "output";
|
||||||
|
|
||||||
|
/// Creates a `YAML::String` from a `&str`
|
||||||
|
macro_rules! ystr {
|
||||||
|
($str:literal) => {
|
||||||
|
&Yaml::String(String::from($str))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let config_file = Path::new(CONFIG_NAME);
|
||||||
|
if !config_file.is_dir() {
|
||||||
|
eprintln!("A {} file was not found. Aborting.", CONFIG_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_str = match fs::read(config_file) {
|
||||||
|
Ok(s) => String::from_utf8(s).expect("md-docs.config.yaml MUST contain valid UTF-8"),
|
||||||
|
Err(error) => {
|
||||||
|
println!("Error reading {}.\n{:?}", CONFIG_NAME, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_yaml = match yaml_rust::YamlLoader::load_from_str(&config_str) {
|
||||||
|
Ok(y) => {
|
||||||
|
let document = &y[0];
|
||||||
|
let Yaml::Hash(hash) = document
|
||||||
|
else {
|
||||||
|
eprintln!("{} doesn't contain a hash as first value.", CONFIG_NAME);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
hash.clone()
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("{} doesn't contain valid YAML.\n{:?}", CONFIG_NAME, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_folder = match config_yaml.get(ystr!("input")) {
|
||||||
|
Some(Yaml::String(input)) => input,
|
||||||
|
Some(_) => {
|
||||||
|
eprintln!("{}'s `{}` key MUST be a string", CONFIG_NAME, INPUT_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("{} MUST have a `{}` key", CONFIG_NAME, INPUT_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_folder = match config_yaml.get(ystr!("output")) {
|
||||||
|
Some(Yaml::String(input)) => input,
|
||||||
|
Some(_) => {
|
||||||
|
eprintln!("{}'s `{}` key MUST be a string", CONFIG_NAME, OUTPUT_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("{} MUST have a `{}` key", CONFIG_NAME, OUTPUT_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_folder = Path::new(input_folder);
|
||||||
|
let output_folder = Path::new(output_folder);
|
||||||
|
|
||||||
|
match (input_folder.is_dir(), output_folder.is_dir()) {
|
||||||
|
(true, true) => {
|
||||||
|
processor::search_config_file(&input_folder, input_folder, output_folder);
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
eprintln!(
|
||||||
|
"{}'s `{}` key is not a valid path to a folder",
|
||||||
|
CONFIG_NAME, INPUT_KEY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(true, false) => {
|
||||||
|
eprintln!(
|
||||||
|
"{}'s `{}` key is not a valid path to a folder",
|
||||||
|
CONFIG_NAME, OUTPUT_KEY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(false, false) => {
|
||||||
|
eprintln!(
|
||||||
|
"{}'s `{}` and `{}` keys are not valid paths to a folder",
|
||||||
|
CONFIG_NAME, INPUT_KEY, OUTPUT_KEY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/pages/md_compiler.rs
Normal file
67
src/pages/md_compiler.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::Write,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{generator::Printable, sidebar::SidebarGenerator};
|
||||||
|
|
||||||
|
/// ## Parameters
|
||||||
|
///
|
||||||
|
/// - `file`: Path to the MD file to compile
|
||||||
|
/// - `input_folder`: Path to the input folder passed as parameter of the program
|
||||||
|
/// - `output_folder`: Path to the output folder passed as parameter of the program
|
||||||
|
/// - `file_tree_html`: HTML code of the file tree to be inserted into the generated HTML
|
||||||
|
pub fn compile(file: &PathBuf, input_folder: &Path, output_folder: &Path, file_tree_html: &String) {
|
||||||
|
// /home/fernando/misti/docs/markdown
|
||||||
|
let input_folder = input_folder.canonicalize().unwrap();
|
||||||
|
|
||||||
|
// /home/fernando/misti/docs/markdown/en/docs/latest/index.md
|
||||||
|
let input_file = file
|
||||||
|
.canonicalize()
|
||||||
|
.expect(format!("Expected file {:?} to exist", file).as_str());
|
||||||
|
|
||||||
|
// /home/fernando/misti/docs/static
|
||||||
|
let output_folder = output_folder.canonicalize().unwrap();
|
||||||
|
|
||||||
|
// en/docs/latests/index.md
|
||||||
|
let relative_input_file = input_file.strip_prefix(input_folder).unwrap();
|
||||||
|
|
||||||
|
let mut output_file = output_folder.clone();
|
||||||
|
output_file.push(relative_input_file);
|
||||||
|
output_file.set_extension("html");
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read MD from disk
|
||||||
|
//
|
||||||
|
let file_content_bytes = fs::read(&input_file).unwrap();
|
||||||
|
let markdown_text = String::from_utf8(file_content_bytes).unwrap();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compile MD
|
||||||
|
//
|
||||||
|
let md_ast = markdown::to_mdast(&markdown_text, &markdown::ParseOptions::gfm()).unwrap();
|
||||||
|
let html_text = md_ast.to_html();
|
||||||
|
let sidebar_html = md_ast.generate_sidebar();
|
||||||
|
|
||||||
|
// Read template.html
|
||||||
|
let mut template_path = output_folder.clone();
|
||||||
|
template_path.push("template.html");
|
||||||
|
|
||||||
|
let template_contents = fs::read(template_path).unwrap();
|
||||||
|
let template_contents = String::from_utf8(template_contents).unwrap();
|
||||||
|
|
||||||
|
// Insert the markdown, sidebar and file tree into the template
|
||||||
|
let final_output = template_contents
|
||||||
|
.replace("{{markdown}}", &html_text)
|
||||||
|
.replace("{{sidebar}}", &sidebar_html)
|
||||||
|
.replace("{{pages}}", &file_tree_html);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Write to disk
|
||||||
|
//
|
||||||
|
File::create(&output_file)
|
||||||
|
.expect(format!("MD: Output file should be valid {:?}", &output_file).as_str())
|
||||||
|
.write_all(final_output.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
}
|
174
src/pages/mod.rs
Normal file
174
src/pages/mod.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
use crate::utils;
|
||||||
|
|
||||||
|
mod md_compiler;
|
||||||
|
|
||||||
|
pub enum Node<'a> {
|
||||||
|
File(File<'a>),
|
||||||
|
Folder(Folder<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct File<'a> {
|
||||||
|
/// Name of the file
|
||||||
|
path: &'a String,
|
||||||
|
/// Display name of the file
|
||||||
|
name: &'a String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Folder<'a> {
|
||||||
|
/// Name of the folder
|
||||||
|
path: &'a String,
|
||||||
|
/// Display name of the folder
|
||||||
|
name: &'a String,
|
||||||
|
/// If true, then there MUST be a `File {path: "index"}` in the `children` field
|
||||||
|
has_index: bool,
|
||||||
|
/// Sub files or folders
|
||||||
|
children: Box<Vec<Node<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `YAML::String` from a `&str`
|
||||||
|
macro_rules! y_str {
|
||||||
|
($str:literal) => {
|
||||||
|
&Yaml::String(String::from($str))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_yaml(values: &Yaml) -> Node {
|
||||||
|
let Yaml::Hash(table) = values
|
||||||
|
else {panic!("YAML: input MUST be an object")};
|
||||||
|
|
||||||
|
// Node path
|
||||||
|
let Yaml::String(path) = table.get(y_str!("path")).expect("YAML: Node MUST have a `path` key")
|
||||||
|
else { panic!("YAML: `path` MUST be a String") };
|
||||||
|
|
||||||
|
let Yaml::String(name) = table.get(y_str!("name")).expect("YAML: Node MUST have a `name` key")
|
||||||
|
else { panic!("YAML: `name` MUST be a String") };
|
||||||
|
|
||||||
|
let input_data = (
|
||||||
|
table.get(y_str!("has_index")),
|
||||||
|
table.get(y_str!("children")),
|
||||||
|
);
|
||||||
|
|
||||||
|
match input_data {
|
||||||
|
(None, None) => Node::File(File { path, name }),
|
||||||
|
(has_index, Some(children)) => {
|
||||||
|
let has_index = match has_index {
|
||||||
|
Some(Yaml::Boolean(v)) => *v,
|
||||||
|
Some(_) => panic!("YAML: if key `has_index` exists, it MUST be a Boolean"),
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Yaml::Array(children) = children
|
||||||
|
else {panic!("YAML: `children` MUST be an Array")};
|
||||||
|
|
||||||
|
let children_nodes: Vec<Node> = children
|
||||||
|
.into_iter()
|
||||||
|
.map(|values| parse_yaml(values))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Node::Folder(Folder {
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
has_index,
|
||||||
|
children: Box::new(children_nodes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("YAML: A Node is missing a `name` or `children` key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_pages_html(file_tree: &Node, current_path: &Path) -> String {
|
||||||
|
match file_tree {
|
||||||
|
Node::File(file) => {
|
||||||
|
if file.path == "index" {
|
||||||
|
format!(
|
||||||
|
"<li class=\"my-1\">
|
||||||
|
<a class=\"inline-block rounded-md w-full hover:text-c2-primary p-1\" href=\"/{}\">Index</a>
|
||||||
|
</li>",
|
||||||
|
current_path.to_str().unwrap()
|
||||||
|
)
|
||||||
|
} else if file.path == "" {
|
||||||
|
String::from("")
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"<li class=\"my-1\">
|
||||||
|
<a class=\"inline-block rounded-md w-full hover:text-c2-primary p-1\" href=\"/{}/{}.html\">{}</a>
|
||||||
|
</li>",
|
||||||
|
current_path.to_str().unwrap(),
|
||||||
|
file.path,
|
||||||
|
file.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Folder(folder) => {
|
||||||
|
let mut new_path = current_path.to_path_buf();
|
||||||
|
new_path.push(folder.path);
|
||||||
|
|
||||||
|
let sub_nodes_html: Vec<String> = folder
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|n| generate_pages_html(n, &new_path))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// This is true for the root of the YAML file
|
||||||
|
if folder.path == "" {
|
||||||
|
format!("<ul>{}</ul>", sub_nodes_html.join(""))
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"<li class=\"my-1\">
|
||||||
|
<div class=\"uppercase opacity-80 mt-6 font-semibold\">{}</div>
|
||||||
|
<ul>{}</ul>
|
||||||
|
</li>",
|
||||||
|
folder.name,
|
||||||
|
sub_nodes_html.join("")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_md_to_html(
|
||||||
|
file_tree: &Node,
|
||||||
|
current_path: &Path,
|
||||||
|
input_folder: &Path,
|
||||||
|
output_folder: &Path,
|
||||||
|
file_tree_html: &String,
|
||||||
|
) {
|
||||||
|
match file_tree {
|
||||||
|
Node::File(file) if file.path != "" => {
|
||||||
|
let mut file_path = current_path.canonicalize().unwrap();
|
||||||
|
file_path.push(format!("{}.md", file.path));
|
||||||
|
|
||||||
|
md_compiler::compile(&file_path, input_folder, output_folder, file_tree_html);
|
||||||
|
}
|
||||||
|
Node::File(_) => {
|
||||||
|
panic!("YAML: A file cannot have an empty `path` key")
|
||||||
|
}
|
||||||
|
Node::Folder(folder) if folder.path != "" => {
|
||||||
|
let mut new_path = current_path.canonicalize().unwrap();
|
||||||
|
new_path.push(folder.path);
|
||||||
|
utils::ensure_folder_exists(&new_path, input_folder, output_folder)
|
||||||
|
.expect("SHOULD be able to create folder");
|
||||||
|
|
||||||
|
for node in folder.children.iter() {
|
||||||
|
compile_md_to_html(node, &new_path, input_folder, output_folder, file_tree_html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Folder(folder) => {
|
||||||
|
for node in folder.children.iter() {
|
||||||
|
compile_md_to_html(
|
||||||
|
node,
|
||||||
|
¤t_path,
|
||||||
|
input_folder,
|
||||||
|
output_folder,
|
||||||
|
file_tree_html,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
src/processor.rs
Normal file
97
src/processor.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use crate::pages::{compile_md_to_html, generate_pages_html, parse_yaml};
|
||||||
|
use crate::utils;
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
use yaml_rust::YamlLoader;
|
||||||
|
|
||||||
|
enum EntryFound {
|
||||||
|
YamlFile,
|
||||||
|
OtherFile,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverses the current path searching for a YAML file
|
||||||
|
pub fn search_config_file(current_path: &Path, input_folder: &Path, output_folder: &Path) {
|
||||||
|
// Iterate over all the files searching for a YAML file
|
||||||
|
let result = current_path
|
||||||
|
.read_dir()
|
||||||
|
.unwrap()
|
||||||
|
.fold(&EntryFound::None, |acc, next| {
|
||||||
|
let p = next.unwrap().path();
|
||||||
|
let is_file = p.is_file();
|
||||||
|
let ext = p.extension();
|
||||||
|
|
||||||
|
match (acc, is_file, ext) {
|
||||||
|
(EntryFound::YamlFile, true, Some(x)) if x == "yaml" => {
|
||||||
|
panic!("FOUND A SECOND YAML FILE!!!")
|
||||||
|
}
|
||||||
|
(EntryFound::YamlFile, _, _) => acc,
|
||||||
|
(EntryFound::OtherFile, true, Some(x)) if x == "yaml" => &EntryFound::YamlFile,
|
||||||
|
(EntryFound::None, true, Some(x)) if x == "yaml" => &EntryFound::YamlFile,
|
||||||
|
(EntryFound::None, true, Some(_)) => &EntryFound::OtherFile,
|
||||||
|
_ => acc,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match result {
|
||||||
|
// If a file other than a YAML file is found, panic
|
||||||
|
EntryFound::OtherFile => panic!(
|
||||||
|
"Found an orphan file without a YAML parent at {:?}",
|
||||||
|
current_path
|
||||||
|
),
|
||||||
|
// Process the YAML file
|
||||||
|
EntryFound::YamlFile => process_yaml(current_path, input_folder, output_folder),
|
||||||
|
// No files found, recursively read children folders
|
||||||
|
EntryFound::None => {
|
||||||
|
for entry in current_path.read_dir().unwrap() {
|
||||||
|
// Should always succeed, and countain a folder
|
||||||
|
let x = entry.unwrap();
|
||||||
|
let path = x.path();
|
||||||
|
|
||||||
|
utils::ensure_folder_exists(&path, input_folder, output_folder).unwrap();
|
||||||
|
search_config_file(&path, input_folder, output_folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_yaml(current_path: &Path, input_folder: &Path, output_folder: &Path) {
|
||||||
|
//
|
||||||
|
// Read YAML file
|
||||||
|
//
|
||||||
|
let mut yaml_path = current_path.canonicalize().unwrap();
|
||||||
|
yaml_path.push("index.yaml");
|
||||||
|
|
||||||
|
let yaml_bytes = fs::read(yaml_path).expect("File index.yaml MUST exist");
|
||||||
|
let yaml = String::from_utf8(yaml_bytes).expect("YAML index file MUST be valid UTF-8");
|
||||||
|
|
||||||
|
let yaml_docs =
|
||||||
|
YamlLoader::load_from_str(yaml.as_str()).expect("YAML file MUST contain valid YAML");
|
||||||
|
let yaml = &yaml_docs[0];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parse YAML
|
||||||
|
//
|
||||||
|
let file_tree = parse_yaml(&yaml);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Generate File Tree HTML
|
||||||
|
//
|
||||||
|
let tree_html = {
|
||||||
|
let input_folder = input_folder.canonicalize().unwrap();
|
||||||
|
let yaml_folder_temp = current_path.canonicalize().unwrap();
|
||||||
|
let web_absolute_path = yaml_folder_temp.strip_prefix(input_folder).unwrap();
|
||||||
|
|
||||||
|
generate_pages_html(&file_tree, web_absolute_path)
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compile MD to HTML
|
||||||
|
//
|
||||||
|
compile_md_to_html(
|
||||||
|
&file_tree,
|
||||||
|
current_path,
|
||||||
|
input_folder,
|
||||||
|
output_folder,
|
||||||
|
&tree_html,
|
||||||
|
);
|
||||||
|
}
|
90
src/sidebar/mod.rs
Normal file
90
src/sidebar/mod.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use markdown::mdast::{Heading, Node};
|
||||||
|
|
||||||
|
use crate::{generator::Printable, utils};
|
||||||
|
|
||||||
|
pub trait SidebarGenerator {
|
||||||
|
fn generate_sidebar(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SidebarGenerator for Node {
|
||||||
|
fn generate_sidebar(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Node::Root(root) => {
|
||||||
|
let children_nodes = root
|
||||||
|
.children
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|x| match x {
|
||||||
|
Node::Heading(h) if h.depth <= 3 => Some(h),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// A top level topic that contains other topics
|
||||||
|
let topic = extract_topics(&children_nodes, 0, 1);
|
||||||
|
|
||||||
|
match topic {
|
||||||
|
Some((t, _)) => {
|
||||||
|
let html: String = t.children.iter().map(|x| x.get_html()).collect();
|
||||||
|
format!("<ul>{}</ul>", html)
|
||||||
|
}
|
||||||
|
None => String::from("D:"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("??"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Topic {
|
||||||
|
text: String,
|
||||||
|
children: Box<Vec<Topic>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Topic {
|
||||||
|
pub fn get_html(&self) -> String {
|
||||||
|
let extra = if self.children.len() > 0 {
|
||||||
|
let children_html: String = self.children.iter().map(|x| x.get_html()).collect();
|
||||||
|
|
||||||
|
format!("<ol class=\"px-4\">{}</ol>", children_html)
|
||||||
|
} else {
|
||||||
|
String::from("")
|
||||||
|
};
|
||||||
|
|
||||||
|
let html_fragment_link = utils::to_html_fragment(&self.text);
|
||||||
|
format!(
|
||||||
|
"<li class=\"m-2\"><a href=\"#{}\" class=\"inline-block w-full hover:text-c2-primary\">{}</a>{}</li>",
|
||||||
|
html_fragment_link, self.text, extra
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the next heading and all its children
|
||||||
|
// current_level: the depth of the heading to match
|
||||||
|
fn extract_topics<'a>(
|
||||||
|
headings: &'a Vec<Heading>,
|
||||||
|
current_pos: usize,
|
||||||
|
current_level: u8,
|
||||||
|
) -> Option<(Topic, usize)> {
|
||||||
|
match headings.get(current_pos) {
|
||||||
|
Some(h) if h.depth == current_level => {
|
||||||
|
let mut new_vec = Vec::new();
|
||||||
|
let mut next_pos = current_pos + 1;
|
||||||
|
|
||||||
|
while let Some((topic, next)) = extract_topics(headings, next_pos, current_level + 1) {
|
||||||
|
new_vec.push(topic);
|
||||||
|
next_pos = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = h.get_text();
|
||||||
|
let topic = Topic {
|
||||||
|
text: title,
|
||||||
|
children: Box::new(new_vec),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((topic, next_pos))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
66
src/utils.rs
Normal file
66
src/utils.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
use markdown::mdast::Node;
|
||||||
|
|
||||||
|
use crate::generator::Printable;
|
||||||
|
|
||||||
|
pub fn to_html_fragment(text: &String) -> String {
|
||||||
|
text.clone().replace(" ", "-")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_children_html(vec: &Vec<Node>) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in vec {
|
||||||
|
result.push(node.to_html())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_children_text(vec: &Vec<Node>) -> String {
|
||||||
|
let mut result = Vec::<String>::new();
|
||||||
|
|
||||||
|
for node in vec {
|
||||||
|
result.push(node.get_text())
|
||||||
|
}
|
||||||
|
|
||||||
|
result.join("-")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_folder_exists(
|
||||||
|
folder: &Path,
|
||||||
|
input_folder: &Path,
|
||||||
|
output_folder: &Path,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// /home/fernando/misti/docs/markdown
|
||||||
|
let input_folder = input_folder.canonicalize().unwrap();
|
||||||
|
|
||||||
|
// /home/fernando/misti/docs/static
|
||||||
|
let output_folder = output_folder.canonicalize().unwrap();
|
||||||
|
|
||||||
|
// /home/fernando/misti/docs/markdown/en/
|
||||||
|
let full_input_folder = folder.canonicalize().unwrap();
|
||||||
|
|
||||||
|
let relative_new_folder = full_input_folder.strip_prefix(input_folder).unwrap();
|
||||||
|
|
||||||
|
let mut full_output_folder = output_folder.clone();
|
||||||
|
full_output_folder.push(relative_new_folder);
|
||||||
|
|
||||||
|
// println!("Ensuring that folder exists:\n{:?}", full_output_folder);
|
||||||
|
|
||||||
|
// If this is a "top-level" folder, remove all its contents, if it exists
|
||||||
|
if full_output_folder.is_dir() {
|
||||||
|
// println!("| Removing...");
|
||||||
|
let _ = fs::remove_dir_all(&full_output_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create folder
|
||||||
|
match fs::create_dir(&full_output_folder) {
|
||||||
|
Ok(_) => {
|
||||||
|
// println!("| done\n\n");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(_) => Err(format!("Error creating folder {:?}", full_output_folder)),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user