rusty_commit_saver/
lib.rs

1//! # Rusty Commit Saver
2//!
3//! A Rust tool to automatically log Git commits into Obsidian diary entries.
4//!
5//! ## Overview
6//!
7//! Rusty Commit Saver captures each commit's metadata and appends it to a dated
8//! diary entry in your Obsidian vault. Each entry includes:
9//!
10//! - Timestamp
11//! - Commit message
12//! - Repository URL
13//! - Branch name
14//! - Commit hash
15//!
16//! ## Quick Start
17//!
18//! ```ignore
19//! use rusty_commit_saver::{run_commit_saver, config::GlobalVars};
20//! use std::path::PathBuf;
21//!
22//! // Initialize configuration
23//! let global_vars = GlobalVars::new();
24//! global_vars.set_all();
25//!
26//! // Get configuration values
27//! let obsidian_root = global_vars.get_obsidian_root_path_dir();
28//! let commit_path = global_vars.get_obsidian_commit_path();
29//! let date_template = global_vars.get_template_commit_date_path();
30//!
31//! // Save the commit
32//! run_commit_saver(obsidian_root, &commit_path, &date_template).unwrap();
33//! ```
34//!
35//! ## Configuration
36//!
37//! Configuration is stored in an INI file at:
38//! `~/.config/rusty-commit-saver/rusty-commit-saver.ini`
39//!
40//! Example configuration:
41//!
42//! ```text
43//! [obsidian]
44//! root_path_dir = ~/Documents/Obsidian
45//! commit_path = Diaries/Commits
46//!
47//! [templates]
48//! commit_date_path = %Y/%m-%B/%F.md
49//! commit_datetime = %Y-%m-%d %H:%M:%S
50//! ```
51//!
52//! ## Modules
53//!
54//! - [`vim_commit`] - Core commit processing and diary file operations
55//! - [`config`] - Configuration management and INI file parsing
56//!
57//! ## Features
58//!
59//! - ✅ Automatic diary entry creation with YAML frontmatter
60//! - ✅ Timestamped commit rows formatted for Obsidian
61//! - ✅ Customizable storage path with date-based organization
62//! - ✅ Pipe escaping in commit messages for Markdown table safety
63//! - ✅ Thread-safe configuration with `OnceCell`
64
65pub mod config;
66pub mod vim_commit;
67
68use log::info;
69use std::error::Error;
70use std::path::{Path, PathBuf};
71use vim_commit::{
72    CommitSaver, check_diary_path_exists, create_diary_file, create_directories_for_new_entry,
73};
74
75/// Core logic for saving a commit to an Obsidian diary file.
76///
77/// (Full documentation from Function 9 above)
78///
79/// # Errors
80///
81/// Returns an error if:
82/// - Git repository cannot be discovered (not in a git repo or parent directories)
83/// - The diary path cannot be converted to valid UTF-8 encoding
84/// - Parent directories cannot be created (permission denied, invalid path, etc.)
85/// - The diary file cannot be created or written to (disk full, IO error, etc.)
86/// - The commit entry cannot be appended to the file (permission denied, file locked, etc.)
87/// - The current working directory cannot be determined
88pub fn run_commit_saver(
89    obsidian_root_path_dir: PathBuf,
90    obsidian_commit_path: &Path,
91    template_commit_date_path: &str,
92) -> Result<(), Box<dyn Error>> {
93    info!("[run_commit_saver()]: Instanciating CommitSaver Struct");
94    let mut commit_saver_struct = CommitSaver::new();
95
96    info!("[run_commit_saver()]: Preparing the diary entry path to the new commit.");
97    let diary_entry_path = commit_saver_struct
98        .prepare_path_for_commit(obsidian_commit_path, template_commit_date_path);
99
100    let mut full_path = obsidian_root_path_dir;
101    for directory in diary_entry_path.split('/') {
102        full_path.push(directory);
103    }
104
105    let stringed_root_path_dir = full_path
106        .as_os_str()
107        .to_str()
108        .ok_or("Could not convert path to string")?;
109
110    info!("[run_commit_saver()]: Checking if Diary file and/or path exists.");
111    if check_diary_path_exists(&full_path).is_ok() {
112        info!("[run_commit_saver()]: Diary file and path exists: {stringed_root_path_dir:}");
113    } else {
114        info!("[run_commit_saver()]: Diary file and or path DO NOT exist.");
115        info!("[run_commit_saver()]: Creating the directories for the new entry.");
116        create_directories_for_new_entry(&full_path)?;
117
118        info!("[run_commit_saver()]: Creating the files for the new entry.");
119        create_diary_file(stringed_root_path_dir, &mut commit_saver_struct)?;
120    }
121
122    info!("[run_commit_saver()]: Writing the commit in the file.");
123    commit_saver_struct.append_entry_to_diary(&full_path)?;
124    info!("[run_commit_saver]: Commit logged in ");
125
126    Ok(())
127}