diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-03-31 21:57:01 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-03-31 21:57:01 +0200 |
commit | 2e5e4b5736c446198e36760e254b7c17dd987166 (patch) | |
tree | b74915864a2c80dbc0a0ebe26a52140a934f45c5 | |
parent | docs(example): Add an example directory (diff) | |
download | lpm-2e5e4b5736c446198e36760e254b7c17dd987166.tar.gz lpm-2e5e4b5736c446198e36760e254b7c17dd987166.zip |
refactor(treewide): Improve code quality by working with a FileTree
The FileTree has been taken from the implementation written by my for the Trinitrix project. It alleviates the problem, where functions had to do many things themselves.
-rw-r--r-- | Cargo.lock | 595 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/cli.rs (renamed from src/command_line_interface.rs) | 19 | ||||
-rw-r--r-- | src/config_file.rs | 19 | ||||
-rw-r--r-- | src/data.rs | 27 | ||||
-rw-r--r-- | src/file_tree/mod.rs | 88 | ||||
-rw-r--r-- | src/main.rs | 127 | ||||
-rw-r--r-- | src/new/chapter.rs | 143 | ||||
-rw-r--r-- | src/new/mod.rs | 95 | ||||
-rw-r--r-- | src/new/project.rs | 111 | ||||
-rw-r--r-- | src/new/section.rs | 102 |
11 files changed, 954 insertions, 375 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) + )); + + let chapter_file = GeneratedFile::new(chapter_file_path, chapter_file_text); + file_tree.add_file(chapter_file); -pub fn get_section_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| { - 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) } |