From 82f7304590bec02c0aad71bbe8cbb1b5961745d4 Mon Sep 17 00:00:00 2001 From: Arne Hilmann Date: Fri, 23 Apr 2021 13:57:16 +0200 Subject: [PATCH] add rust implementation --- Cargo.lock | 319 +++++++++-------- Cargo.toml | 41 ++- src/Dockerfile.pandoc => Dockerfile.pandoc | 136 +++++--- build.rs | 12 +- src/Makefile | 81 +++-- src/docker-compose.yaml.source | 20 +- src/live_server.rs | 235 +++++-------- src/main.rs | 385 +++++++++++++++------ src/markdown_pp.rs | 87 +++++ src/sass.rs | 24 ++ 10 files changed, 794 insertions(+), 546 deletions(-) rename src/Dockerfile.pandoc => Dockerfile.pandoc (64%) create mode 100644 src/markdown_pp.rs create mode 100644 src/sass.rs diff --git a/Cargo.lock b/Cargo.lock index ae2257c..dc188e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,7 +17,7 @@ dependencies = [ "log", "once_cell", "parking_lot", - "pin-project 0.4.27", + "pin-project 0.4.28", "smallvec", "tokio", "tokio-util", @@ -36,7 +36,7 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project 0.4.27", + "pin-project 0.4.28", "tokio", "tokio-util", ] @@ -62,11 +62,10 @@ dependencies = [ [[package]] name = "actix-files" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8035f08f194893b199f4928b40425bd727c0257cf0fcf36f4ac214968d649ec7" +checksum = "c51e8a9146c12fce92a6e4c24b8c4d9b05268130bfd8d61bc587e822c32ce689" dependencies = [ - "actix-http", "actix-service", "actix-web", "bitflags", @@ -117,7 +116,7 @@ dependencies = [ "log", "mime", "percent-encoding", - "pin-project 1.0.5", + "pin-project 1.0.7", "rand", "regex", "serde", @@ -135,7 +134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" dependencies = [ "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -193,7 +192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" dependencies = [ "futures-util", - "pin-project 0.4.27", + "pin-project 0.4.28", ] [[package]] @@ -253,7 +252,7 @@ dependencies = [ "futures-sink", "futures-util", "log", - "pin-project 0.4.27", + "pin-project 0.4.28", "slab", ] @@ -285,7 +284,7 @@ dependencies = [ "fxhash", "log", "mime", - "pin-project 1.0.5", + "pin-project 1.0.7", "regex", "serde", "serde_json", @@ -309,7 +308,7 @@ dependencies = [ "bytes 0.5.6", "futures-channel", "futures-core", - "pin-project 0.4.27", + "pin-project 0.4.28", ] [[package]] @@ -318,9 +317,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -329,9 +328,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -372,19 +371,19 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "async-trait" -version = "0.1.48" +version = "0.1.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea56748e10732c49404c153638a15ec3d6211ec5ff35d9bb20e13b93576adf" +checksum = "589652ce7ccb335d1e7ecb3be145425702b290dbcb7029bbeaae263fc1d87b48" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -483,9 +482,9 @@ dependencies = [ [[package]] name = "buf-min" -version = "0.1.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ae7069aad07c7cdefe6a22a671f00650728bd2331a4cc62e1e5d0becdf9ca4" +checksum = "fa17aa1cf56bdd6bb30518767d00e58019d326f3f05d8c3e0730b549d332ea83" dependencies = [ "bytes 0.5.6", ] @@ -508,6 +507,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +[[package]] +name = "bytes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16" + [[package]] name = "bytes" version = "1.0.1" @@ -575,9 +580,15 @@ dependencies = [ [[package]] name = "const_fn" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" +checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cookie" @@ -587,7 +598,7 @@ checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "percent-encoding", "time", - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -634,9 +645,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.1.8" +version = "3.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15b8ec3b5755a188c141c1f6a98e76de31b936209bf066b647979e2a84764a9" +checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a" dependencies = [ "nix", "winapi 0.3.9", @@ -644,13 +655,14 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.11" +version = "0.99.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6" dependencies = [ - "proc-macro2 1.0.24", + "convert_case", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -690,9 +702,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" dependencies = [ "heck", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -727,7 +739,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.5", + "redox_syscall 0.2.6", "winapi 0.3.9", ] @@ -796,9 +808,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1" +checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253" dependencies = [ "futures-channel", "futures-core", @@ -811,9 +823,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939" +checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25" dependencies = [ "futures-core", "futures-sink", @@ -821,15 +833,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" [[package]] name = "futures-executor" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1" +checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d" dependencies = [ "futures-core", "futures-task", @@ -838,39 +850,39 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" [[package]] name = "futures-macro" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7" +checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] name = "futures-sink" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3" +checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23" [[package]] name = "futures-task" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80" +checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc" [[package]] name = "futures-util" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1" +checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025" dependencies = [ "futures-channel", "futures-core", @@ -902,7 +914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -1020,9 +1032,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ "bytes 1.0.1", "fnv", @@ -1031,9 +1043,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.3.5" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +checksum = "bc35c995b9d93ec174cf9a27d425c7892722101e14993cd227fdb51d70cf9589" [[package]] name = "humansize" @@ -1049,9 +1061,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ "matches", "unicode-bidi", @@ -1197,9 +1209,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.88" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libflate" @@ -1221,9 +1233,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -1248,27 +1260,32 @@ dependencies = [ [[package]] name = "markdeck" -version = "0.1.0" +version = "0.60.0" dependencies = [ "actix", "actix-codec", "actix-files", + "actix-service", "actix-web", "actix-web-actors", "anyhow", "awc", - "bytes 0.5.6", + "bytes 0.6.0", + "clap", "ctrlc", "env_logger", "futures", "glob", "grass", + "log", "notify", + "regex", "rust-embed", "rusync", "serde", "serde_json", "uuid", + "walkdir", ] [[package]] @@ -1410,9 +1427,9 @@ dependencies = [ [[package]] name = "notify" -version = "4.0.15" +version = "4.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" +checksum = "2599080e87c9bd051ddb11b10074f4da7b1223298df65d4c2ec5bcf309af1533" dependencies = [ "bitflags", "filetime 0.2.14", @@ -1510,7 +1527,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.2.5", + "redox_syscall 0.2.6", "smallvec", "winapi 0.3.9", ] @@ -1566,9 +1583,9 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro-hack", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -1582,42 +1599,42 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f" dependencies = [ - "pin-project-internal 0.4.27", + "pin-project-internal 0.4.28", ] [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" dependencies = [ - "pin-project-internal 1.0.5", + "pin-project-internal 1.0.7", ] [[package]] name = "pin-project-internal" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -1667,9 +1684,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid 0.2.1", ] @@ -1695,7 +1712,7 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", ] [[package]] @@ -1757,18 +1774,18 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.4.4" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54fd1046a3107eb58f42de31d656fee6853e5d276c455fd943742dce89fc3dd3" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" dependencies = [ "aho-corasick", "memchr", @@ -1815,10 +1832,10 @@ version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed91c41c42ef7bf687384439c312e75e0da9c149b0390889b94de3c7d9d9e66" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", "rust-embed-utils", - "syn 1.0.62", + "syn 1.0.69", "walkdir", ] @@ -1894,22 +1911,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -1965,9 +1982,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" +checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" [[package]] name = "slab" @@ -1994,11 +2011,11 @@ dependencies = [ [[package]] name = "standback" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" dependencies = [ - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -2021,11 +2038,11 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", "serde", "serde_derive", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -2035,13 +2052,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", "serde", "serde_derive", "serde_json", "sha1", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -2091,11 +2108,11 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.62" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", "unicode-xid 0.2.1", ] @@ -2149,9 +2166,9 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] @@ -2165,16 +2182,16 @@ dependencies = [ [[package]] name = "time" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" +checksum = "08a8cbfbf47955132d0202d1662f49b2423ae35862aee471f3ba4b133358f372" dependencies = [ "const_fn", "libc", "standback", "stdweb", "time-macros", - "version_check 0.9.2", + "version_check 0.9.3", "winapi 0.3.9", ] @@ -2195,17 +2212,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", "standback", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" dependencies = [ "tinyvec_macros", ] @@ -2279,7 +2296,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "pin-project 1.0.5", + "pin-project 1.0.7", "tracing", ] @@ -2334,14 +2351,14 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" dependencies = [ "matches", ] @@ -2403,9 +2420,9 @@ dependencies = [ [[package]] name = "v_escape" -version = "0.13.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039a44473286eb84e4e74f90165feff67c802dbeced7ee4c5b00d719b0d0475e" +checksum = "f3e0ab5fab1db278a9413d2ea794cb66f471f898c5b020c3c394f6447625d9d4" dependencies = [ "buf-min", "v_escape_derive", @@ -2418,16 +2435,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c860ad1273f4eee7006cee05db20c9e60e5d24cba024a32e1094aa8e574f3668" dependencies = [ "nom", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", ] [[package]] name = "v_htmlescape" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7c2a33ed7cf0dc1b42bcf39e01b6512f9df08f09e1cd8a49d9dc49a6a9482" +checksum = "1f9a8af610ad6f7fc9989c9d2590d9764bc61f294884e9ee93baa58795174572" dependencies = [ "cfg-if 1.0.0", "v_escape", @@ -2447,15 +2464,15 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi 0.3.9", @@ -2476,9 +2493,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2486,24 +2503,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote 1.0.9", "wasm-bindgen-macro-support", @@ -2511,22 +2528,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.62", + "syn 1.0.69", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "widestring" diff --git a/Cargo.toml b/Cargo.toml index fb2fcba..00271d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,33 +1,40 @@ [package] name = "markdeck" -version = "0.1.0" +version = "0.60.0" authors = ["Arne Hilmann "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -grass = "0.10.4" +grass = "0" +anyhow = "1" [dependencies] -rust-embed = { version = "5.8.0", features = ["compression"] } -grass = "0.10.4" -rusync = "0.7.0" -serde = { version = "1.0.124", features = ["derive"] } -serde_json = "1.0.64" -glob = "0.3.0" +rust-embed = { version = "5", features = ["compression"] } +grass = "0" +rusync = "0" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +glob = "0" actix = "0.10" -actix-codec = "0.3" +actix-codec = "0" actix-web = "3" actix-web-actors = "3" -actix-files = "0.3" +actix-files = "0" awc = "2" -env_logger = "0.8" -futures = "0.3.1" -bytes = "0.5.3" +env_logger = "0" +futures = "0" +bytes = "0" +actix-service = "1" -notify = "4.0.15" -anyhow = "1.0" -ctrlc = { version = "3.1.8", features = ["termination"] } -uuid = { version = "0.8.2", features = ["serde", "v4"] } \ No newline at end of file +notify = "4" +anyhow = "1" +ctrlc = { version = "3", features = ["termination"] } +uuid = { version = "0", features = ["serde", "v4"] } +log = "0" +walkdir = "2" +regex = "1" + +clap = "2" \ No newline at end of file diff --git a/src/Dockerfile.pandoc b/Dockerfile.pandoc similarity index 64% rename from src/Dockerfile.pandoc rename to Dockerfile.pandoc index c1ab497..288a7db 100644 --- a/src/Dockerfile.pandoc +++ b/Dockerfile.pandoc @@ -1,10 +1,12 @@ -FROM alpine:3.12.2 AS ALPINE_BASE -FROM node:15.4.0-alpine3.12 AS NODE_BASE -FROM gradle:6.7.1-jdk8-openj9 AS GRADLE_BASE -FROM rust:1.48.0-alpine3.12 AS RUST_BASE +FROM alpine:3.13.4 AS alpine_base +FROM node:15.14.0-alpine3.13 AS node_base +FROM gradle:6.8.3-jdk8-openj9 AS gradle_base +# FROM rust:1.51.0-slim-buster AS rust_base +FROM rust:1.51.0-alpine3.13 AS rust_base -FROM GRADLE_BASE AS build-ditaa + +FROM gradle_base AS build-ditaa WORKDIR / RUN curl -OL https://github.com/pepijnve/ditaa/archive/mini-0.12.tar.gz @@ -16,17 +18,20 @@ RUN cp build/libs/ditaa*.jar /ditaa.jar -FROM NODE_BASE AS build-vega +FROM node_base AS build-vega RUN apk add g++ make pkgconf pixman-dev cairo-dev pango-dev libjpeg-turbo-dev giflib-dev RUN npm -g config set user root RUN npm install -g vega vega-cli vega-lite \ && npm cache clean --force -RUN tar zcvf vega.tgz /usr/local/bin/vl2* /usr/local/bin/vg2* /usr/local/lib/node_modules/vega* +RUN tar zcvf /vega.tgz /usr/local/bin/vl2* /usr/local/bin/vg2* /usr/local/lib/node_modules/vega* +RUN mkdir /vega +WORKDIR /vega +RUN tar zxvf /vega.tgz -FROM NODE_BASE AS build-mermaid +FROM node_base AS build-mermaid ARG MERMAID_VERSION WORKDIR /tmp RUN yarn add mermaid@${MERMAID_VERSION} @@ -35,7 +40,7 @@ RUN ls -al / -FROM RUST_BASE AS build-svgbob +FROM rust_base AS build-svgbob ARG SVGBOB_VERSION WORKDIR /tmp RUN apk add curl musl-dev @@ -51,7 +56,7 @@ RUN chmod a+rx /svgbob -FROM ALPINE_BASE AS build-githubstyle +FROM alpine_base AS build-githubstyle WORKDIR /tmp RUN apk add git @@ -60,7 +65,7 @@ RUN cp pandoc-goodies/templates/html5/github/GitHub.html5 / -FROM ALPINE_BASE AS build-pandoc +FROM alpine_base AS build-pandoc ARG PANDOC_VERSION WORKDIR /tmp RUN apk add curl @@ -69,7 +74,7 @@ RUN mv bin/pandoc / -FROM ALPINE_BASE AS build-jquery +FROM alpine_base AS build-jquery ARG JQUERY_VERSION WORKDIR /tmp RUN apk add curl @@ -77,7 +82,7 @@ RUN curl -L -o /jquery.js https://code.jquery.com/jquery-${JQUERY_VERSION}.js -FROM ALPINE_BASE AS build-jqueryemojis +FROM alpine_base AS build-jqueryemojis WORKDIR /tmp RUN apk add curl RUN curl -L -o emojis.zip https://github.com/rodrigopolo/jqueryemoji/archive/master.zip @@ -87,7 +92,7 @@ RUN mv jqueryemoji-master/img/apple72/* /emojis -FROM ALPINE_BASE AS build-revealjs +FROM alpine_base AS build-revealjs ARG REVEALJS_VERSION WORKDIR /tmp RUN apk add curl @@ -99,7 +104,7 @@ RUN touch dist/reveal.esm.js.map -FROM ALPINE_BASE AS build-impressjs +FROM alpine_base AS build-impressjs ARG IMPRESSJS_VERSION WORKDIR /tmp RUN apk add curl @@ -110,7 +115,7 @@ RUN rm -rf *.md *.png build.js examples/ karma.conf.js package*.json qunit* test -FROM ALPINE_BASE AS build-plantuml +FROM alpine_base AS build-plantuml WORKDIR /tmp RUN apk add curl # RUN curl -L -o /plantuml.jar "https://sourceforge.net/projects/plantuml/files/plantuml.jar/download?use_mirror=10gbps-io" @@ -118,7 +123,7 @@ RUN curl -L -o /plantuml.jar "https://sourceforge.net/projects/plantuml/files/pl -FROM ALPINE_BASE AS build-asciinemaplayer +FROM alpine_base AS build-asciinemaplayer ARG ASCIINEMAPLAYER_VERSION WORKDIR /tmp RUN apk add curl @@ -128,7 +133,7 @@ RUN curl -L -o /asciinema-player.css "https://github.com/asciinema/asciinema-pla -FROM ALPINE_BASE AS build-foundationicons +FROM alpine_base AS build-foundationicons WORKDIR /tmp RUN apk add curl RUN curl -L -O https://zurb.com/playground/uploads/upload/upload/288/foundation-icons.zip @@ -137,65 +142,96 @@ RUN mv foundation-icons / -FROM ALPINE_BASE AS build-cssgg +FROM alpine_base AS build-cssgg RUN apk add curl RUN curl -L -o /all-gg.css https://css.gg/css -FROM NODE_BASE AS production +FROM alpine_base as build-mathjaxjs +RUN apk add git +RUN git clone https://github.com/mathjax/MathJax.git mj-tmp +RUN mv mj-tmp/es5 /mathjax -LABEL maintainer="arne@hilmann.de" -RUN apk add \ - make bash curl \ - graphviz inotify-tools rsync \ - libqrencode jq sassc zip openjdk8-jre \ - python3 py3-pip fontconfig lua5.3 \ - pixman cairo pango libjpeg-turbo giflib \ - && rm -rf /var/cache/apk/* -RUN npm -g config set user root -RUN npm install -g mathjax-pandoc-filter doctoc \ - && npm cache clean --force -COPY --from=build-vega /vega.tgz / -WORKDIR / -RUN tar zxf vega.tgz +FROM rust_base as build-markdeck -RUN mkdir -p /usr/local/share/lua/5.3 -RUN curl -o /usr/local/share/lua/5.3/inspect.lua -L https://raw.githubusercontent.com/kikito/inspect.lua/master/inspect.lua - -RUN pip3 install MarkdownPP +RUN apk add musl-dev RUN mkdir -p /markdeck/ -COPY --from=build-pandoc /pandoc /usr/bin/ +# TODO dont use docroot/main here, but copy from src/markdeck: +# COPY markdeck/ /markdeck/ COPY --from=build-revealjs /reveal.js /markdeck/assets/framework/reveal.js/ COPY --from=build-impressjs /impress.js /markdeck/assets/framework/impress.js/ COPY --from=build-jquery /jquery.js /markdeck/assets/3rdparty/ COPY --from=build-jqueryemojis /emojis /markdeck/lib/ COPY --from=build-ditaa /ditaa.jar /markdeck/lib/ -COPY --from=build-mermaid /mermaid.min.js /mermaid.min.js.map /markdeck/assets/3rdparty/ -COPY --from=build-asciinemaplayer /asciinema-player.* markdeck/assets/3rdparty/ -# markdeck/assets/3rdparty/asciinema-player.js: -# markdeck/assets/3rdparty/asciinema-player.js.map: +COPY --from=build-asciinemaplayer /asciinema-player.js /asciinema-player.js.map /asciinema-player.css /markdeck/assets/3rdparty/ COPY --from=build-plantuml /plantuml.jar /markdeck/lib/ -RUN ls -al /markdeck/assets/3rdparty -COPY --from=build-svgbob /svgbob* /usr/local/bin/ COPY --from=build-githubstyle /GitHub.html5 /markdeck/template-github.html COPY --from=build-foundationicons /foundation-icons /markdeck/assets/markdeck/css COPY --from=build-cssgg /all-gg.css /markdeck/assets/markdeck/css +COPY --from=build-mathjaxjs /mathjax /markdeck/assets/3rdparty/mathjax/ +COPY src/markdeck/template* /markdeck/.markdeck/ +COPY src/markdeck/explain.html /markdeck/ +COPY src/markdeck/metadata.template /markdeck/.markdeck/ +COPY src/markdeck/defaults.yaml /markdeck/.markdeck/ +COPY src/markdeck/lib/*.lua /markdeck/.markdeck/ +COPY src/markdeck/lib/*.config /markdeck/.markdeck/ +COPY src/markdeck/lib/*.jar /markdeck/.markdeck/ +COPY src/markdeck/assets/markdeck /markdeck/assets/ + +# TODO RUN rustup target add x86_64-unknown-linux-musl +# TODO RUN cargo install --target x86_64-unknown-linux-musl --path . + +WORKDIR /sources +COPY Cargo.* build.rs ./ +RUN mkdir src +RUN echo 'fn main() {}' > src/main.rs +RUN cargo build --release + +COPY src ./src +RUN rm -rf src/docroot/main/ +RUN ln -sf /markdeck src/docroot/main +RUN cargo build --release --frozen + -COPY markdeck/ /markdeck/ -WORKDIR /markdeck/assets/framework/reveal.js -RUN sassc css/theme/source/simple.scss > css/theme/simple.css -RUN sassc css/reveal.scss > css/reveal.css +FROM alpine_base AS production + +LABEL maintainer="arne@hilmann.de" + +RUN apk add \ + bash curl \ + graphviz \ + libqrencode zip openjdk8-jre \ + fontconfig lua5.3 \ + pixman cairo pango libjpeg-turbo giflib \ + npm \ + && rm -rf /var/cache/apk/* + +WORKDIR / + +RUN mkdir -p /usr/local/share/lua/5.3 +RUN curl -o /usr/local/share/lua/5.3/inspect.lua -L https://raw.githubusercontent.com/kikito/inspect.lua/master/inspect.lua + +COPY --from=build-vega /vega / +COPY --from=build-pandoc /pandoc /usr/bin/ +COPY --from=build-svgbob /svgbob* /usr/local/bin/ +COPY --from=build-markdeck /sources/target/release/markdeck /usr/bin/ + +# WORKDIR /markdeck/assets/framework/reveal.js +# RUN sassc css/theme/source/simple.scss > css/theme/simple.css +# RUN sassc css/reveal.scss > css/reveal.css WORKDIR /markdeck RUN ln -sf /target/assets/css/fonts /.fonts RUN ln -sf /markdeck/assets/markdeck/css/fonts/ /usr/share/fonts +WORKDIR /source + ARG version ENV VERSION $version @@ -211,7 +247,7 @@ RUN chmod 755 /target USER markdeck VOLUME ["/source", "/target"] -ENTRYPOINT ["/markdeck/loop"] +ENTRYPOINT ["/usr/bin/markdeck", "--port", "8080", "--source", "/source", "--target", "/target", "--watcher", "CompareReference"] FROM production AS development diff --git a/build.rs b/build.rs index 99b19a9..e71fdf5 100644 --- a/build.rs +++ b/build.rs @@ -1,11 +1 @@ -use std::error::Error; -use std::fs; - -fn main() -> Result<(), Box> { - println!("compiling scss"); - let sass = grass::from_path( - "src/markdeck/assets/markdeck/css/markdeck.revealjs.scss", - &grass::Options::default())?; - fs::write("src/markdeck/assets/markdeck/css/markdeck.revealjs.css", sass)?; - Ok(()) -} \ No newline at end of file +fn main() {} \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 554bee0..54085b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,18 +7,17 @@ MAKEFLAGS += --no-builtin-rules .PHONY: all tag push check-versions clean tabularasa -PANDOC_VERSION=2.9.2.1 +PANDOC_VERSION=2.13 REVEALJS_VERSION=4.1.0 ASCIINEMAPLAYER_VERSION=v2.6.1 -JQUERY_VERSION=3.5.1.slim.min +JQUERY_VERSION=3.6.0.slim.min IMPRESSJS_VERSION=1.1.0 -MERMAID_VERSION=v8.8.4 +MERMAID_VERSION=v8.9.2 SVGBOB_VERSION=0.5.0-alpha.6 -VERSION=0.54.3 -MOTTO=simple version update -WEB_VERSION=0.2 -PDF_VERSION=0.10 +VERSION=0.60.0 +MOTTO=rusty version +PDF_VERSION=0.11 STANDALONE_VERSION=0.5 FRIENDS_VERSION=0.5 A2SKETCH_VERSION=0.15 @@ -29,7 +28,8 @@ all: docker-compose.yaml develop.yaml ../markdeck images.built images.built: Makefile Dockerfile.* $(shell find markdeck/) markdeck/versions - docker-compose build \ + docker compose build \ + --no-rm --progress plain \ --build-arg MERMAID_VERSION=$(MERMAID_VERSION) \ --build-arg PANDOC_VERSION=$(PANDOC_VERSION) \ --build-arg JQUERY_VERSION=$(JQUERY_VERSION) \ @@ -57,48 +57,45 @@ push: all docker-compose.yaml: docker-compose.yaml.source Makefile cp docker-compose.yaml.source docker-compose.yaml - yq w -i docker-compose.yaml services.markdeck.image "arne/markdeck-pandoc:$(VERSION)" - yq w -i docker-compose.yaml services.markdeck.build.args.version "$(VERSION)" - yq w -i docker-compose.yaml services.markdeck.build.args.motto "$(MOTTO)" - yq w -i docker-compose.yaml services.web.image "arne/markdeck-liveserver:$(WEB_VERSION)" - yq w -i docker-compose.yaml services.pdf.image "arne/markdeck-decktape:$(PDF_VERSION)" - yq w -i docker-compose.yaml services.standalone.image "arne/markdeck-standalone:$(STANDALONE_VERSION)" - yq w -i docker-compose.yaml services.friends.image "arne/markdown-friends:$(FRIENDS_VERSION)" - yq w -i docker-compose.yaml services.a2sketch.image "arne/a2sketch:$(A2SKETCH_VERSION)" - yq w -i docker-compose.yaml services.jupyter.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" - yq w -i docker-compose.yaml services.jupyterlab.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" + yq eval '.services.markdeck.image = "arne/markdeck-pandoc:$(VERSION)"' -i docker-compose.yaml + yq eval '.services.markdeck.build.args.version = "$(VERSION)"' -i docker-compose.yaml + yq eval '.services.markdeck.build.args.motto = "$(MOTTO)"' -i docker-compose.yaml + yq eval '.services.pdf.image = "arne/markdeck-decktape:$(PDF_VERSION)"' -i docker-compose.yaml + yq eval '.services.standalone.image = "arne/markdeck-standalone:$(STANDALONE_VERSION)"' -i docker-compose.yaml + yq eval '.services.friends.image = "arne/markdown-friends:$(FRIENDS_VERSION)"' -i docker-compose.yaml + yq eval '.services.a2sketch.image = "arne/a2sketch:$(A2SKETCH_VERSION)"' -i docker-compose.yaml + yq eval '.services.jupyter.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i docker-compose.yaml + yq eval '.services.jupyterlab.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i docker-compose.yaml develop.yaml: develop.yaml.source Makefile cp develop.yaml.source develop.yaml - yq w -i develop.yaml services.markdeck.image "arne/markdeck-pandoc:$(VERSION)" - yq w -i develop.yaml services.web.image "arne/markdeck-liveserver:$(WEB_VERSION)" - yq w -i develop.yaml services.pdf.image "arne/markdeck-decktape:$(PDF_VERSION)" - yq w -i develop.yaml services.standalone.image "arne/markdeck-standalone:$(STANDALONE_VERSION)" - yq w -i develop.yaml services.friends.image "arne/markdown-friends:$(FRIENDS_VERSION)" - yq w -i develop.yaml services.a2sketch.image "arne/a2sketch:$(A2SKETCH_VERSION)" - yq w -i develop.yaml services.jupyter.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" - yq w -i develop.yaml services.jupyterlab.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" + yq eval '.services.markdeck.image = "arne/markdeck-pandoc:$(VERSION)"' -i docker-compose.yaml + yq eval '.services.pdf.image = "arne/markdeck-decktape:$(PDF_VERSION)"' -i docker-compose.yaml + yq eval '.services.standalone.image = "arne/markdeck-standalone:$(STANDALONE_VERSION)"' -i docker-compose.yaml + yq eval '.services.friends.image = "arne/markdown-friends:$(FRIENDS_VERSION)"' -i docker-compose.yaml + yq eval '.services.a2sketch.image = "arne/a2sketch:$(A2SKETCH_VERSION)"' -i docker-compose.yaml + yq eval '.services.jupyter.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i docker-compose.yaml + yq eval '.services.jupyterlab.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i docker-compose.yaml ../markdeck: docker-compose.yaml Makefile sed -n '/EODCF/,/EODCF/{/EODCF/d;s/..MARKDECK_USER/______/;p;}' ../markdeck > dc.tmp - yq w -i dc.tmp services.markdeck.image "arne/markdeck-pandoc:$(VERSION)" - yq w -i dc.tmp services.markdeck.build.args.version "$(VERSION)" - yq d -i dc.tmp services.markdeck.build - yq w -i dc.tmp services.web.image "arne/markdeck-liveserver:$(WEB_VERSION)" - yq d -i dc.tmp services.web.build - yq w -i dc.tmp services.pdf.image "arne/markdeck-decktape:$(PDF_VERSION)" - yq d -i dc.tmp services.pdf.build - yq w -i dc.tmp services.standalone.image "arne/markdeck-standalone:$(STANDALONE_VERSION)" - yq d -i dc.tmp services.standalone.build - yq w -i dc.tmp services.friends.image "arne/markdown-friends:$(FRIENDS_VERSION)" - yq d -i dc.tmp services.friends.build - yq w -i dc.tmp services.a2sketch.image "arne/a2sketch:$(A2SKETCH_VERSION)" - yq d -i dc.tmp services.jupyter.build - yq w -i dc.tmp services.jupyter.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" - yq d -i dc.tmp services.jupyterlab.build - yq w -i dc.tmp services.jupyterlab.image "arne/markdeck-jupyter:$(JUPYTER_VERSION)" + yq eval '.services.markdeck.image = "arne/markdeck-pandoc:$(VERSION)"' -i dc.tmp + yq eval '.services.markdeck.build.args.version = "$(VERSION)"' -i dc.tmp + yq eval 'del(.services.markdeck.build)' -i dc.tmp + yq eval 'del(.services.web.build)' -i dc.tmp + yq eval '.services.pdf.image = "arne/markdeck-decktape:$(PDF_VERSION)"' -i dc.tmp + yq eval 'del(.services.pdf.build)' -i dc.tmp + yq eval '.services.standalone.image = "arne/markdeck-standalone:$(STANDALONE_VERSION)"' -i dc.tmp + yq eval 'del(.services.standalone.build)' -i dc.tmp + yq eval '.services.friends.image = "arne/markdown-friends:$(FRIENDS_VERSION)"' -i dc.tmp + yq eval 'del(.services.friends.build)' -i dc.tmp + yq eval '.services.a2sketch.image = "arne/a2sketch:$(A2SKETCH_VERSION)"' -i dc.tmp + yq eval 'del(.services.jupyter.build)' -i dc.tmp + yq eval '.services.jupyter.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i dc.tmp + yq eval 'del(.services.jupyterlab.build)' -i dc.tmp + yq eval '.services.jupyterlab.image = "arne/markdeck-jupyter:$(JUPYTER_VERSION)"' -i dc.tmp sed -i.bak 's/______/\\$$MARKDECK_USER/' dc.tmp sed -i.bak -e '/ EODCF/r dc.tmp' -e '/ EODCF/,/EODCF/{/EODCF/p;d;}' ../markdeck rm -f dc.tmp* ../markdeck.bak diff --git a/src/docker-compose.yaml.source b/src/docker-compose.yaml.source index e81c1cf..cf8c064 100644 --- a/src/docker-compose.yaml.source +++ b/src/docker-compose.yaml.source @@ -2,13 +2,15 @@ version: "3.4" services: markdeck: build: - context: . + context: .. dockerfile: Dockerfile.pandoc args: version: 0.36 motto: the next version target: development image: arne/markdeck-pandoc:0.36 + ports: + - 8080:8080 volumes: - type: bind source: ./ @@ -17,19 +19,6 @@ services: - type: bind source: ./deck target: /target - web: - build: - context: . - dockerfile: Dockerfile.liveserver - image: arne/markdeck-liveserver:0.2 - ports: - - 8080:8080 - volumes: - - type: bind - source: ./deck - target: /target - depends_on: - - markdeck pdf: build: context: . @@ -42,7 +31,6 @@ services: shm_size: 1G depends_on: - markdeck - - web standalone: build: context: . @@ -53,7 +41,7 @@ services: source: ./deck target: /target depends_on: - - web + - markdeck friends: image: arne/markdown-friends:0.4 ports: diff --git a/src/live_server.rs b/src/live_server.rs index 916ac10..e1e6fd6 100644 --- a/src/live_server.rs +++ b/src/live_server.rs @@ -1,12 +1,5 @@ -//! Simple echo websocket server. -//! Open `http://localhost:8080/ws/index.html` in browser -//! or [python console client](https://github.com/actix/examples/blob/master/websocket/websocket-client.py) -//! could be used for testing. -//! blatantly copied from https://github.com/actix/examples/blob/master/websockets/websocket/src/main.rs - use std::collections::HashSet; -use std::panic; -use std::path::Path; +use std::path::PathBuf; use std::sync::mpsc::Receiver; use std::sync::Arc; use std::sync::RwLock; @@ -16,39 +9,82 @@ use std::time::{Duration, Instant}; use actix::prelude::*; use actix_files as fs; use actix_web::web::Data; -use actix_web::Responder; use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use actix_web_actors::ws; - -async fn get_sessions(r: HttpRequest, _stream: web::Payload) -> impl Responder { - let mydata = r - .app_data::>>>() - .expect("no mydata found!"); - format!("aha: {:#?}", mydata.get_ref()) -} +use log::{debug, info}; +use rust_embed::RustEmbed; /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); /// How long before lack of client response causes a timeout const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); +#[derive(Message, Debug, Clone)] +#[rtype(result = "()")] +pub enum Trigger { + Reload, + StartRerendering, + Shutdown, +} + +impl Handler for MyWebSocket { + type Result = (); + + fn handle(&mut self, msg: Trigger, ctx: &mut Self::Context) -> Self::Result { + debug!("handling trigger {:#?}", msg); + match msg { + Trigger::Reload => ctx.text("reload"), + Trigger::StartRerendering => ctx.text("start_rerendering"), + Trigger::Shutdown => ctx.text("shutdown"), + } + } +} + +#[actix_web::main] +pub async fn start_live_server( + docroot: PathBuf, + port: u32, + rx: Receiver, +) -> std::io::Result<()> { + let concierge = Arc::new(RwLock::new(Concierge::new())); + let concierge_extern = concierge.clone(); + + thread::spawn(move || { + loop { + let msg = rx.recv().unwrap(); // when sender hangs up, recv() returns an error, thus this thread panics here, good + if let Ok(mut c) = concierge_extern.write() { + c.trigger(msg); + } + } + }); + + HttpServer::new(move || { + App::new() + .data(concierge.clone()) + .data(docroot.clone()) + .wrap(middleware::Logger::default()) + .service(web::resource("/ws").route(web::get().to(ws_index))) + .service(web::resource("/").route(web::get().to(|| { + HttpResponse::Found() + .header("LOCATION", "index.html") + .finish() + }))) + .service(web::resource("/index.html").route(web::get().to(patch_response))) + .service(fs::Files::new("/", docroot.clone())) + }) + // .bind(format!("127.0.0.1:{}", port))? + .bind(format!("0.0.0.0:{}", port))? + .run() + .await +} + async fn ws_index(r: HttpRequest, stream: web::Payload) -> Result { let concierge = r .app_data::>>>() .expect("no concierge found!"); let mws = MyWebSocket::new(concierge.get_ref().clone()); - let (addr, res) = ws::start_with_addr(mws, &r, stream)?; + let (_addr, res) = ws::start_with_addr(mws, &r, stream)?; - /* - let data_arc = r - .app_data::>>>() - .expect("TODO") - .get_ref(); - println!("{:#?}", data_arc); - if let Ok(mut data) = data_arc.write() { - data.sockets.insert(addr); - } - */ Ok(res) } @@ -66,11 +102,12 @@ impl MyWebSocket { } } } + impl Actor for MyWebSocket { type Context = ws::WebsocketContext; fn started(&mut self, ctx: &mut Self::Context) { - println!("Websocket started"); + debug!("Websocket started"); let addr = ctx.address(); if let Ok(mut concierge) = self.concierge.write() { concierge.sockets.insert(addr); @@ -79,7 +116,7 @@ impl Actor for MyWebSocket { } fn stopped(&mut self, ctx: &mut Self::Context) { - println!("Websocket stopped"); + debug!("Websocket stopped"); let addr = ctx.address(); if let Ok(mut concierge) = self.concierge.write() { concierge.sockets.remove(&addr); @@ -87,27 +124,6 @@ impl Actor for MyWebSocket { } } -#[derive(Message, Debug, Clone)] -#[rtype(result = "()")] -pub enum Trigger { - Reload, - RefreshCss, - Shutdown, -} - -impl Handler for MyWebSocket { - type Result = (); - - fn handle(&mut self, msg: Trigger, ctx: &mut Self::Context) -> Self::Result { - println!("handling trigger {:#?}", msg); - match msg { - Trigger::Reload => ctx.text("reload"), - Trigger::RefreshCss => ctx.text("refreshcss"), - Trigger::Shutdown => ctx.text("shutdown"), - } - } -} - /// Handler for `ws::Message` impl StreamHandler> for MyWebSocket { fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { @@ -137,7 +153,7 @@ impl MyWebSocket { ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { // check client heartbeats if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { - println!("Websocket Client heartbeat failed, disconnecting!"); + info!("Websocket Client heartbeat failed, disconnecting!"); ctx.stop(); return; } @@ -165,120 +181,23 @@ impl Concierge { } } -#[actix_web::main] -pub async fn start_live_server( - docroot: &'static Path, - rx: Receiver, -) -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); - env_logger::init(); - - let concierge = Arc::new(RwLock::new(Concierge::new())); - let concierge_extern = concierge.clone(); - - thread::spawn(move || { - loop { - let msg = rx.recv().unwrap(); // TODO - if let Ok(mut c) = concierge_extern.write() { - c.trigger(msg); - } - } - }); - - HttpServer::new(move || { - App::new() - .data(concierge.clone()) - .data(docroot) - .wrap(middleware::Logger::default()) - .service(web::resource("/ws").route(web::get().to(ws_index))) - .service(web::resource("/").route(web::get().to(|| { - HttpResponse::Found() - .header("LOCATION", "index.html") - .finish() - }))) - .service(web::resource("/index.html").route(web::get().to(patch_response))) - // .service(web::resource("/refresh").route(web::get().to(refresh))) - // .service(web::resource("/reload").route(web::get().to(reload))) - // .route("/sessions", web::get().to(get_sessions)) - .service(fs::Files::new("/", docroot)) - }) - .bind("127.0.0.1:8081")? - .run() - .await -} - -/* -async fn reload(r: HttpRequest, _stream: web::Payload) -> Result { - println!("triggering reload"); - if let Ok(mut concierge) = r - .app_data::>>>() - .expect("no state?!") - .write() - { - // concierge.address().do_send(Trigger::Reload); - concierge.trigger(Trigger::Reload); - } - Ok(actix_web::HttpResponse::Ok().body("")) -} - -async fn refresh(r: HttpRequest, _stream: web::Payload) -> Result { - println!("triggering refresh"); - if let Ok(mut concierge) = r - .app_data::>>>() - .expect("no state?!") - .write() - { - // concierge.address().do_send(Trigger::Reload); - concierge.trigger(Trigger::RefreshCss); - } - Ok(actix_web::HttpResponse::Ok().body("")) -} -*/ +#[derive(RustEmbed)] +#[folder = "src/markdeck/.live_server"] +struct Assets; async fn patch_response(r: HttpRequest, _stream: web::Payload) -> Result { let body = std::fs::read(format!( "{}/index.html", - r.app_data::>().expect("no docroot?").display() + r.app_data::>() + .expect("no docroot?") + .display() ))?; + let patch = + Assets::get("patch-html.html").expect("cannot find html snippet to patch index.html!"); let res = actix_web::HttpResponse::Ok().body(format!( - "{}\n\n\n{}", + "{}\n\n\n{}", // horray to the lazy html renderer String::from_utf8_lossy(&body), - r#" - - - "# + std::str::from_utf8(&patch)? )); Ok(res) } diff --git a/src/main.rs b/src/main.rs index c146670..5811589 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,82 +1,232 @@ +#[macro_use] +extern crate clap; + use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; use std::sync::mpsc::Sender; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use std::env; +use log::{debug, info, warn}; use glob::glob; use rust_embed::RustEmbed; use rusync::{sync, Syncer}; use serde_json::{from_str, Map, Value}; - use anyhow::Error; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; -use std::sync::mpsc::channel; -use std::time::Duration; use live_server::Trigger; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; // TODO sanity check // TODO check for following tools: // TODO brew install pandoc // TODO brew install qrencode // TODO brew install java -// TODO brew install gradle -// TODO brew install curl +// TODO ... mathjax-pandoc-filter (incl npm) +// TODO ... svgbob // TODO ... graphviz // TODO ... vegalite // TODO docker run -d -p 22753:22753 arne/a2sketch:0.15 mod live_server; +mod sass; +mod markdown_pp; #[derive(RustEmbed)] -#[folder = "src/markdeck"] +#[folder = "src/docroot/main"] struct Assets; +fn print_versions() { + let result = Command::new("pandoc") + .arg("--version") + .output() + .expect("failed to run command"); + match result.status.code() { + Some(code) => { + if code != 0 { + warn!("exited with status code: {}", code) + } + } + None => warn!("Process terminated by signal"), + } + if let Some(version) = String::from_utf8_lossy(&result.stdout).lines().next() { + info!("pandoc: {}", version); + } else { + warn!("pandoc: cannot retrieve version! Is it installed?") + } +} + +arg_enum! { + #[derive(Debug)] + enum WatcherType { + Recommended, + CompareReference, + } +} + fn main() -> Result<(), Error> { - let source_path = Path::new("."); - let target_path: &'static Path = Path::new("argh"); + let matches = clap_app!(markdeck => + (version: "0.60.0") + (author: "Arne Hilmann ") + (about: "Does awesome things") + (@arg port: --port +takes_value default_value("8080") "port of web server") + (@arg source: --source +takes_value default_value(".") "path of source folder") + (@arg target: --target +takes_value default_value("deck") "path of target folder") + (@arg watcher: --watcher +takes_value +case_insensitive possible_values(&WatcherType::variants()) "watcher, detects source file changes") + (@arg watch_delay: --watch_delay +takes_value default_value("500") "delay between watch calls, in millis") + (@subcommand start => + (about: "starts specific components") + ) + (@subcommand dev => + (about: "develop some features") + + ) + ) + .get_matches(); + + match env::var("RUST_LOG") { + Ok(_) => {}, + Err(_) => env::set_var( + "RUST_LOG", + "actix_server=info,actix_web=warn,live_server=debug,markdeck=debug", + ) + } + env_logger::init(); + + print_versions(); + + // let source_path = Path::new("."); + let source_path = matches.value_of("source").unwrap(); // TODO unwrap + let source_path = PathBuf::from(source_path); + let target_path = matches.value_of("target").unwrap(); // TODO unwrap + let target_path = PathBuf::from(target_path); + let port: u32 = matches.value_of("port").unwrap().parse::().unwrap(); // TODO unwrap + let watcher_type = + value_t!(matches.value_of("watcher"), WatcherType).unwrap_or(WatcherType::Recommended); + let watch_delay = value_t_or_exit!(matches.value_of("watch_delay"), u64); let sources = fetch_sources(&source_path); + if let Some(_matches) = matches.subcommand_matches("dev") { + markdown_pp::preprocess_file(PathBuf::from("slides.md"))?; + std::process::exit(0); + } + + // render(&source_path, &sources, &target_path)?; + let running = Arc::new(AtomicBool::new(true)); let running_watcher = running.clone(); let (tx, rx) = channel(); let tx2 = tx.clone(); - let live_server = std::thread::spawn(move || live_server::start_live_server(&target_path, rx)); - let watcher = std::thread::spawn(move || { - watch(&source_path, &sources, &target_path, running_watcher, tx) + let target_path_for_live_server = target_path.clone(); + let live_server = std::thread::spawn(move || { + live_server::start_live_server(target_path_for_live_server, port, rx) + }); + let watcher = std::thread::spawn(move || match watcher_type { + WatcherType::CompareReference => compare_watch( + &source_path, + &sources, + &target_path, + "index.html", + watch_delay, + running_watcher, + tx, + ), + _ => watch( + &source_path, + &sources, + &target_path, + watch_delay, + running_watcher, + tx, + ), }); ctrlc::set_handler(move || { - println!("\nctrl-c received"); + info!("\nctrl-c received"); running.store(false, Ordering::Relaxed); - tx2.send(Trigger::Shutdown).unwrap(); - println!("all stopped"); + tx2.send(Trigger::Shutdown).unwrap(); // TODO unwrap + info!("stopping all threads"); }) .expect("Error setting Ctrl-C handler"); - live_server.join().unwrap()?; - watcher.join().unwrap()?; + live_server.join().unwrap()?; // TODO unwrap + watcher.join().unwrap()?; // TODO unwrap + + Ok(()) +} + +fn compare_watch( + source_path: &PathBuf, + sources: &[PathBuf], + target_path: &Path, + target_file: &str, + watch_delay: u64, + running: Arc, + sender: Sender, +) -> Result<(), Error> { + info!("starting watcher"); + + let mut reference = PathBuf::from(target_path); + reference.push(target_file); + + let mut files_count; + let mut last_files_count = 0; + let mut rerender; + + while running.load(Ordering::Relaxed) { + let r_m = reference.metadata()?.modified()?; + rerender = false; + files_count = 0; + + for s in interesting_files(&source_path) { + files_count += 1; + let s_m = s.metadata()?.modified()?; + if s_m > r_m { + info!("change detected in {}, rerendering!", s.display()); + rerender = true; + break; + } + } + if files_count != last_files_count { + info!("number of source files changed, rerendering!"); + rerender = true; + } + if rerender { + sender.send(Trigger::StartRerendering)?; + match render(&source_path, &sources, &target_path) { + Ok(_) => sender.send(Trigger::Reload)?, + Err(e) => warn!("{:?}", e), + } + } + + last_files_count = files_count; + std::thread::sleep(Duration::from_millis(watch_delay)); + } + info!("shutting down watcher"); Ok(()) } fn watch( - source_path: &Path, + source_path: &PathBuf, sources: &[PathBuf], target_path: &Path, + watch_delay: u64, running: Arc, sender: Sender, ) -> Result<(), Error> { - render(&source_path, &sources, &target_path)?; + info!("starting watcher"); let (tx, rx) = channel(); - let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(1))?; - watcher.watch("assets/", RecursiveMode::Recursive)?; + let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_millis(watch_delay))?; + watcher.watch("assets/", RecursiveMode::Recursive)?; // TODO regard source_path // TODO handle changing sources for s in sources { watcher.watch(s, RecursiveMode::NonRecursive)?; @@ -84,19 +234,9 @@ fn watch( while running.load(Ordering::Relaxed) { match rx.recv_timeout(Duration::from_millis(500)) { Ok(notify::DebouncedEvent::Write(_)) => { - let content = Assets::get("assets/markdeck/css/when-rerendering.css") - .expect("cannot convert data to utf8"); - fs::write( - format!("{}/assets/css/rerendering.css", target_path.display()), - content.as_ref(), - )?; - sender.send(Trigger::RefreshCss)?; + sender.send(Trigger::StartRerendering)?; match render(&source_path, &sources, &target_path) { Ok(_) => { - fs::write( - format!("{}/assets/css/rerendering.css", target_path.display()), - "", - )?; sender.send(Trigger::Reload)?; } Err(e) => println!("{:?}", e), @@ -106,14 +246,15 @@ fn watch( Err(_) => {} } } - println!("shutting down watcher"); + info!("shutting down watcher"); Ok(()) } -fn render(source_path: &Path, sources: &[PathBuf], target_path: &Path) -> Result<(), Error> { +fn render(source_path: &PathBuf, sources: &[PathBuf], target_path: &Path) -> Result<(), Error> { + let start = Instant::now(); for source in sources.iter() { - println!("sources: {}", source.display()); + info!("sources: {}", source.display()); } sync(&source_path, "assets", &target_path)?; @@ -124,40 +265,44 @@ fn render(source_path: &Path, sources: &[PathBuf], target_path: &Path) -> Result sync_static("explain.html", &target_path)?; sync_static("template-", &target_path)?; - let metadata = fetch_metadata(&target_path)?; - println!( - "variant: {}", - metadata["variant"] - .as_str() - .expect("cannot access variant as str") - ); - - render_deck(&source_path, &sources, &target_path, &metadata)?; + render_deck(&source_path, &sources, &target_path)?; - // devserver_lib::run(&"localhost", 8080, "", /*Auto-reload:*/ true ); // Runs forever serving the current folder on http://localhost:8080 + let duration = start.elapsed(); + info!("rendering took {} msecs", duration.as_millis()); Ok(()) } +fn interesting_files(source_path: &PathBuf) -> Vec { + glob(&format!("{}/*[a-z]*.md", source_path.display())) + .expect("Failed to read md glob pattern") + .chain( + glob(&format!("{}/assets/**/*", source_path.display())) + .expect("Failed to read assets glob pattern"), + ) + .filter_map(|f| f.ok()) + .collect() +} + fn fetch_sources(source_path: &Path) -> Vec { glob(&format!("{}/*[a-z]*.md", source_path.display())) - .expect("Failed to read glob pattern") - .map(|x| x.expect("mmh")) - .collect::>() + .expect("Failed to read md glob pattern") + .filter_map(|f| f.ok()) + .collect() } /* if pandoc \ -f markdown+yaml_metadata_block \ -t html \ - ${table_of_contents:+ --toc} \ + ${table_of_contents:+ --toc} \ --no-highlight \ --wrap=preserve \ --standalone \ ${template:+ --template=$template} \ --section-divs \ --filter mathjax-pandoc-filter \ - $([[ -e $SHORTCUT_FILTER ]] && echo --lua-filter $SHORTCUT_FILTER) \ + $([[ -e $SHORTCUT_FILTER ]] && echo --lua-filter $SHORTCUT_FILTER) \ --lua-filter /markdeck/lib/skip-slide-filter.lua \ --lua-filter /markdeck/lib/render-asciiart-filter.lua \ --lua-filter /markdeck/lib/render-emojis-filter.lua \ @@ -169,56 +314,75 @@ if pandoc \ /markdeck/defaults.yaml /target/slides.combined.md.txt 2>&1 | tee /tmp/pandoc.output; */ -fn sassc(source_path: &Path, scss_file: &str, target_path: &Path) -> anyhow::Result<()> { - let sass = grass::from_path( - format!("{}/{}", source_path.display(), scss_file).as_ref(), - &grass::Options::default(), - ); - // TODO calc suffix: scss -> css - if let Ok(sass) = sass { - fs::write(format!("{}/{}", target_path.display(), scss_file), sass)?; - } - Ok(()) -} - fn render_deck( source_path: &Path, source_files: &[PathBuf], target_path: &Path, - metadata: &Map, ) -> Result<(), Error> { + info!("render_deck"); let mut slides_combined = PathBuf::from(target_path); slides_combined.push("slides.combined.md.txt"); if slides_combined.exists() { fs::remove_file(&slides_combined)?; } + info!("combining source files"); let mut slides_combined = OpenOptions::new() .append(true) .create(true) .open(slides_combined)?; for source_file in source_files.iter() { + info!("source file: {}", source_file.display()); let contents = fs::read(&source_file)?; + let contents = markdown_pp::preprocess(contents)?; slides_combined.write_all(&contents)?; slides_combined.write_all(b"\n\n")?; } - let theme = metadata["themes"].as_str().unwrap(); - let sass = grass::from_path( - format!("{}/themes/{}/css/slides.scss", source_path.display(), theme).as_ref(), - &grass::Options::default(), + + let metadata = fetch_metadata(&target_path)?; + info!( + "variant: {}", + metadata["variant"] + .as_str() + .expect("cannot access variant as str") ); - if let Ok(sass) = sass { - fs::write( - format!("{}/themes/{}/css/slides.css", target_path.display(), theme), - sass, + + let variant = metadata["variant"].as_str().unwrap_or(""); + let toc = metadata["table_of_contents"].as_bool().unwrap_or(false); + let themes = metadata["themes"].as_str().unwrap_or(""); + + info!("compiling scss -> css"); + sass::sassc(source_path, "assets/css/slides.scss", target_path)?; + sass::sassc( + source_path, + &format!("assets/css/slides.{}.scss", variant), + target_path, + )?; + for theme in themes.split(&[' ', ','][..]) { + sass::sassc( + source_path, + &format!("themes/{}/css/slides.scss", theme), + target_path, + )?; + sass::sassc( + source_path, + &format!("themes/{}/css/slides.{}.scss", theme, variant), + target_path, )?; } + info!("rendering slides now"); + + let mut template_path = std::env::current_dir()?; + template_path.push(target_path); + template_path.push(".markdeck"); + template_path.push(format!("template-{}.html", variant)); + let mut render_cmd = Command::new("pandoc"); render_cmd .current_dir(&target_path) .arg("-f") - .arg("markdown+yaml_metadata_block") + .arg("markdown+yaml_metadata_block+tex_math_dollars") .arg("-t") .arg("html") .arg("--standalone") @@ -239,48 +403,63 @@ fn render_deck( .arg(".markdeck/inline-svg.lua") .arg("--lua-filter") .arg(".markdeck/icons.lua") - .arg(format!( - "--template={}/{}/template-{}.html", - std::env::current_dir()?.display(), - target_path.display(), - metadata["variant"] - .as_str() - .expect("cannot access variant in metadata") - )) + .arg("--mathjax") + .arg(format!("--template={}", template_path.display())); + + let shortcut_filter = format!(".markdeck/{}-shortcut-filter.lua", variant); + if Path::new(&shortcut_filter).exists() { + render_cmd.arg("--lua-filter").arg(shortcut_filter); + } + + if toc { + render_cmd.arg("--toc"); + } + + render_cmd .arg(".markdeck/defaults.yaml") .arg("slides.combined.md.txt"); - println!("{:#?}", render_cmd); + debug!("render cmd: {:#?}", render_cmd); let output = render_cmd.output().expect("failed to run pandoc"); - println!("{}", output.status); - - // std::io::stdout().write_all(&output.stdout).unwrap(); - // std::io::stderr().write_all(&output.stderr).unwrap(); + if output.status.success() { + for line in String::from_utf8(output.stdout)?.lines() { + debug!("{}", line); + } + for line in String::from_utf8(output.stderr)?.lines() { + debug!("{}", line); + } + info!("slides rendered successfully"); + } else { + for line in String::from_utf8(output.stdout)?.lines() { + info!("{}", line); + } + for line in String::from_utf8(output.stderr)?.lines() { + warn!("{}", line); + } + warn!("an error occured! {:?}", output.status.code()); + } Ok(()) } fn fetch_metadata(target_path: &Path) -> Result, Error> { - // TODO sourcepath, sources + info!("fetch_metadata"); + let mut metadata_cmd = Command::new("pandoc"); - metadata_cmd.arg(format!( - "--template={}/{}/.markdeck/metadata.template", - std::env::current_dir()?.display(), - &target_path.display() - )); - metadata_cmd.arg(format!( - "{}/.markdeck/defaults.yaml", - &target_path.display() - )); - metadata_cmd.arg("slides.md"); + metadata_cmd + .current_dir(&target_path) + .arg("--template=.markdeck/metadata.template") + .arg(".markdeck/defaults.yaml") + .arg("slides.combined.md.txt"); + debug!("metadata cmd: {:#?}", metadata_cmd); let metadata = metadata_cmd.output().expect("failed to run pandoc"); - println!("raw: {:#?}", metadata); let metadata: Value = from_str(std::str::from_utf8(&metadata.stdout)?)?; - println!("json: {:#?}", metadata); let metadata: Map = metadata .as_object() .expect("cannot convert metadata") .to_owned(); - println!("map: {:#?}", metadata); + for pair in metadata.iter() { + debug!("metadata: {} -> {}", pair.0, pair.1); + } Ok(metadata) } @@ -307,7 +486,11 @@ fn sync_static(source_path: &str, target_root: &Path) -> Result<(), Error> { Ok(()) } -fn sync(source_root: &Path, source_path: &str, target_root: &Path) -> Result { +fn sync( + source_root: &PathBuf, + source_path: &str, + target_root: &Path, +) -> Result { let console_info = rusync::ConsoleProgressInfo::new(); let options = rusync::SyncOptions::default(); diff --git a/src/markdown_pp.rs b/src/markdown_pp.rs new file mode 100644 index 0000000..89ec300 --- /dev/null +++ b/src/markdown_pp.rs @@ -0,0 +1,87 @@ +// !INCLUDECODE "mds/cli/dockerfile.sh" ({.bash .fragment .hl-code ln-start-from=15}), 15:18 +//

+// !INCLUDE "mds/introduction.md"
+
+use regex::Regex;
+use std::fs;
+use std::io::Result;
+use std::path::PathBuf;
+
+use log::{debug, warn};
+
+pub fn preprocess(input: Vec) -> Result> {
+    debug!("preprocessing");
+
+    let includecode_re = Regex::new("!INCLUDECODE \"(.*)\" [(](.*)[)](.*)$").unwrap();
+    let range_re = Regex::new(", *(\\d+)?:?(\\d+)?").unwrap();
+
+    let mut content = input;
+    let mut run = true;
+    while run {
+        run = false;
+        let content_str = String::from_utf8(content).expect("not valid UTF-8");
+        content = Vec::new();
+        for line in content_str.lines() {
+            if line.starts_with("!INCLUDE ") {
+                let filename = line.replace("!INCLUDE ", "");
+                let filename = filename.trim().trim_matches('"');
+                let mut included_content = fs::read(filename)?;
+                content.append(&mut included_content);
+                run = true;
+            } else if line.starts_with("!INCLUDECODE ") {
+                // !INCLUDECODE "markdeck" ({.bash .fragment .hl-code ln-start-from=15}), 1:3
+                // ```{.bash .hl-code ln-start-from=23}
+                let caps = includecode_re.captures(line).unwrap();
+                content.extend(b"\n```");
+                content.extend(caps.get(2).map_or("", |m| m.as_str()).as_bytes());
+                content.push(b'\n');
+                let filename = caps.get(1).unwrap().as_str();
+                // TODO include selected lines only
+                // TODO add optional highlight lines and/or fragmentsc
+                let line_range = caps.get(3).map_or("", |m| m.as_str());
+                debug!("line range: {:#?}", line_range);
+                let mut start = "1";
+                let mut end = "9999";
+                if line_range != "" {
+                    match range_re.captures(line_range) {
+                        Some(range_caps) => {
+                            start = range_caps.get(1).map_or("1", |m| m.as_str());
+                            end = range_caps.get(2).map_or(start, |m| m.as_str());
+                        }
+                        None => warn!("{} not a valid line_range", line_range),
+                    };
+                }
+
+                let start = start.parse::().unwrap() - 1;
+                debug!("start: {}", start);
+
+                let end = end.parse::().unwrap() - 1;
+                debug!("end: {}", end);
+
+                let included_content = fs::read(filename)?;
+                for (nr, line) in std::str::from_utf8(&included_content)
+                    .expect("not utf8")
+                    .lines()
+                    .enumerate()
+                {
+                    if nr >= start && nr <= end {
+                        content.extend_from_slice(line.as_bytes());
+                        content.push(b'\n');
+                    }
+                }
+                content.extend(b"```\n");
+            } else {
+                content.extend_from_slice(line.as_bytes());
+            }
+            content.push(b'\n');
+        }
+    }
+    // debug!("{}", std::str::from_utf8(&content).expect("TODO"));
+    Ok(content)
+}
+
+pub fn preprocess_file(f: PathBuf) -> Result> {
+    debug!("preprocessing {}", f.display());
+    let content = fs::read(f)?;
+    preprocess(content)
+}
diff --git a/src/sass.rs b/src/sass.rs
new file mode 100644
index 0000000..f9dc07b
--- /dev/null
+++ b/src/sass.rs
@@ -0,0 +1,24 @@
+use std::fs::{self};
+use std::path::Path;
+
+pub fn sassc(source_folder: &Path, scss_file: &str, target_folder: &Path) -> anyhow::Result<()> {
+    let mut source_path = source_folder.to_path_buf();
+    source_path.push(scss_file);
+
+    if !source_path.exists() {
+        return Ok(());
+    }
+
+    let sass_result = grass::from_path(
+        source_path.to_str().expect("not utf-8?"),
+        &grass::Options::default(),
+    );
+    let mut target_path = target_folder.to_path_buf();
+    target_path.push(scss_file);
+    target_path.set_extension("css");
+    match sass_result {
+        Ok(css) => fs::write(target_path, css)?,
+        Err(_) => fs::write(target_path, "")?,
+    }
+    Ok(())
+}