tftio-prompter 4.0.0

A CLI tool for composing reusable prompt snippets from a library using TOML profiles
Documentation
//! Default configuration and library scaffolding.
use crate::{PrompterError, config_path, info_message, library_dir, success_message};
use std::fs;
use std::path::PathBuf;
use tftio_cli_common::progress::make_spinner;

/// Initialize default configuration and library structure.
///
/// # Errors
/// Returns an error if the home directory cannot be located, a config or
/// library directory cannot be created, or a default file cannot be written.
pub fn init_scaffold() -> Result<(), PrompterError> {
    let pb = make_spinner(true, "Initializing prompter...");

    let cfg_path = config_path()?;
    let cfg_dir = cfg_path
        .parent()
        .ok_or_else(|| PrompterError::NoParentDir(cfg_path.clone()))?;

    if let Some(ref pb) = pb {
        pb.set_message("Creating config directory...");
    }
    fs::create_dir_all(cfg_dir).map_err(|source| PrompterError::Io {
        path: cfg_dir.to_path_buf(),
        source,
    })?;

    let lib = library_dir()?;
    if let Some(ref pb) = pb {
        pb.set_message("Creating library directory...");
    }
    fs::create_dir_all(&lib).map_err(|source| PrompterError::Io {
        path: lib.clone(),
        source,
    })?;

    if !cfg_path.exists() {
        if let Some(ref pb) = pb {
            pb.set_message("Writing default config...");
        }
        let default_cfg = r#"# Prompter configuration
# Profiles map to sets of markdown files and/or other profiles.
# Files are relative to $HOME/.local/prompter/library

[python.api]
depends_on = ["a/b/c.md", "f/g/h.md"]

[general.testing]
depends_on = ["python.api", "a/b/d.md"]
"#;
        fs::write(&cfg_path, default_cfg).map_err(|source| PrompterError::Io {
            path: cfg_path.clone(),
            source,
        })?;
    }

    let paths_and_contents: Vec<(PathBuf, &str)> = vec![
        (
            lib.join("a/b/c.md"),
            "# a/b/c.md\nExample snippet for python.api.\n",
        ),
        (lib.join("a/b.md"), "# a/b.md\nFolder-level notes.\n"),
        (
            lib.join("a/b/d.md"),
            "# a/b/d.md\nGeneral testing snippet.\n",
        ),
        (lib.join("f/g/h.md"), "# f/g/h.md\nShared helper snippet.\n"),
    ];

    for (path, contents) in paths_and_contents {
        if let Some(ref pb) = pb {
            pb.set_message(format!(
                "Creating {}",
                path.file_name().unwrap_or_default().to_string_lossy()
            ));
        }
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).map_err(|source| PrompterError::Io {
                path: parent.to_path_buf(),
                source,
            })?;
        }
        if !path.exists() {
            fs::write(&path, contents).map_err(|source| PrompterError::Io {
                path: path.clone(),
                source,
            })?;
        }
    }

    if let Some(pb) = pb {
        pb.finish_with_message("Initialization complete!");
        std::thread::sleep(std::time::Duration::from_millis(200)); // Brief pause to show completion
    }

    println!(
        "{}",
        success_message(&format!("Initialized config at {}", cfg_path.display()))
    );
    println!(
        "{}",
        info_message(&format!("Library root at {}", lib.display()))
    );
    Ok(())
}