bin_efo/commands/init.rs
1//! # `init` module
2//!
3//! Provides the implementation of the `init` sub-command for the
4//! command-line interface. The sub-command creates an empty
5//! repository, writes the default configuration (`.epoch`),
6//! and sets up the first epoch (epoch 0).
7//!
8//! The main public API consists of:
9//!
10//! * `InitCmd` - a struct containing the command-line arguments.
11//!
12//! ## Guide
13//!
14//! The `init` sub‑command bootstraps a new epoch repository:
15//!
16//! * It creates the default `.epoch` configuration file in the current directory.
17//! * It creates an empty **epoch 0** folder (with its metadata file).
18//! * It refuses to run if the target directory already contains files unless the user supplies `--force`.
19//!
20//! ### How to use
21//!
22//! ```bash
23//! epoch init # normal init – directory must be empty
24//! epoch init --force # skip the empty‑folder check
25//! ```
26//!
27//! ### Table Entry
28//!
29//! | Sub‑command | Usage | What it changes |
30//! |-------------|-------|-----------------|
31//! | `init` | `epoch init [--force]` | Initialize the current folder as an epoch folder |
32
33#![warn(missing_docs, clippy::missing_docs_in_private_items)]
34
35use clap::Args;
36use lib_efo::{
37 epoch::{EpochConfig, EpochInfo, epoch_traits::ConfigWritable},
38 error::EpochError,
39};
40use std::fs::read_dir;
41
42use crate::{
43 error::AppError,
44 success::{SuccessMessages, display_success},
45};
46
47#[derive(Args, Debug)]
48
49/// Arguments for the `init` subcommand.
50///
51/// The `--force` flag forces initialisation even when the target
52/// directory already contains files.
53pub struct InitCmd {
54 /// Force initialisation even if the directory isn't empty
55 #[arg(long)]
56 force: bool,
57}
58
59/// Initialise an empty repository.
60///
61/// Creates the default `EpochConfig`, verifies that the target directory
62/// is empty, and writes the initial configuration and epoch data.
63//TODO(5): Upholster the logic behind force and empty detection
64pub fn init(args: &InitCmd) -> Result<(), AppError> {
65 let epoch_config = EpochConfig::default();
66 let root = epoch_config.save_folder();
67
68 if read_dir(root)?.count() != 0 {
69 Err(EpochError::FolderNotEmpty {
70 name: None,
71 show_label: None,
72 })?;
73 }
74 init_epoch(args)?;
75 epoch_config
76 .save_with_context(
77 "Creatig initial config",
78 &lib_efo::epoch::SaveMode::DoNotErase,
79 )
80 .map_err(|_| EpochError::AlreadyInitialized {
81 advice: Some(
82 "You must remove the .epoch configuration file manually or pick another folder"
83 .to_string(),
84 ),
85 })?;
86
87 display_success(&SuccessMessages::FolderInitializedSuccessfully("."));
88 Ok(())
89}
90
91/// Helper that creates the initial epoch (epoch 0).
92///
93/// This function is not part of the public API; it is kept internal
94/// to `init`. It creates an empty `EpochInfo`, crea
95fn init_epoch(_args: &InitCmd) -> Result<(), AppError> {
96 let empty = EpochInfo::default();
97 _ = empty.create_subfolders();
98 empty.save_with_context(
99 "Creating empty epoch file 0",
100 &lib_efo::epoch::SaveMode::DoNotErase,
101 )?;
102 Ok(())
103}