Add repo add and repo show commands

This commit is contained in:
Joscha 2025-04-30 00:49:04 +02:00
parent b922af9283
commit 2c5ff584db
10 changed files with 219 additions and 4 deletions

View file

@ -8,9 +8,11 @@ mod lockfile;
mod v0;
mod v1;
pub use crate::repo::VERSION as REPO_VERSION;
pub use self::{
datadir::{LockedDataDir, UnlockedDataDir},
v1::{State, VERSION, load_state, tidy},
v1::{State, VERSION, add_repo, load_repo, load_repo_version, load_state, tidy},
};
fn migrate(dir: &LockedDataDir) -> anyhow::Result<()> {

View file

@ -3,7 +3,10 @@ use std::{collections::HashMap, fs, path::PathBuf};
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
use crate::ids::RepoId;
use crate::{
ids::RepoId,
repo::{self, Repo},
};
use super::{LockedDataDir, UnlockedDataDir};
@ -23,6 +26,23 @@ impl State {
}
}
}
pub fn resolve_repo_identifier(&self, identifier: &str) -> Option<RepoId> {
// If the identifier is a valid repo id, always interpret it as such.
// There must always be an unambiguous way to refer to repos.
if let Ok(id) = identifier.parse::<RepoId>() {
if self.repos.contains_key(&id) {
return Some(id);
}
}
// Otherwise, interpret the identifier as a repo name and find the
// corresponding id.
self.repos
.iter()
.find(|(_, name)| *name == identifier)
.map(|(id, _)| *id)
}
}
pub fn state_file(dir: &UnlockedDataDir) -> PathBuf {
@ -46,6 +66,14 @@ pub fn save_state(dir: &LockedDataDir, mut state: State) -> anyhow::Result<()> {
dir.write_json(&state_file(dir), &state)
}
pub fn load_repo_version(dir: &UnlockedDataDir, id: RepoId) -> anyhow::Result<u32> {
repo::load_version(&repo_dir(dir, id))
}
pub fn load_repo(dir: &UnlockedDataDir, id: RepoId) -> anyhow::Result<Repo> {
repo::load(&repo_dir(dir, id))
}
pub fn add_repo(dir: &LockedDataDir, name: String) -> anyhow::Result<RepoId> {
let id = RepoId::new();
@ -53,8 +81,8 @@ pub fn add_repo(dir: &LockedDataDir, name: String) -> anyhow::Result<RepoId> {
state.repos.insert(id, name);
save_state(dir, state)?;
fs::create_dir_all(repos_dir(dir))?;
// TODO Initialize bare repo
repo::init(&repo_dir(dir, id))?;
Ok(id)
}

View file

@ -1,5 +1,6 @@
pub mod data;
pub mod ids;
mod repo;
pub const PROPER_NAME: &str = "GedächtNAS";
pub const TECHNICAL_NAME: &str = "gedaechtnas";

59
gdn/src/repo.rs Normal file
View file

@ -0,0 +1,59 @@
mod v0;
mod v1;
use std::path::Path;
use anyhow::{anyhow, bail};
use git2::{ErrorCode, Repository};
pub use self::v1::{Note, Repo, VERSION};
const VERSION_FILE: &str = "VERSION";
pub fn init(path: &Path) -> anyhow::Result<()> {
Repository::init_bare(path)?;
Ok(())
}
fn read_version(repo: &Repository) -> anyhow::Result<u32> {
let head = match repo.head() {
Ok(head) => head,
Err(error) if error.code() == ErrorCode::UnbornBranch => return Ok(0),
Err(error) => Err(error)?,
};
let object = head
.peel_to_commit()?
.tree()?
.get_path(VERSION_FILE.as_ref())?
.to_object(repo)?;
let blob = object
.as_blob()
.ok_or(anyhow!("Failed to read file {VERSION_FILE}"))?;
let content = blob.content().to_vec();
let version = String::from_utf8(content)?.trim().parse::<u32>()?;
Ok(version)
}
pub fn load_version(path: &Path) -> anyhow::Result<u32> {
let repo = Repository::open_bare(path)?;
let version = read_version(&repo)?;
Ok(version)
}
pub fn load(path: &Path) -> anyhow::Result<Repo> {
let repository = Repository::open_bare(path)?;
let version = read_version(&repository)?;
#[expect(unused_qualifications)]
let repo = match version {
0 => v0::Repo::load().migrate(),
1 => v1::Repo::load(&repository)?.migrate(),
n => bail!("invalid repo version {n}"),
};
Ok(repo)
}

20
gdn/src/repo/v0.rs Normal file
View file

@ -0,0 +1,20 @@
use std::collections::HashMap;
use super::v1;
pub const VERSION: u32 = 0;
pub struct Repo;
impl Repo {
pub fn load() -> Self {
Self
}
pub fn migrate(self) -> super::Repo {
v1::Repo {
notes: HashMap::new(),
}
.migrate()
}
}

29
gdn/src/repo/v1.rs Normal file
View file

@ -0,0 +1,29 @@
use std::collections::HashMap;
use git2::Repository;
use serde::{Deserialize, Serialize};
use crate::ids::NoteId;
pub const VERSION: u32 = 1;
#[derive(Serialize, Deserialize)]
pub struct Note {
pub text: String,
pub children: Vec<NoteId>,
}
#[derive(Default)]
pub struct Repo {
pub notes: HashMap<NoteId, Note>,
}
impl Repo {
pub fn load(repository: &Repository) -> anyhow::Result<Self> {
todo!()
}
pub fn migrate(self) -> super::Repo {
self
}
}