yagit

Yet another static site generator for Git 🙀️

Commit
63a11a42223272cc5a7df11a6106377bdfb35145
Parent
98ee25f2cf656060d36c1afd6089bdafde29387d
Author
Pablo <pablo-pie@riseup.net>
Date

Implemented the 'delete' feature

Diffstats

4 files changed, 118 insertions, 25 deletions

Status Name Changes Insertions Deletions
Modified TODO.md 2 files changed 1 2
Modified src/command.rs 2 files changed 21 1
Modified src/log.rs 2 files changed 34 7
Modified src/main.rs 2 files changed 62 15
diff --git a/TODO.md b/TODO.md
@@ -1,4 +1,3 @@
 # TODO
 
-* Add functionality:
-  * subcommand for deleting an existing repo
+* add a testing framework
diff --git a/src/command.rs b/src/command.rs
@@ -3,6 +3,7 @@ use std::{env, ops::BitOrAssign};
 const RENDER_BATCH_CMD: &str = "render-batch";
 const RENDER_CMD:       &str = "render";
 const INIT_CMD:         &str = "init";
+const DELETE_CMD:           &str = "delete";
 
 const FULL_BUILD_FLAG: &str = "--full-build";
 const PRIVATE_FLAG:    &str = "--private";
@@ -18,6 +19,7 @@ enum CmdTag {
   RenderBatch,
   Render,
   Init,
+  Delete,
 }
 
 #[derive(Clone, Debug)]
