From 3a3a2de8dccafba314b68b2acee919e90e5ee73d Mon Sep 17 00:00:00 2001 From: Joscha Date: Tue, 29 Apr 2025 01:33:28 +0200 Subject: [PATCH] Store repo names and selected repo in state --- gdn-cli/src/commands/status.rs | 18 +++++-- gdn/src/data.rs | 2 +- gdn/src/data/v0.rs | 2 +- gdn/src/data/v1.rs | 85 ++++++++++++++++++++++++++++++++-- 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/gdn-cli/src/commands/status.rs b/gdn-cli/src/commands/status.rs index 363f238..d819551 100644 --- a/gdn-cli/src/commands/status.rs +++ b/gdn-cli/src/commands/status.rs @@ -9,12 +9,22 @@ pub struct Command {} impl Command { pub fn run(self, env: &Environment) -> anyhow::Result<()> { let data_dir = gdn::data::open(env.data_dir.clone())?; + let state = gdn::data::load_state(&data_dir)?; + println!("Data dir version: {}", gdn::data::VERSION); - let state = gdn::data::load_state(&data_dir)?; - match state.name { - Some(name) => println!("Name: {name}"), - None => println!("No name"), + println!(); + if state.repos.is_empty() { + println!("No repos"); + } else { + println!("Repos ({}):", state.repos.len()); + for (id, name) in &state.repos { + if state.selected_repo == Some(*id) { + println!("- {name} ({id}, selected)"); + } else { + println!("- {name} ({id})"); + } + } } Ok(()) diff --git a/gdn/src/data.rs b/gdn/src/data.rs index dca543e..b47ca10 100644 --- a/gdn/src/data.rs +++ b/gdn/src/data.rs @@ -10,7 +10,7 @@ mod v1; pub use self::{ datadir::{LockedDataDir, UnlockedDataDir}, - v1::{State, VERSION, load_state, save_state}, + v1::{State, VERSION, load_state, tidy}, }; fn migrate(dir: &LockedDataDir) -> anyhow::Result<()> { diff --git a/gdn/src/data/v0.rs b/gdn/src/data/v0.rs index a6a452d..ad8c297 100644 --- a/gdn/src/data/v0.rs +++ b/gdn/src/data/v0.rs @@ -6,7 +6,7 @@ pub const VERSION: u32 = 0; pub fn migrate(dir: &LockedDataDir) -> anyhow::Result<()> { dir.require_version(VERSION)?; - v1::save_state(dir, &v1::State::default())?; + v1::save_state(dir, v1::State::default())?; dir.write_version(v1::VERSION)?; Ok(()) } diff --git a/gdn/src/data/v1.rs b/gdn/src/data/v1.rs index 863426c..0932fea 100644 --- a/gdn/src/data/v1.rs +++ b/gdn/src/data/v1.rs @@ -1,24 +1,101 @@ -use std::path::PathBuf; +use std::{collections::HashMap, fs, path::PathBuf}; +use anyhow::anyhow; use serde::{Deserialize, Serialize}; +use crate::ids::RepoId; + use super::{LockedDataDir, UnlockedDataDir}; pub const VERSION: u32 = 1; #[derive(Default, Serialize, Deserialize)] pub struct State { - pub name: Option, + pub repos: HashMap, + pub selected_repo: Option, +} + +impl State { + pub fn normalize(&mut self) { + if let Some(selected) = self.selected_repo { + if !self.repos.contains_key(&selected) { + self.selected_repo = None; + } + } + } } pub fn state_file(dir: &UnlockedDataDir) -> PathBuf { dir.path().join("state.json") } +pub fn repos_dir(dir: &UnlockedDataDir) -> PathBuf { + dir.path().join("repos") +} + +pub fn repo_dir(dir: &UnlockedDataDir, id: RepoId) -> PathBuf { + repos_dir(dir).join(id.to_string()) +} + pub fn load_state(dir: &UnlockedDataDir) -> anyhow::Result { dir.read_json(&state_file(dir)) } -pub fn save_state(dir: &LockedDataDir, state: &State) -> anyhow::Result<()> { - dir.write_json(&state_file(dir), state) +pub fn save_state(dir: &LockedDataDir, mut state: State) -> anyhow::Result<()> { + state.normalize(); + dir.write_json(&state_file(dir), &state) +} + +pub fn add_repo(dir: &LockedDataDir, name: String) -> anyhow::Result { + let id = RepoId::new(); + + let mut state = load_state(dir)?; + state.repos.insert(id, name); + save_state(dir, state)?; + + fs::create_dir_all(repos_dir(dir))?; + // TODO Initialize bare repo + Ok(id) +} + +pub fn remove_repo(dir: &LockedDataDir, id: RepoId) -> anyhow::Result<()> { + let mut state = load_state(dir)?; + state.repos.remove(&id).is_none(); + save_state(dir, state)?; + + // TODO Check if this works with read-only files + fs::remove_dir_all(repo_dir(dir, id))?; + + Ok(()) +} + +pub fn set_repo_name(dir: &LockedDataDir, id: RepoId, name: String) -> anyhow::Result<()> { + let mut state = load_state(dir)?; + *state + .repos + .get_mut(&id) + .ok_or_else(|| anyhow!("no repo with id {id}"))? = name; + save_state(dir, state)?; + Ok(()) +} + +pub fn select_repo(dir: &LockedDataDir, id: Option) -> anyhow::Result<()> { + let mut state = load_state(dir)?; + state.selected_repo = id; + save_state(dir, state)?; + Ok(()) +} + +pub fn tidy(dir: &LockedDataDir) -> anyhow::Result<()> { + let state = load_state(dir)?; + + // TODO Detect repo dirs that should not exist, and let the user now. + // + // The repo dir contains very important user data. To avoid data loss, we + // must not delete files or directories that should not exist. Instead, we + // should let the user know so they can check if they want to keep them. + + // TODO Create repos that don't exist. + + Ok(()) }