changesette 6.2.1

A version and changelog manager using the changesets file format
use std::{env, fs};

use anyhow::{Context, Result, bail};
use tracing::info;

use crate::{
    output::display_path,
    pre::{self, PreJson, PreMode},
    workspace::Workspace,
};

/// Enters pre-release mode by writing `.changeset/pre.json` with `tag`; it
/// is an error to already be in pre mode.
pub(crate) fn enter(tag: &str) -> Result<()> {
    let workspace = Workspace::discover(&env::current_dir()?)?;
    let changeset_dir = workspace.root().join(".changeset");
    let pre = PreJson::load(&changeset_dir)?;

    // Checked before the tag, so that an existing pre.json with an invalid
    // tag still reports the state rather than the tag.
    if matches!(&pre, Some(pre) if pre.mode() == PreMode::Pre) {
        bail!("already in pre mode; run `changesette pre exit` to exit");
    }
    pre::validate_tag(tag)?;

    if let Some(mut pre) = pre {
        pre.set_mode(PreMode::Pre);
        pre.set_tag(tag);
        fs::write(pre.path(), pre.text()).with_context(|| display_path(pre.path()))?;
    } else {
        fs::create_dir_all(&changeset_dir).with_context(|| display_path(&changeset_dir))?;
        pre::write_new(&changeset_dir, tag)?;
    }

    info!(
        "Entered pre mode with tag `{tag}`\nRun `changesette version` to bump to prerelease versions"
    );
    Ok(())
}

/// Exits pre-release mode by flipping `.changeset/pre.json` to the exited
/// state; it is an error not to have a pre.json, while exiting twice
/// succeeds.
pub(crate) fn exit() -> Result<()> {
    let workspace = Workspace::discover(&env::current_dir()?)?;
    let changeset_dir = workspace.root().join(".changeset");
    let Some(mut pre) = PreJson::load(&changeset_dir)? else {
        bail!("not in pre mode; run `changesette pre enter <tag>` to enter");
    };

    pre.set_mode(PreMode::Exit);
    fs::write(pre.path(), pre.text()).with_context(|| display_path(pre.path()))?;

    info!("Exited pre mode\nRun `changesette version` to bump to final versions");
    Ok(())
}