aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorottjk <joshott16@gmail.com>2024-01-20 02:01:41 -0500
committerottjk <joshott16@gmail.com>2024-01-20 02:01:41 -0500
commitd030712eeee03ad00f464a09af15229bca05ef50 (patch)
treeff918971dc25efd3dc0a0ca47a295db6b9c46c38
downloadlectern-d030712eeee03ad00f464a09af15229bca05ef50.tar.gz
lectern-d030712eeee03ad00f464a09af15229bca05ef50.zip
initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock584
-rw-r--r--Cargo.toml14
-rw-r--r--README.md3
-rw-r--r--src/lecture.rs91
-rw-r--r--src/main.rs271
6 files changed, 964 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..efc45de
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,584 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "lectern"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "dirs",
+ "regex",
+ "serde",
+ "tinytemplate",
+ "toml",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "ryu"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
+
+[[package]]
+name = "serde"
+version = "1.0.195"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.195"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
+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.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinytemplate"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
+[[package]]
+name = "winnow"
+version = "0.5.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
+dependencies = [
+ "memchr",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..5fe890d
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "lectern"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "4.4.16", features = ["derive"] }
+dirs = "5.0.1"
+regex = "1.10.2"
+serde = { version = "1.0.195", features = ["derive"] }
+tinytemplate = "1.2.1"
+toml = "0.8.8"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3dd5ca8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# lectern
+
+My lecture note manager. homework management coming soon tm
diff --git a/src/lecture.rs b/src/lecture.rs
new file mode 100644
index 0000000..462fd86
--- /dev/null
+++ b/src/lecture.rs
@@ -0,0 +1,91 @@
+use std::{
+ fs,
+ io,
+ path::PathBuf,
+};
+use tinytemplate::TinyTemplate;
+use regex::Regex;
+
+use crate::{Course, CourseContext, Config};
+
+fn init_lecture(course: &Course, config: &Config) {
+ let template_stream = fs::read(&config.template)
+ .expect("Couldn't open template.");
+ let template_string = String::from_utf8_lossy(&template_stream);
+
+ let mut template = TinyTemplate::new();
+ template.add_template("main", &template_string)
+ .expect("Failed initializing template.");
+
+ let context = CourseContext::from(&course, &config);
+
+ let rendered = template.render("main", &context)
+ .expect("Failed applying template");
+
+ let lecture_directory = config.root.join(&course.semester).join(&course.name)
+ .join("lecture");
+ fs::write(lecture_directory.join("main.tex"), rendered)
+ .expect("Failed making main.tex for course.");
+}
+
+fn detect_lessons(path: &PathBuf) -> io::Result<Vec<u32>> {
+ let mut lesson_numbers: Vec<u32> = Vec::new();
+ let dir_iter = fs::read_dir(path)?;
+ let pattern = Regex::new(r"les(\d+)\.tex").unwrap();
+
+ for entry in dir_iter {
+ let entry_path = entry?.path();
+ let name = entry_path.file_name().unwrap().to_string_lossy();
+ if let Some(caps) = pattern.captures(&name) {
+ let lesson_number = caps[1].parse::<u32>().unwrap();
+ lesson_numbers.push(lesson_number);
+ }
+ }
+
+ lesson_numbers.sort();
+
+ return Ok(lesson_numbers);
+}
+
+fn update_main(main_path: &PathBuf, new_lessons: String) -> io::Result<()> {
+ let main = fs::read(main_path)?;
+ let pattern = Regex::new(r"% start lessons\n( {4}\\input\{les(\d+)\.tex\}\n)* {4}% end lessons")
+ .unwrap();
+ let fmt_lessons = format!("% start lessons\n{} %end lessons", new_lessons);
+ let main_string = String::from_utf8_lossy(&main);
+ let new_main = pattern.replace(&main_string, fmt_lessons);
+ fs::write(main_path, new_main.as_bytes())?;
+
+ Ok(())
+}
+
+pub fn new_lesson(course: &Course, config: &Config) -> String {
+ let lecture_directory = config.root.join(&course.semester).join(&course.name)
+ .join("lecture");
+
+ if !lecture_directory.try_exists().unwrap() {
+ fs::create_dir_all(&lecture_directory).unwrap();
+ init_lecture(course, config);
+ }
+
+ let mut lessons = detect_lessons(&lecture_directory)
+ .expect("Could not make sense of the lesson situation.");
+ let new_lesson = lessons.last().unwrap_or(&0).clone() + 1;
+
+ let lesson_file = format!("les{new_lesson}.tex");
+ let lesson_path = lecture_directory.join(lesson_file.clone());
+ fs::write(&lesson_path, format!("\\lesson{{{new_lesson}}}{{}}\n\n").as_bytes())
+ .expect("Unable to write to lesson file.");
+
+ lessons.push(new_lesson);
+
+ let mut lessons_string = String::new();
+
+ for num in lessons {
+ lessons_string.push_str(format!(" \\input{{les{num}.tex}}\n").as_str());
+ }
+
+ update_main(&lecture_directory.join("main.tex"), lessons_string)
+ .expect("Unable to update main.tex");
+ return lesson_file;
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..847d096
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,271 @@
+use std::{
+ path::PathBuf,
+ io::{self, Write},
+ process::{Command, Stdio},
+ collections::HashMap,
+ fs,
+};
+use lecture::new_lesson;
+use serde::{Deserialize, Serialize};
+use clap::{Parser, Subcommand, Args};
+
+mod lecture;
+
+#[derive(Parser)]
+struct Cli {
+ #[command(subcommand)]
+ command: Commands,
+
+ /// Configuration file location
+ #[arg(short, long)]
+ config: Option<PathBuf>,
+}
+
+#[derive(Subcommand)]
+enum Commands {
+ /// Initialize a course
+ Init(InitArgs),
+
+ /// Open notes for a course
+ Open(OpenArgs),
+}
+
+#[derive(Args)]
+struct InitArgs {
+ /// Course prefix and number
+ name: String,
+
+ /// Course title
+ title: String,
+
+ /// Professor's name
+ prof: String,
+
+ /// Semester id for grouping courses
+ semester: String,
+}
+
+#[derive(Args)]
+struct OpenArgs {
+ /// Course prefix and number
+ name: Option<String>,
+}
+
+#[derive(Deserialize, Serialize)]
+struct Config {
+ root: PathBuf,
+ template: PathBuf,
+}
+
+#[derive(Deserialize, Serialize)]
+struct Course {
+ name: String,
+ title: String,
+ prof: String,
+ semester: String,
+}
+
+#[derive(Serialize)]
+struct CourseContext<'a> {
+ name: &'a String,
+ title: &'a String,
+ prof: &'a String,
+ semester: &'a String,
+ notebook: String,
+}
+
+impl<'a> CourseContext<'a> {
+ fn from(course: &'a Course, config: &'a Config) -> CourseContext<'a> {
+ let notebook = config.root.to_str()
+ .unwrap().to_owned();
+
+ CourseContext {
+ name: &course.name,
+ title: &course.title,
+ prof: &course.prof,
+ semester: &course.semester,
+ notebook,
+ }
+ }
+}
+
+fn resolve_home(path: &mut PathBuf) {
+ if path.starts_with("~") {
+ let temp = path.strip_prefix("~").unwrap();
+ *path = dirs::home_dir().expect("Could not resolve home directory.")
+ .join(temp);
+ }
+}
+
+fn create_config(path: &PathBuf) -> Config {
+ let mut root: PathBuf;
+ let mut input = String::new();
+
+ print!("Where should lecture notes go? ");
+ io::stdout().flush().unwrap();
+ io::stdin()
+ .read_line(&mut input)
+ .expect("Failed to read user input.");
+
+ root = PathBuf::from(input.trim());
+ resolve_home(&mut root);
+
+ let template = path.join("lecture_template.tex");
+ let config = Config { root, template };
+
+ let toml = toml::to_string(&config)
+ .expect("Unable to convert config struct to toml string.");
+ fs::write(path.join("config.toml"), toml)
+ .expect("Unable to initialize config.");
+
+ return config;
+}
+
+fn read_config(path: &PathBuf) -> Config {
+ let file_path = path.join("config.toml");
+
+ if !file_path.try_exists().unwrap() {
+ return create_config(&path);
+ }
+
+ let file = fs::read_to_string(file_path)
+ .expect("Error reading config file.");
+ return toml::from_str(&file)
+ .expect("Error parsing config file.");
+}
+
+fn get_courses(path: &PathBuf) -> HashMap<String, Course> {
+ let file_path = path.join("courses.toml");
+ let file = fs::read_to_string(file_path)
+ .unwrap_or(String::new());
+
+ return toml::from_str(&file).unwrap();
+}
+
+fn save_courses(courses: &HashMap<String, Course>, path: &PathBuf) {
+ let toml = toml::to_string(courses)
+ .expect("Unable to convert courses to toml string.");
+ fs::write(path.join("courses.toml"), toml)
+ .expect("Unable save courses information.");
+}
+
+fn pick_course<'a>(courses: &'a HashMap<String, Course>) -> Result<&'a Course, &'static str> {
+ let mut options = String::new();
+ let courses_seq: Vec<&Course> = courses.values().collect();
+ for course in &courses_seq {
+ options.push_str(&course.name.to_uppercase());
+ options.push_str(": ");
+ options.push_str(&course.title);
+ options.push_str("\n");
+ }
+
+ let course_idx = rofi_picker("Courses", options)?;
+
+ return Ok(&courses_seq[course_idx as usize]);
+}
+
+fn rofi_picker(title: &'static str, input: String) -> Result<u32, &'static str> {
+ let mut rofi = Command::new("rofi")
+ .args(["-dmenu", "-i"])
+ .args(["-p", title])
+ .args(["-format", "i"])
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn().map_err(|_| "Rofi failed to launch.")?;
+
+ rofi.stdin.as_mut().unwrap().write(input.as_bytes())
+ .map_err(|_| "Could not send input to Rofi process.")?;
+
+ let rofi_output = rofi.wait_with_output()
+ .map_err(|_| "Waiting on Rofi failed.")?;
+ let choice = String::from_utf8(rofi_output.stdout)
+ .map_err(|_| "Couldn't make sense of Rofi output.")?;
+
+ if choice.trim().is_empty() { return Err("Rofi was quit without a choice."); }
+
+ let choice_idx = choice.trim().parse::<u32>().unwrap();
+
+ return Ok(choice_idx);
+}
+
+fn launch_tex(directory: &PathBuf, file_name: &String) {
+ let mut xoppdog = Command::new("xoppdog")
+ .arg("sit")
+ .arg(directory.join("figures").as_os_str())
+ .stdout(io::stdout())
+ .stderr(io::stderr())
+ .spawn()
+ .expect("Failed to start xoppdog.");
+
+ let mut wezterm = Command::new("wezterm")
+ .args(["start", "--always-new-process", "--cwd"])
+ .arg(directory.as_os_str())
+ .args(["nvim", file_name])
+ .spawn()
+ .expect("Failed to start neovim terminal.");
+
+ wezterm.wait().expect("Where did the terminal go??");
+ xoppdog.kill().expect("Couldn't kill xoppdog.");
+}
+
+fn init_command(args: InitArgs, config: &Config) -> Course {
+ let course = Course {
+ name: args.name,
+ title: args.title,
+ prof: args.prof,
+ semester: args.semester
+ };
+
+ let lecture_directory = config.root.join(&course.semester).join(&course.name)
+ .join("lecture");
+ fs::create_dir_all(&lecture_directory)
+ .expect("Failed creating course dir.");
+
+ return course;
+}
+
+fn open_command(args: OpenArgs, courses: &HashMap<String, Course>, config: &Config) -> Result<(), &'static str> {
+ let course = match &args.name {
+ Some(name) => courses.get(name).expect("Course not found."),
+ None => pick_course(courses)?,
+ };
+
+ let in_lecture = rofi_picker("Type", String::from("Lecture\nHomework"))? == 0;
+ let new_file = rofi_picker("Action", String::from("New\nEdit"))? == 0;
+
+ let course_directory = config.root.join(&course.semester).join(&course.name).join("lecture");
+ let file_name = match (in_lecture, new_file) {
+ (true, true) => new_lesson(&course, &config),
+ (_, _) => String::from("main.tex"),
+ };
+
+ launch_tex(&course_directory, &file_name);
+
+ Ok(())
+}
+
+fn main() {
+ let cli = Cli::parse();
+
+ let config_path = dirs::config_dir()
+ .expect("Could not resolve config directory.")
+ .join("lectern");
+
+ if !config_path.try_exists().unwrap() {
+ fs::create_dir_all(&config_path).unwrap();
+ }
+
+ let config = read_config(&config_path);
+ let mut courses = get_courses(&config_path);
+
+ match cli.command {
+ Commands::Init(args) => {
+ let course = init_command(args, &config);
+ let name = course.name.clone();
+ courses.insert(name, course);
+ save_courses(&courses, &config_path);
+ },
+ Commands::Open(args) => open_command(args, &courses, &config).unwrap(),
+
+ };
+}