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}