summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command_line_interface.rs42
-rw-r--r--src/data.rs28
-rw-r--r--src/main.rs40
-rw-r--r--src/new/chapter.rs63
-rw-r--r--src/new/mod.rs99
-rw-r--r--src/new/project.rs110
-rw-r--r--src/new/section.rs63
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",
+    ))
+}