@@ -29,7 +31,10 @@ pub enum SubCmd {
   Init {
     repo_name:   String,
     description: String,
-  }
+  },
+  Delete {
+    repo_name: String,
+  },
 }
 
 impl Cmd {
@@ -40,6 +45,7 @@ impl Cmd {
         Some(arg) if arg == RENDER_BATCH_CMD => break CmdTag::RenderBatch,
         Some(arg) if arg == RENDER_CMD       => break CmdTag::Render,
         Some(arg) if arg == INIT_CMD         => break CmdTag::Init,
+        Some(arg) if arg == DELETE_CMD       => break CmdTag::Delete,
 
         Some(arg) if arg == FULL_BUILD_FLAG => {
           flags |= Flags::FULL_BUILD;
@@ -100,6 +106,17 @@ impl Cmd {
 
         SubCmd::Init { repo_name, description, }
       }
+      CmdTag::Delete => {
+        let repo_name = if let Some(name) = args.next() {
+          name
+        } else {
+          errorln!("No repository name providade");
+          usage(program_name, Some(tag));
+          return Err(());
+        };
+
+        SubCmd::Delete { repo_name, }
+      }
     };
 
     if args.next().is_some() {
@@ -151,5 +168,8 @@ fn usage(program_name: &str, tag: Option) {
     Some(CmdTag::Init) => {
       usageln!("{program_name} [{PRIVATE_FLAG}] {INIT_CMD} <repo-name>");
     }
+    Some(CmdTag::Delete) => {
+      usageln!("{program_name} [{PRIVATE_FLAG}] {DELETE_CMD} <repo-name>");
+    }
   }
 }
diff --git a/src/log.rs b/src/log.rs
@@ -39,22 +39,22 @@ struct Counter {
 pub(crate) fn log(level: Level, args: &Arguments<'_>) {
   match level {
     Level::Error => {
-      eprint!("{BOLD_RED}    Error{RESET} ");
+      eprint!("{BOLD_RED}     Error{RESET} ");
       eprintln!("{}", args);
       // shouldn't print the job counter because we are about to die
     }
     Level::Info => {
-      print!("{BOLD_BLUE}     Info{RESET} ");
+      print!("{BOLD_BLUE}      Info{RESET} ");
       println!("{}", args);
       log_current_job();
     }
     Level::Warn => {
-      print!("{BOLD_YELLOW}  Warning{RESET} ");
+      print!("{BOLD_YELLOW}   Warning{RESET} ");
       println!("{}", args);
       log_current_job();
     }
     Level::Usage => {
-      print!("{BOLD_YELLOW}    Usage{RESET} ");
+      print!("{BOLD_YELLOW}     Usage{RESET} ");
       println!("{}", args);
       println!("          For more information check the {UNDERLINE}yagit(1){RESET} man page.");
       log_current_job();
@@ -62,6 +62,25 @@ pub(crate) fn log(level: Level, args: &Arguments<'_>) {
   }
 }
 
+pub(crate) fn query(args: &Arguments<'_>) -> String {
+  let mut stdout = io::stdout();
+  let stdin = io::stdin();
+  let mut result = String::new();
+
+  let _ = write!(stdout, "{BOLD_YELLOW}   Confirm{RESET} {} ", args);
+  let _ = stdout.flush();
+
+  if stdin.read_line(&mut result).is_err() {
+    result.clear();
+  } else if result.ends_with('\n') {
+    let _ = result.pop();
+  }
+
+  // shouldn't print the job counter because we are should be running the
+  // 'delete' command, so there are no jobs
+  result
+}
+
 pub fn set_job_count(total: usize) {
   unsafe {
     COUNTER.total = total;
@@ -87,7 +106,7 @@ pub fn render_done() {
 
     let space_padding = "... [/]".len() + 2 * crate::log_floor(COUNTER.total);
     println!(
-      "{BOLD_GREEN} Rendered{RESET} {name}{empty:space_padding$}",
+      "{BOLD_GREEN}  Rendered{RESET} {name}{empty:space_padding$}",
       name  = COUNTER.current_repo_name,
       empty = "",
     );
@@ -104,7 +123,7 @@ fn log_current_job() {
 
     let _ = write!(
       stdout,
-      "{BOLD_CYAN}Rendering{RESET} {name}... {BOLD_WHITE}[{count:>padding$}/{total}]{RESET}\r",
+      "{BOLD_CYAN} Rendering{RESET} {name}... {BOLD_WHITE}[{count:>padding$}/{total}]{RESET}\r",
       count = COUNTER.count,
       total = COUNTER.total,
       padding = crate::log_floor(COUNTER.total),
@@ -158,12 +177,20 @@ macro_rules! usageln {
   });
 }
 
+#[macro_export]
+macro_rules! query {
+  // query!("a {}", "question?");
+  ($($arg:tt)+) => ({
+    $crate::log::query(&std::format_args!($($arg)+))
+  });
+}
+
 pub fn finished(duration: Duration) {
   let duration = duration.as_millis() / 100;
   let secs  = duration / 10;
   let dsecs = duration % 10;
 
-  println!("{BOLD_GREEN} Finished{RESET} Rendering took {secs}.{dsecs}s");
+  println!("{BOLD_GREEN}  Finished{RESET} Rendering took {secs}.{dsecs}s");
 }
 
 #[cfg(target_arch = "x86_64")]
diff --git a/src/main.rs b/src/main.rs
@@ -5,7 +5,6 @@ use std::{
   mem,
   env,
   fmt::{self, Display},
-  ffi::OsStr,
   collections::HashMap,
   time::{Duration, SystemTime, Instant},
   process::ExitCode,
@@ -61,6 +60,7 @@ struct RepoInfo {
   pub name:        String,
   pub owner:       String,
   pub description: Option<String>,
+  pub path:        PathBuf,
 
   pub repo:         Repository,
   pub last_commit:  Time,
@@ -68,9 +68,8 @@ struct RepoInfo {
 }
 
 impl RepoInfo {
-  fn open<P, S>(path: P, name: S) -> Result<Self, ()>
+  fn open<S>(path: PathBuf, name: S) -> Result<Self, ()>
   where
-    P: AsRef<Path> + AsRef<OsStr> + fmt::Debug,
     S: AsRef<str>,
   {
     let repo = match Repository::open(&path) {
@@ -113,13 +112,9 @@ impl RepoInfo {
       return Err(());
     }
 
-    let mut path = PathBuf::from(&path);
-    if !repo.is_bare() {
-      path.push(".git");
-    }
-
     let owner = {
       let mut owner_path = path.clone();
+      if !repo.is_bare() { owner_path.push(".git"); }
       owner_path.push("owner");
 
       let mut owner = String::with_capacity(32);
@@ -141,6 +136,7 @@ impl RepoInfo {
 
     let description = {
       let mut dsc_path = path.clone();
+      if !repo.is_bare() { dsc_path.push(".git"); }
       dsc_path.push("description");
       let mut dsc = String::with_capacity(512);
 
@@ -164,6 +160,7 @@ impl RepoInfo {
       name: String::from(name.as_ref()),
       owner,
       description,
+      path,
       repo,
       first_commit,
       last_commit,
@@ -189,7 +186,7 @@ impl RepoInfo {
               let repo_name = entry.file_name();
 
               result.push(
-                RepoInfo::open(&repo_path, repo_name.to_string_lossy())?
+                RepoInfo::open(repo_path, repo_name.to_string_lossy())?
               );
             }
             _ => continue,
@@ -1628,6 +1625,8 @@ fn main() -> ExitCode {
         }
         log::render_done();
       }
+
+      log::finished(start.elapsed());
     }
     SubCmd::Render { repo_name } => {
       let repos = if let Ok(repos) = RepoInfo::index(cmd.flags.private()) {
@@ -1662,18 +1661,18 @@ fn main() -> ExitCode {
 
       log::render_start("repository index");
       if let Err(e) = render_index(&repos, cmd.flags.private()) {
-        errorln!("Failed rendering global repository index: {e}");
+        errorln!("Failed rendering repository index: {e}");
       }
       log::render_done();
 
       log::render_start(&repo.name);
-
       if let Err(e) = renderer.render() {
         errorln!("Failed rendering pages for {name:?}: {e}",
           name = renderer.name);
       }
-
       log::render_done();
+
+      log::finished(start.elapsed());
     }
     SubCmd::Init { repo_name, description } => {
       let mut repo_path = if cmd.flags.private() {
@@ -1686,8 +1685,6 @@ fn main() -> ExitCode {
       let mut opts = RepositoryInitOptions::new();
       opts.bare(false).no_reinit(true);
 
-      infoln!("Initializing empty {repo_name:?} repository in {repo_path:?}");
-
       if let Err(e) = Repository::init_opts(&repo_path, &opts) {
         errorln!("Couldn't initialize {repo_name:?}: {e}", e = e.message());
         return ExitCode::FAILURE;
@@ -1697,9 +1694,59 @@ fn main() -> ExitCode {
         .is_err() {
         return ExitCode::FAILURE;
       }
+
+      infoln!("Initialized empty repository in {repo_path:?}");
+    }
+    SubCmd::Delete { repo_name } => {
+      let mut repos = if let Ok(repos) = RepoInfo::index(cmd.flags.private()) {
+        repos
+      } else {
+        return ExitCode::FAILURE;
+      };
+
+      let mut repo = None;
+      for i in 0..repos.len() {
+        if repos[i].name == *repo_name {
+          repo = Some(repos.remove(i));
+          break;
+        }
+      }
+
+      if repo.is_none() {
+        errorln!("Couldnt' find repository {repo_name:?} in {repos_dir:?}");
+        return ExitCode::FAILURE;
+      }
+      let repo = repo.unwrap();
+
+      let answer = query!(
+        "Would you like to remove {repo_path:?}?",
+        repo_path = repo.path
+      );
+
+      if answer != "y" && answer != "Y" {
+        infoln!("Not deleting {repo_name:?}", repo_name = repo.name);
+        return ExitCode::SUCCESS;
+      }
+
+      log::set_job_count(1); // tasks: render index
+
+      if let Err(e) = fs::remove_dir_all(&repo.path) {
+        errorln!("Couldnt' remove {repo_path:?}: {e}", repo_path = repo.path);
+        return ExitCode::FAILURE;
+      }
+
+      log::render_start("repository index");
+      infoln!("Removed {repo_path:?}", repo_path = repo.path);
+      log::render_done();
+
+      if let Err(e) = render_index(&repos, cmd.flags.private()) {
+        errorln!("Failed rendering repository index: {e}");
+        return ExitCode::FAILURE;
+      }
+
+      log::finished(start.elapsed());
     }
   }
 
-  log::finished(start.elapsed());
   ExitCode::SUCCESS
 }