aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Cargo.lock595
-rw-r--r--Cargo.toml3
-rw-r--r--src/cli.rs (renamed from src/command_line_interface.rs)19
-rw-r--r--src/config_file.rs19
-rw-r--r--src/data.rs27
-rw-r--r--src/file_tree/mod.rs88
-rw-r--r--src/main.rs127
-rw-r--r--src/new/chapter.rs143
-rw-r--r--src/new/mod.rs95
-rw-r--r--src/new/project.rs111
-rw-r--r--src/new/section.rs100
11 files changed, 953 insertions, 374 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9d8cd04..3bb0abf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,5 +3,598 @@
version = 3
[[package]]
-name = "TODO"
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[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",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "env_filter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "lpm"
version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "clap",
+ "convert_case",
+ "env_logger",
+ "log",
+ "serde",
+ "serde_derive",
+ "toml",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+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 = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[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.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+
+[[package]]
+name = "syn"
+version = "2.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
+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.22.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
+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 = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "winnow"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
+dependencies = [
+ "memchr",
+]
diff --git a/Cargo.toml b/Cargo.toml
index b74b73a..07cf5ab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,9 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+anyhow = "1.0.81"
chrono = { version = "0.4.37", features = ["alloc"] }
clap = { version = "4.5.4", features = ["derive"] }
convert_case = "0.6.0"
+env_logger = "0.11.3"
+log = "0.4.21"
serde = { version = "1.0.197", features = ["derive"] }
serde_derive = "1.0.197"
toml = "0.8.12"
diff --git a/src/command_line_interface.rs b/src/cli.rs
index 5d24ae5..fe1b194 100644
--- a/src/command_line_interface.rs
+++ b/src/cli.rs
@@ -2,7 +2,7 @@ use clap::{Parser, Subcommand};
/// A project manager for LaTeX
#[derive(Parser, Debug)]
-#[clap(author, version, about, long_about = None)]
+#[command(author, version, about, long_about = None)]
pub struct Args {
#[command(subcommand)]
pub cli: Command,
@@ -12,13 +12,17 @@ pub struct Args {
pub enum Command {
/// Generates a new part
#[command(subcommand)]
- New(SubCommand),
+ New(What),
}
#[derive(Subcommand, Debug)]
-pub enum SubCommand {
+pub enum What {
/// Adds a section
Section {
+ /// The name of the chapter to extend, can be empty when the current_dir is inside a
+ /// chapter already.
+ #[arg(long, short)]
+ chapter: Option<String>,
/// Name of the new Section
name: String,
},
@@ -28,13 +32,4 @@ pub enum SubCommand {
/// Name of the new Chapter
name: String,
},
- // /// generates a new project
- // Project {
- // /// Name of the new Project
- // name: String,
- // /// Name of the first chapter
- // first_chapter: String,
- // // /// Name of the first section
- // // first_section: String,
- // },
}
diff --git a/src/config_file.rs b/src/config_file.rs
new file mode 100644
index 0000000..838a78d
--- /dev/null
+++ b/src/config_file.rs
@@ -0,0 +1,19 @@
+use serde_derive::{Deserialize, Serialize};
+
+#[derive(Deserialize, Serialize)]
+pub struct Config {
+ pub last_chapter: LastChapter,
+ pub templates: Template,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct LastChapter {
+ pub user_name: String,
+ pub number: u32,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct Template {
+ pub section: String,
+ pub chapter: String
+}
diff --git a/src/data.rs b/src/data.rs
deleted file mode 100644
index 72609b8..0000000
--- a/src/data.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use serde_derive::{Deserialize, Serialize};
-
-#[derive(Deserialize, Serialize)]
-pub struct Data {
- pub last_chapter: LastChapter,
-}
-
-#[derive(Deserialize, Serialize)]
-pub struct LastChapter {
- pub user_name: String,
- pub number: u32,
-}
-
-//fn main() {
-// let config: Config = toml::from_str(r#"
-// ip = '127.0.0.1'
-//
-// [keys]
-// github = 'xxxxxxxxxxxxxxxxx'
-// travis = 'yyyyyyyyyyyyyyyyy'
-// "#).unwrap();
-//
-// assert_eq!(config.ip, "127.0.0.1");
-// assert_eq!(config.port, None);
-// assert_eq!(config.keys.github, "xxxxxxxxxxxxxxxxx");
-// assert_eq!(config.keys.travis.as_ref().unwrap(), "yyyyyyyyyyyyyyyyy");
-//}
diff --git a/src/file_tree/mod.rs b/src/file_tree/mod.rs
new file mode 100644
index 0000000..d6f0c3c
--- /dev/null
+++ b/src/file_tree/mod.rs
@@ -0,0 +1,88 @@
+/*
+* Copyright (C) 2023 - 2024:
+* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
+* SPDX-License-Identifier: LGPL-3.0-or-later
+*
+* This file is part of the Trixy crate for Trinitrix.
+*
+* Trixy is free software: you can redistribute it and/or modify
+* it under the terms of the Lesser GNU General Public License as
+* published by the Free Software Foundation, either version 3 of
+* the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* and the Lesser GNU General Public License along with this program.
+* If not, see <https://www.gnu.org/licenses/>.
+*/
+
+//! [`FileTree`]s are the fundamental data structure used by trixy to present generated data to
+//! you.
+
+use std::{
+ fs, io,
+ path::{Path, PathBuf},
+};
+
+/// A file tree containing all files that were generated. These are separated into host and
+/// auxiliary files. See their respective descriptions about what differentiates them.
+#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FileTree {
+ /// Files, that are supposed to be included in the compiled crate.
+ pub files: Vec<GeneratedFile>,
+}
+
+/// A generated files
+#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct GeneratedFile {
+ /// The path this generated file would like to be placed at.
+ /// This path is relative to the crate root.
+ pub path: PathBuf,
+
+ /// The content of this file.
+ ///
+ /// This should already be formatted and ready to be used.
+ pub value: String,
+}
+
+impl GeneratedFile {
+ pub fn new(path: PathBuf, value: String) -> Self {
+ Self { path, value }
+ }
+ pub fn new_in_out_dir(name: String, value: String, out_dir: &Path) -> Self {
+ let path = out_dir.join(name);
+ Self { path, value }
+ }
+
+ pub fn materialize(self) -> io::Result<()> {
+ fs::create_dir_all(self.path.parent().expect("This path should have a parent"))?;
+ fs::write(self.path, self.value.as_bytes())?;
+ Ok(())
+ }
+}
+
+impl FileTree {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn add_file(&mut self, file: GeneratedFile) {
+ self.files.push(file)
+ }
+
+ pub fn extend(&mut self, files: Vec<GeneratedFile>) {
+ files.into_iter().for_each(|file| self.add_file(file));
+ }
+
+ pub fn materialize(self) -> io::Result<()> {
+ self.files
+ .into_iter()
+ .map(|file| -> io::Result<()> { file.materialize() })
+ .collect::<io::Result<()>>()?;
+ Ok(())
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index b19e7bf..8c2ea62 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,38 +1,111 @@
+use std::{env, ffi::OsString, fs, path::PathBuf};
+
+use anyhow::{bail, Context};
use clap::Parser;
-use command_line_interface::{
- Args,
- Command::New,
- SubCommand::{Chapter, Section},
+use log::debug;
+
+use crate::{
+ cli::{
+ Args,
+ Command::New,
+ What::{Chapter, Section},
+ },
+ config_file::Config,
+ new::{chapter::generate_new_chapter, section::generate_new_section},
};
-use new::{chapter::generate_new_chapter, section::generate_new_section};
-pub mod command_line_interface;
-pub mod data;
+pub mod cli;
+pub mod config_file;
pub mod new;
-fn main() {
+// The copyright header tells you, where this file is from.
+pub mod file_tree;
+
+fn main() -> anyhow::Result<()> {
+ env_logger::init();
let args = Args::parse();
- match args.cli {
+ let project_root = get_project_root_by_lmp_toml().context("Looking for the project root")?;
+
+ let config_file = fs::read_to_string(project_root.join("lpm.toml"))?;
+ let config: Config = toml::from_str(&config_file).context("Reading toml from string")?;
+
+ let file_tree = match args.cli {
New(new_command) => match new_command {
- Section { name } => generate_new_section(name).unwrap(),
- Chapter { name } => generate_new_chapter(name).unwrap(),
- // Project {
- // name,
- // first_chapter,
- // //first_section,
- // } => {
- // let preamble_path = PathBuf::from("");
- // let resource_path = PathBuf::from("");
- // generate_new_project(
- // name,
- // first_chapter,
- // //first_section,
- // preamble_path,
- // resource_path,
- // )
- // .unwrap()
- // }
+ Section { name, chapter } => {
+ let chapter = if let Some(val) = chapter {
+ // The user probably has not added the preceeding chapter number to the chapter
+ // string
+ if val.starts_with(|c: char| c.is_numeric()) {
+ eprintln!(
+ "Your chapter name starts with a number, assuming \
+ that you have already added the chapter number"
+ );
+ val
+ } else {
+ bail!(
+ "Calculating the chapter number is not yet \
+ implemented, please add it yourself"
+ );
+ }
+ } else {
+ // The user thinks that they are already inside a chapter
+ get_upwards_chapter()?
+ };
+
+ generate_new_section(&config, name, &project_root, &chapter)?
+ }
+ Chapter { name } => generate_new_chapter(config, &project_root, name)?,
},
+ };
+
+ file_tree.materialize()?;
+
+ Ok(())
+}
+
+pub fn get_project_root_by_lmp_toml() -> anyhow::Result<PathBuf> {
+ let path = env::current_dir()?;
+ let mut path_ancestors = path.as_path().ancestors();
+
+ while let Some(path_segment) = path_ancestors.next() {
+ if fs::read_dir(path_segment)?.into_iter().any(|path_segment| {
+ path_segment
+ .expect("The read_dir shouldn't error out here")
+ .file_name()
+ == OsString::from("lpm.toml")
+ }) {
+ return Ok(PathBuf::from(path_segment));
+ }
}
+ bail!("Ran out of places to find lpm.toml")
+}
+
+fn get_upwards_chapter() -> anyhow::Result<String> {
+ let current_path = env::current_dir()?;
+
+ for anc in current_path.as_path().ancestors() {
+ debug!("Reading directory {}", anc.display());
+
+ for dir in fs::read_dir(anc)? {
+ let dir = dir?;
+ debug!("Checking path: {}", dir.file_name().to_string_lossy());
+
+ if dir.file_name() == OsString::from("chapter.tex") {
+ match anc
+ .file_name()
+ .expect("This should always be a file")
+ .to_str()
+ {
+ Some(str) => return Ok(str.to_owned()),
+ None => bail!(
+ "Failed to convert your path ('{}') to a string!",
+ dir.file_name().to_string_lossy()
+ ),
+ }
+ }
+ }
+ }
+
+ bail!("Failed to get a chapter name, please specify one with the `--chapter` flag!")
}
diff --git a/src/new/chapter.rs b/src/new/chapter.rs
index 88f2a85..749202f 100644
--- a/src/new/chapter.rs
+++ b/src/new/chapter.rs
@@ -1,67 +1,108 @@
-use std::{
- fs::{self, File},
- io::{self, Write},
-};
+use std::{fs, path::Path};
use convert_case::{Case, Casing};
-use crate::data::Data;
+use crate::{
+ config_file::Config,
+ file_tree::{FileTree, GeneratedFile},
+};
-use super::{get_project_root, CHAPTER};
+pub fn generate_new_chapter(
+ config: Config,
+ project_root: &Path,
+ name: String,
+) -> anyhow::Result<FileTree> {
+ let mut file_tree = FileTree::new();
+ file_tree.add_file(new_main_file(project_root, &config, &name)?);
+ file_tree.add_file(new_chapter_file(&config, &name, project_root));
+ file_tree.add_file(new_lpm_toml_file(config, name, project_root));
-pub fn generate_new_chapter(name: String) -> io::Result<()> {
- let project_root = get_project_root().unwrap();
+ Ok(file_tree)
+}
- let raw_data_file = fs::read_to_string(project_root.join("lpm.toml")).unwrap();
- let mut data_file: Data = toml::from_str(&raw_data_file).unwrap();
- let mut main_file = fs::read_to_string(project_root.join("main.tex")).unwrap();
+fn new_lpm_toml_file(mut config: Config, name: String, project_root: &Path) -> GeneratedFile {
+ config.last_chapter.user_name = name.to_case(Case::Snake);
+ config.last_chapter.number += 1;
- fs::create_dir(project_root.join(format!("content/{}", name.to_case(Case::Snake),))).unwrap();
- let mut new_chapter = File::create(project_root.join(format!(
- "content/{}/chapter_{:02}.tex",
- name.to_case(Case::Snake),
- data_file.last_chapter.number + 1
- )))
- .unwrap();
- new_chapter
- .write_all(CHAPTER.replace("REPLACEMENT_CHAPTER", &name).as_bytes())
- .unwrap();
+ GeneratedFile::new(
+ project_root.join("lpm.toml"),
+ toml::to_string(&config).expect("We changed it ourselfes, the conversion should work"),
+ )
+}
- fs::create_dir(project_root.join(format!("content/{}/sections", name.to_case(Case::Snake),)))
- .unwrap();
+fn new_chapter_file(config: &Config, name: &str, project_root: &Path) -> GeneratedFile {
+ let chapter_text = config
+ .templates
+ .chapter
+ .replace("REPLACEMENT_CHAPTER", &name);
- main_file = main_file.replace(
- &format!(
- "\\includeonly{{content/{}/{}}}",
- &data_file.last_chapter.user_name,
- &format!("chapter_{:02}", data_file.last_chapter.number)
- ),
- &format!(
- "\\includeonly{{content/{}/{}}}",
- name.to_case(Case::Snake),
- &format!("chapter_{:02}", data_file.last_chapter.number + 1)
- ),
- );
- let find_index = main_file.find("% NEXT_CHAPTER").unwrap();
- main_file.insert_str(
+ GeneratedFile::new(
+ project_root
+ .join("content")
+ .join(format! {"{:02}_{}", config.last_chapter.number + 1, name.to_case(Case::Snake)})
+ .join("chapter.tex"),
+ chapter_text,
+ )
+}
+
+fn new_main_file(
+ project_root: &Path,
+ config: &Config,
+ name: &str,
+) -> anyhow::Result<GeneratedFile> {
+ let mut main_text = fs::read_to_string(project_root.join("main.tex"))?;
+
+ if &config.last_chapter.user_name == "static" && config.last_chapter.number == 0 {
+ // This is the first added chapter; The `\includeonly` will be empty.
+ main_text = main_text.replace(
+ "\\includeonly{}",
+ &format!(
+ "\\includeonly{{content/{}/{}}}",
+ &format!(
+ "{:02}_{}",
+ config.last_chapter.number + 1,
+ &name.to_case(Case::Snake)
+ ),
+ "chapter.tex",
+ ),
+ )
+ } else {
+ main_text = main_text.replace(
+ &format!(
+ "\\includeonly{{content/{}/{}}}",
+ &format!(
+ "{:02}_{}",
+ config.last_chapter.number, &config.last_chapter.user_name
+ ),
+ "chapter.tex",
+ ),
+ &format!(
+ "\\includeonly{{content/{}/{}}}",
+ &format!(
+ "{:02}_{}",
+ config.last_chapter.number + 1,
+ &name.to_case(Case::Snake)
+ ),
+ "chapter.tex",
+ ),
+ )
+ };
+
+ let find_index = main_text
+ .find("% NEXT_CHAPTER")
+ .expect("The % NEXT_CHAPTER maker must exist");
+ main_text.insert_str(
find_index,
&format!(
"\\include{{content/{}/{}}}\n ",
- name.to_case(Case::Snake),
- &format!("chapter_{:02}", data_file.last_chapter.number + 1)
+ &format!(
+ "{:02}_{}",
+ config.last_chapter.number + 1,
+ &name.to_case(Case::Snake)
+ ),
+ "chapter.tex",
),
);
- data_file.last_chapter.user_name = name.to_case(Case::Snake);
- data_file.last_chapter.number += 1;
-
- fs::write(
- project_root.join("lpm.toml"),
- toml::to_string(&data_file).expect("We changed it ourselfes, the conversion should work"),
- )
- .unwrap();
-
- fs::write(project_root.join("main.tex"), main_file).unwrap();
-
- Ok(())
+ Ok(GeneratedFile::new(project_root.join("main.tex"), main_text))
}
diff --git a/src/new/mod.rs b/src/new/mod.rs
index 33783c4..a85187c 100644
--- a/src/new/mod.rs
+++ b/src/new/mod.rs
@@ -1,97 +1,2 @@
pub mod chapter;
-pub mod project;
pub mod section;
-
-use std::{
- env,
- ffi::OsString,
- fs::read_dir,
- io::{self, ErrorKind},
- path::PathBuf,
-};
-
-const SECTION: &'static str = r#"%! TEX root = ../../../main.tex
-% LTeX: language=de-DE
-
-\lesson{REPLACMENT_SECTION_TITLE}{DATE}{}
-Dies ist etwas Text
-"#;
-
-const CHAPTER: &'static str = r#"%! TEX root = ../main.tex
-% LTeX: language=de-DE
-
-\chapter{REPLACEMENT_CHAPTER}
-"#;
-
-const TITLE_FILE: &'static str = r#"% LTeX: language=de-DE
-
-\maketitle
-\tableofcontents
-\vspace*{\fill}
-\makeatletter
-Copyright \textcopyright{} \@authors{} \@years{}\\
-\ \\
-Dieses Werk ist lizenziert unter den Bedingungen der CC BY-SA 4.0.
-Der Lizenztext ist online unter \url{http://creativecommons.org/licenses/by-sa/4.0/legalcode} abrufbar.
-\makeatother
-\clearpage
-"#;
-
-const MAIN_FILE: &'static str = r#"%\documentclass[a4paper, 12pt, nosolutions]{report}
-\documentclass[a4paper, 12pt]{report}
-% LTeX: language=de-DE
-\input{headers/preamble.tex}
-\input{headers/preamble_local.tex}
-
-
-\title{\textbf{Titel}}
-\author{Name\thanks{Beispiel}}
-\authors{Name}
-\years{2022 - 2023}
-\date{\DTMDate{2002-12-4}}
-
-\includeonly{content/REPLACEMENT_CHAPTER/chapter_01}
-
-\begin{document}
- \input{content/static/title}
-
- \include{content/REPLACEMENT_CHAPTER/chapter_01}
- % NEXT_CHAPTER
-
- \printbibliography\relax
-\end{document}
-"#;
-
-pub fn get_project_root() -> io::Result<PathBuf> {
- let path = env::current_dir()?;
- let mut path_ancestors = path.as_path().ancestors();
-
- while let Some(path_segment) = path_ancestors.next() {
- if read_dir(path_segment)?.into_iter().any(|path_segment| {
- path_segment
- .expect("The read_dir shouldn't error out here")
- .file_name()
- == OsString::from("lpm.toml")
- }) {
- return Ok(PathBuf::from(path_segment));
- }
- }
- Err(io::Error::new(
- ErrorKind::NotFound,
- "Ran out of places to find lpm.toml",
- ))
-}
-
-pub fn get_all_chapters() -> io::Result<Vec<String>> {
- let path = get_project_root()?;
- let output = read_dir(path.join("content"))?
- .map(|path| {
- path.expect("The values sholud be fine")
- .file_name()
- .to_str()
- .expect("all names should be valid utf-8")
- .to_owned()
- })
- .collect();
- Ok(output)
-}
diff --git a/src/new/project.rs b/src/new/project.rs
deleted file mode 100644
index 56edead..0000000
--- a/src/new/project.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-use std::{
- env,
- fs::{self, File},
- io::{self, Write},
- path::PathBuf,
- process::Command,
-};
-
-use convert_case::{Case, Casing};
-
-use crate::data::Data;
-
-use super::{CHAPTER, MAIN_FILE, TITLE_FILE};
-
-const NEEDED_DIRECTORYS: &'static [&'static str] =
- &["build", "content", "headers", "references", "resources"];
-
-pub fn generate_new_project(
- name: String,
- first_chapter: String,
- //first_section: String,
- preamble_path: PathBuf,
- resource_path: PathBuf,
-) -> io::Result<()> {
- fs::create_dir(&name).unwrap();
- env::set_current_dir(&name).unwrap();
- let project_root = env::current_dir().unwrap();
-
- for directory in NEEDED_DIRECTORYS {
- fs::create_dir(directory).unwrap();
- }
-
- // content
- env::set_current_dir(project_root.join("content")).unwrap();
- fs::create_dir(&first_chapter.to_case(Case::Snake)).unwrap();
- fs::create_dir("static").unwrap();
-
- env::set_current_dir("static").unwrap();
- let mut title_file = File::create("title.tex").unwrap();
- title_file.write_all(TITLE_FILE.as_bytes()).unwrap();
-
- env::set_current_dir(
- project_root
- .join("content")
- .join(&first_chapter.to_case(Case::Snake)),
- )
- .unwrap();
- fs::create_dir("sections").unwrap();
- let mut chapter_file = File::create("chapter_01.tex").unwrap();
- chapter_file
- .write_all(
- CHAPTER
- .replace("REPLACEMENT_CHAPTER", &first_chapter)
- .as_bytes(),
- )
- .unwrap();
-
- //env::set_current_dir("sections").unwrap();
- //let mut section_file = File::create(format!("{}.tex", &first_section)).unwrap();
- //section_file
- // .write_all(SECTION.as_bytes())
- // .unwrap();
-
- // headers
- env::set_current_dir(project_root.join("headers")).unwrap();
- File::create("preamble_local.tex").unwrap();
- fs::copy(fs::canonicalize(preamble_path).unwrap(), "preamble.tex").unwrap();
-
- // resources
- env::set_current_dir(project_root.join("resources")).unwrap();
- fs::canonicalize(resource_path)
- .unwrap()
- .read_dir()
- .unwrap()
- .map(|path| path.expect("The provided path should work"))
- .for_each(|path| {
- fs::copy(path.path(), path.file_name()).unwrap();
- });
-
- // root files
- env::set_current_dir(project_root).unwrap();
- let mut main_file = File::create("main.tex").unwrap();
- main_file
- .write_all(
- MAIN_FILE
- .replace("REPLACEMENT_CHAPTER", &first_chapter.to_case(Case::Snake))
- .as_bytes(),
- )
- .unwrap();
-
- let data_file = Data {
- last_chapter: crate::data::LastChapter {
- user_name: first_chapter.to_case(Case::Snake),
- number: 1,
- },
- };
- let mut lpm_file = File::create("lpm.toml").unwrap();
- lpm_file
- .write_all(toml::to_string(&data_file).unwrap().as_bytes())
- .unwrap();
-
- Command::new("git")
- .arg("init")
- .output()
- .expect("failed to execute process");
-
- let mut lpm_file = File::create(".gitignore").unwrap();
- lpm_file.write_all(b"/build").unwrap();
-
- Ok(())
-}
diff --git a/src/new/section.rs b/src/new/section.rs
index 31742c2..a359fb0 100644
--- a/src/new/section.rs
+++ b/src/new/section.rs
@@ -1,63 +1,63 @@
-use std::{
- env,
- fs::{self, read_dir},
- io::{self, ErrorKind},
- path::PathBuf,
- time::SystemTime,
-};
+use std::{fs, path::Path, time::SystemTime};
+use anyhow::Context;
use chrono::{DateTime, Local};
use convert_case::{Case, Casing};
+use log::debug;
-use super::SECTION;
+use crate::{
+ config_file::Config,
+ file_tree::{FileTree, GeneratedFile},
+};
-pub fn generate_new_section(name: String) -> io::Result<()> {
- let chapter_root = get_section_root()?;
- let chapter_main_file_path = read_dir(&chapter_root)?
- .into_iter()
- .map(|path| path.unwrap())
- .filter(|path| path.file_name().to_str().unwrap().contains("chapter_"))
- .last()
- .unwrap()
- .path();
- let mut main_file = fs::read_to_string(&chapter_main_file_path).unwrap();
+pub fn generate_new_section(
+ config: &Config,
+ name: String,
+ project_root: &Path,
+ chapter_name: &str,
+) -> anyhow::Result<FileTree> {
+ let chapter_root = project_root.join("content").join(chapter_name);
+ debug!("Chapter root is: {}", chapter_root.display());
- main_file.push_str(&format!(
- "\\input{{content/{}/sections/{}}}\n",
- chapter_root.file_name().unwrap().to_str().unwrap(),
- &name.to_case(Case::Snake)
- ));
- fs::write(chapter_main_file_path, main_file)?;
- fs::write(
- chapter_root.join(format!("sections/{}.tex", name.to_case(Case::Snake))),
- SECTION.replace("REPLACMENT_SECTION_TITLE", &name).replace(
+ let mut file_tree = FileTree::new();
+
+ let new_section_text = config
+ .templates
+ .section
+ .replace("REPLACMENT_SECTION_TITLE", &name)
+ .replace(
"DATE",
&format!(
"{}",
- DateTime::<Local>::from(SystemTime::now()).format("%Y-%m-%d")
+ // FIXME: The time is not really precise enough to display the time. <2024-03-31>
+ DateTime::<Local>::from(SystemTime::now()).format("%Y-%m-%d (%_H:%_S)")
),
- ),
- )?;
- Ok(())
-}
+ );
+
+ let new_section_file = GeneratedFile::new(
+ chapter_root
+ .join("sections")
+ .join(format!("{}.tex", name.to_case(Case::Snake))),
+ new_section_text,
+ );
+ file_tree.add_file(new_section_file);
+
+ let chapter_file_path = chapter_root.join("chapter.tex");
+ let mut chapter_file_text = fs::read_to_string(&chapter_file_path).with_context(|| {
+ format!(
+ "Failed to read the chapter main file ('{}') to string",
+ &chapter_file_path.display(),
+ )
+ })?;
+
+ chapter_file_text.push_str(&format!(
+ "\\input{{content/{}/sections/{}}}\n",
+ chapter_name,
+ &name.to_case(Case::Snake)
+ ));
-pub fn get_section_root() -> io::Result<PathBuf> {
- let path = env::current_dir()?;
- let mut path_ancestors = path.as_path().ancestors();
+ let chapter_file = GeneratedFile::new(chapter_file_path, chapter_file_text);
+ file_tree.add_file(chapter_file);
- while let Some(path_segment) = path_ancestors.next() {
- if read_dir(path_segment)?.into_iter().any(|path| {
- path.expect("The read_dir shouldn't error out here")
- .file_name()
- .to_str()
- .unwrap()
- .contains("chapter_")
- }) {
- return Ok(PathBuf::from(path_segment));
- }
- }
- Err(io::Error::new(
- ErrorKind::NotFound,
- "Ran out of places to find chapter_root",
- ))
+ Ok(file_tree)
}