diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/command_line_interface.rs | 42 | ||||
-rw-r--r-- | src/data.rs | 28 | ||||
-rw-r--r-- | src/main.rs | 40 | ||||
-rw-r--r-- | src/new/chapter.rs | 63 | ||||
-rw-r--r-- | src/new/mod.rs | 99 | ||||
-rw-r--r-- | src/new/project.rs | 110 | ||||
-rw-r--r-- | src/new/section.rs | 63 |
7 files changed, 445 insertions, 0 deletions
diff --git a/src/command_line_interface.rs b/src/command_line_interface.rs new file mode 100644 index 0000000..66d8122 --- /dev/null +++ b/src/command_line_interface.rs @@ -0,0 +1,42 @@ +use clap::{Subcommand, Parser}; + +/// A project manager for LaTeX +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + #[command(subcommand)] + pub cli: Command, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + /// Generates a new part + #[command(subcommand)] + New (SubCommand), +} + + +#[derive(Subcommand, Debug)] +pub enum SubCommand { + /// Adds a section + Section { + /// Name of the new Section + name: String, + }, + + /// Adds a chapter + Chapter { + /// 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/data.rs b/src/data.rs new file mode 100644 index 0000000..4966e88 --- /dev/null +++ b/src/data.rs @@ -0,0 +1,28 @@ +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/main.rs b/src/main.rs new file mode 100644 index 0000000..b056c88 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,40 @@ +use std::path::PathBuf; + +use clap::Parser; +use command_line_interface::{ + Args, + Command::New, + SubCommand::{Chapter, Project, Section}, +}; +use new::{chapter::generate_new_chapter, project::generate_new_project, section::generate_new_section}; + +pub mod command_line_interface; +pub mod data; +pub mod new; + +fn main() { + let args = Args::parse(); + + 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("/home/dt/repos/tex/preset/headers/preamble.tex"); + let resource_path = PathBuf::from("/home/dt/repos/tex/preset/resources"); + generate_new_project( + name, + first_chapter, + //first_section, + preamble_path, + resource_path, + ) + .unwrap() + } + }, + } +} diff --git a/src/new/chapter.rs b/src/new/chapter.rs new file mode 100644 index 0000000..9cc12d0 --- /dev/null +++ b/src/new/chapter.rs @@ -0,0 +1,63 @@ +use std::{ + fs::{self, File}, + io::{self, Write}, +}; + +use convert_case::{Case, Casing}; + +use crate::data::Data; + +use super::{get_project_root, CHAPTER}; + + +pub fn generate_new_chapter(name: String) -> io::Result<()> { + let project_root = get_project_root().unwrap(); + + 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(); + + 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(); + + fs::create_dir(project_root.join(format!("content/{}/sections", name.to_case(Case::Snake),))).unwrap(); + + 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( + find_index, + &format!( + "\\include{{content/{}/{}}}\n ", + name.to_case(Case::Snake), + &format!("chapter_{:02}", data_file.last_chapter.number + 1) + ), + ); + + 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(()) +} diff --git a/src/new/mod.rs b/src/new/mod.rs new file mode 100644 index 0000000..efc55cd --- /dev/null +++ b/src/new/mod.rs @@ -0,0 +1,99 @@ +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 new file mode 100644 index 0000000..e04d138 --- /dev/null +++ b/src/new/project.rs @@ -0,0 +1,110 @@ +use std::{ + env, + fs::{self, File}, + io::{self, Write}, + path::PathBuf, + process::Command, +}; + + +use convert_case::{Case, Casing}; + +use crate::data::Data; + +use super::{TITLE_FILE, CHAPTER, MAIN_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 new file mode 100644 index 0000000..31742c2 --- /dev/null +++ b/src/new/section.rs @@ -0,0 +1,63 @@ +use std::{ + env, + fs::{self, read_dir}, + io::{self, ErrorKind}, + path::PathBuf, + time::SystemTime, +}; + +use chrono::{DateTime, Local}; +use convert_case::{Case, Casing}; + +use super::SECTION; + +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(); + + 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( + "DATE", + &format!( + "{}", + DateTime::<Local>::from(SystemTime::now()).format("%Y-%m-%d") + ), + ), + )?; + Ok(()) +} + +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", + )) +} |