use eyre::Result;
use crate::system::history::checkpoint::Store;
use crate::system::history::store::{self, Entry};
use crate::system::history::tracked::TrackedSet;
mod describe;
mod diff;
mod ls;
pub(crate) mod show;
#[derive(Debug, usage_rs::Args)]
#[usage(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub(crate) struct DotfilesHistory {
#[usage(subcommand)]
command: Option<HistoryCommands>,
#[usage(flatten)]
ls: ls::HistoryLs,
}
#[derive(Debug, usage_rs::Subcommands)]
enum HistoryCommands {
Describe(describe::HistoryDescribe),
Diff(diff::HistoryDiff),
Ls(ls::HistoryLs),
Show(show::HistoryShow),
}
impl DotfilesHistory {
pub(crate) async fn run(self) -> Result<()> {
match self.command {
Some(HistoryCommands::Describe(cmd)) => cmd.run().await,
Some(HistoryCommands::Diff(cmd)) => cmd.run().await,
Some(HistoryCommands::Ls(cmd)) => cmd.run().await,
Some(HistoryCommands::Show(cmd)) => cmd.run().await,
None => self.ls.run().await,
}
}
}
pub(crate) async fn open() -> Result<(Store, TrackedSet, Vec<Entry>)> {
let store = Store::open()?;
let tracked = TrackedSet::effective().await?;
let entries = store.list()?;
Ok((store, tracked, entries))
}
pub(crate) fn resolve(spec: &str, entries: &[Entry], path: Option<&str>) -> Result<Entry> {
let scoped: Vec<Entry> = match path {
Some(path) => entries
.iter()
.filter(|entry| entry.checkpoint.changes.touches(path))
.cloned()
.collect(),
None => entries.to_vec(),
};
let id = if spec.starts_with("latest") {
store::resolve_ref(spec, &scoped)?
} else {
store::resolve_ref(spec, entries)?
};
entries
.iter()
.find(|entry| entry.id == id)
.cloned()
.ok_or_else(|| eyre::eyre!("no history checkpoint {id}"))
}
pub(crate) fn display_arg(path: &str) -> String {
crate::file::display_path(crate::system::history::tracked::normalize_target(
std::path::Path::new(path),
))
}
pub(crate) fn local_time(rfc3339: &str) -> String {
chrono::DateTime::parse_from_rfc3339(rfc3339)
.map(|time| {
time.with_timezone(&chrono::Local)
.format("%Y-%m-%d %H:%M")
.to_string()
})
.unwrap_or_else(|_| rfc3339.to_string())
}
pub(crate) fn short(oid: &str) -> String {
oid.chars().take(7).collect()
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise bootstrap dotfiles history</bold>
$ <bold>mise bootstrap dotfiles history --path ~/.config/hypr/bindings.lua</bold>
$ <bold>mise bootstrap dotfiles history show latest</bold>
$ <bold>mise bootstrap dotfiles history diff</bold> # the working tree against the latest checkpoint
$ <bold>mise bootstrap dotfiles history diff 11 12 --patch</bold>
$ <bold>mise bootstrap dotfiles save --description "before the theme change"</bold>
$ <bold>mise bootstrap dotfiles rollback ~/.config/hypr/bindings.lua</bold>
$ <bold>mise bootstrap dotfiles undo</bold>
"#
);