use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use super::types::{Branch, BranchError};
#[derive(Debug, Clone)]
pub struct BranchRegistry {
branches: BTreeMap<String, Branch>,
current_branch: String,
next_guid: u64,
}
impl BranchRegistry {
pub fn new(initial_txg: u64, created: u64) -> Self {
let main = Branch::main(1, initial_txg, created);
let mut branches = BTreeMap::new();
branches.insert("main".to_string(), main);
Self {
branches,
current_branch: "main".to_string(),
next_guid: 2,
}
}
pub fn empty() -> Self {
Self {
branches: BTreeMap::new(),
current_branch: String::new(),
next_guid: 1,
}
}
pub fn current(&self) -> &str {
&self.current_branch
}
pub fn current_branch(&self) -> Option<&Branch> {
self.branches.get(&self.current_branch)
}
pub fn current_branch_mut(&mut self) -> Option<&mut Branch> {
self.branches.get_mut(&self.current_branch)
}
pub fn get(&self, name: &str) -> Option<&Branch> {
self.branches.get(name)
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut Branch> {
self.branches.get_mut(name)
}
pub fn contains(&self, name: &str) -> bool {
self.branches.contains_key(name)
}
pub fn len(&self) -> usize {
self.branches.len()
}
pub fn is_empty(&self) -> bool {
self.branches.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Branch> {
self.branches.values()
}
pub fn names(&self) -> impl Iterator<Item = &String> {
self.branches.keys()
}
pub fn default_branch(&self) -> Option<&Branch> {
self.branches.values().find(|b| b.is_default)
}
pub fn default_branch_name(&self) -> Option<&str> {
self.default_branch().map(|b| b.name.as_str())
}
pub fn validate_name(name: &str) -> Result<(), BranchError> {
if name.is_empty() {
return Err(BranchError::InvalidBranchName(
"branch name cannot be empty".into(),
));
}
if name.len() > 255 {
return Err(BranchError::InvalidBranchName(
"branch name too long (max 255 chars)".into(),
));
}
let invalid_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|', '\0', ' '];
for c in invalid_chars {
if name.contains(c) {
return Err(BranchError::InvalidBranchName(alloc::format!(
"branch name contains invalid character '{}'",
if c == '\0' { '0' } else { c }
)));
}
}
if name.starts_with('.') || name.starts_with('-') {
return Err(BranchError::InvalidBranchName(
"branch name cannot start with '.' or '-'".into(),
));
}
if name.ends_with('.') {
return Err(BranchError::InvalidBranchName(
"branch name cannot end with '.'".into(),
));
}
if name.contains("..") {
return Err(BranchError::InvalidBranchName(
"branch name cannot contain '..'".into(),
));
}
if name.contains("@{") {
return Err(BranchError::InvalidBranchName(
"branch name cannot contain '@{'".into(),
));
}
Ok(())
}
pub fn create_branch(&mut self, name: &str, created: u64) -> Result<u64, BranchError> {
Self::validate_name(name)?;
if self.branches.contains_key(name) {
return Err(BranchError::BranchExists(name.to_string()));
}
let (parent_name, fork_txg) = {
let current = self
.current_branch()
.ok_or_else(|| BranchError::Internal("no current branch".into()))?;
(current.name.clone(), current.head_txg)
};
let guid = self.next_guid;
self.next_guid += 1;
let branch = Branch::new(name.to_string(), guid, Some(parent_name), fork_txg, created);
self.branches.insert(name.to_string(), branch);
Ok(guid)
}
pub fn create_branch_from(
&mut self,
name: &str,
parent: &str,
created: u64,
) -> Result<u64, BranchError> {
Self::validate_name(name)?;
if self.branches.contains_key(name) {
return Err(BranchError::BranchExists(name.to_string()));
}
let fork_txg = {
let parent_branch = self
.branches
.get(parent)
.ok_or_else(|| BranchError::BranchNotFound(parent.to_string()))?;
parent_branch.head_txg
};
let guid = self.next_guid;
self.next_guid += 1;
let branch = Branch::new(
name.to_string(),
guid,
Some(parent.to_string()),
fork_txg,
created,
);
self.branches.insert(name.to_string(), branch);
Ok(guid)
}
pub fn create_branch_at_txg(
&mut self,
name: &str,
parent: &str,
txg: u64,
created: u64,
) -> Result<u64, BranchError> {
Self::validate_name(name)?;
if self.branches.contains_key(name) {
return Err(BranchError::BranchExists(name.to_string()));
}
if !self.branches.contains_key(parent) {
return Err(BranchError::BranchNotFound(parent.to_string()));
}
let guid = self.next_guid;
self.next_guid += 1;
let branch = Branch::new(
name.to_string(),
guid,
Some(parent.to_string()),
txg,
created,
);
self.branches.insert(name.to_string(), branch);
Ok(guid)
}
pub fn checkout(&mut self, name: &str) -> Result<&Branch, BranchError> {
if self.current_branch == name {
return Err(BranchError::AlreadyOnBranch(name.to_string()));
}
if !self.branches.contains_key(name) {
return Err(BranchError::BranchNotFound(name.to_string()));
}
self.current_branch = name.to_string();
Ok(self.branches.get(name).unwrap())
}
pub fn checkout_or_create(&mut self, name: &str, created: u64) -> Result<&Branch, BranchError> {
if !self.branches.contains_key(name) {
self.create_branch(name, created)?;
}
self.current_branch = name.to_string();
Ok(self.branches.get(name).unwrap())
}
pub fn delete_branch(&mut self, name: &str, force: bool) -> Result<Branch, BranchError> {
if self.current_branch == name {
return Err(BranchError::CannotDeleteCurrent(name.to_string()));
}
let branch = self
.branches
.get(name)
.ok_or_else(|| BranchError::BranchNotFound(name.to_string()))?;
if branch.is_default {
return Err(BranchError::CannotDeleteDefault(name.to_string()));
}
if !force && branch.txgs_since_fork() > 0 {
return Err(BranchError::UnmergedChanges(name.to_string()));
}
Ok(self.branches.remove(name).unwrap())
}
pub fn update_head(&mut self, name: &str, txg: u64) -> Result<(), BranchError> {
let branch = self
.branches
.get_mut(name)
.ok_or_else(|| BranchError::BranchNotFound(name.to_string()))?;
branch.head_txg = txg;
Ok(())
}
pub fn update_current_head(&mut self, txg: u64) -> Result<(), BranchError> {
let name = self.current_branch.clone();
self.update_head(&name, txg)
}
pub fn set_default(&mut self, name: &str) -> Result<(), BranchError> {
if !self.branches.contains_key(name) {
return Err(BranchError::BranchNotFound(name.to_string()));
}
for branch in self.branches.values_mut() {
branch.is_default = false;
}
self.branches.get_mut(name).unwrap().is_default = true;
Ok(())
}
pub fn rename(&mut self, old_name: &str, new_name: &str) -> Result<(), BranchError> {
Self::validate_name(new_name)?;
if !self.branches.contains_key(old_name) {
return Err(BranchError::BranchNotFound(old_name.to_string()));
}
if self.branches.contains_key(new_name) {
return Err(BranchError::BranchExists(new_name.to_string()));
}
let mut branch = self.branches.remove(old_name).unwrap();
branch.name = new_name.to_string();
self.branches.insert(new_name.to_string(), branch);
if self.current_branch == old_name {
self.current_branch = new_name.to_string();
}
for branch in self.branches.values_mut() {
if branch.parent.as_deref() == Some(old_name) {
branch.parent = Some(new_name.to_string());
}
}
Ok(())
}
pub fn children(&self, name: &str) -> Vec<&Branch> {
self.branches
.values()
.filter(|b| b.parent.as_deref() == Some(name))
.collect()
}
pub fn ancestry(&self, name: &str) -> Vec<&Branch> {
let mut result = Vec::new();
let mut current = name;
while let Some(branch) = self.branches.get(current) {
result.push(branch);
match &branch.parent {
Some(parent) => current = parent,
None => break,
}
}
result
}
pub fn common_ancestor(&self, branch_a: &str, branch_b: &str) -> Option<&Branch> {
let ancestry_a: Vec<_> = self.ancestry(branch_a);
let ancestry_b: Vec<_> = self.ancestry(branch_b);
for ancestor_a in &ancestry_a {
for ancestor_b in &ancestry_b {
if ancestor_a.name == ancestor_b.name {
return Some(ancestor_a);
}
}
}
None
}
pub fn merge_base_txg(&self, branch_a: &str, branch_b: &str) -> Option<u64> {
let a = self.branches.get(branch_a)?;
let b = self.branches.get(branch_b)?;
let ancestry_a: Vec<_> = self.ancestry(branch_a);
for ancestor in &ancestry_a {
if ancestor.name == branch_b {
return Some(a.fork_txg);
}
}
let ancestry_b: Vec<_> = self.ancestry(branch_b);
for ancestor in &ancestry_b {
if ancestor.name == branch_a {
return Some(b.fork_txg);
}
}
self.common_ancestor(branch_a, branch_b)
.map(|ancestor| ancestor.head_txg)
}
pub fn list_sorted(&self) -> Vec<&Branch> {
let mut branches: Vec<_> = self.branches.values().collect();
branches.sort_by(|a, b| a.name.cmp(&b.name));
branches
}
pub fn list_by_activity(&self) -> Vec<&Branch> {
let mut branches: Vec<_> = self.branches.values().collect();
branches.sort_by(|a, b| b.head_txg.cmp(&a.head_txg));
branches
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_registry() {
let registry = BranchRegistry::new(0, 1704067200);
assert_eq!(registry.len(), 1);
assert_eq!(registry.current(), "main");
assert!(registry.contains("main"));
let main = registry.get("main").unwrap();
assert!(main.is_default);
assert!(main.is_root());
}
#[test]
fn test_create_branch() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.update_head("main", 10).unwrap();
let guid = registry.create_branch("feature", 1704067200).unwrap();
assert_eq!(guid, 2);
assert!(registry.contains("feature"));
let feature = registry.get("feature").unwrap();
assert_eq!(feature.parent, Some("main".into()));
assert_eq!(feature.fork_txg, 10);
assert!(!feature.is_default);
}
#[test]
fn test_create_duplicate_branch() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
let result = registry.create_branch("feature", 1704067200);
assert!(matches!(result, Err(BranchError::BranchExists(_))));
}
#[test]
fn test_validate_name() {
assert!(BranchRegistry::validate_name("feature").is_ok());
assert!(BranchRegistry::validate_name("feature-123").is_ok());
assert!(BranchRegistry::validate_name("my_branch").is_ok());
assert!(BranchRegistry::validate_name("").is_err());
assert!(BranchRegistry::validate_name(".hidden").is_err());
assert!(BranchRegistry::validate_name("-dash").is_err());
assert!(BranchRegistry::validate_name("has/slash").is_err());
assert!(BranchRegistry::validate_name("has space").is_err());
assert!(BranchRegistry::validate_name("double..dot").is_err());
}
#[test]
fn test_checkout() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
registry.checkout("feature").unwrap();
assert_eq!(registry.current(), "feature");
let result = registry.checkout("feature");
assert!(matches!(result, Err(BranchError::AlreadyOnBranch(_))));
let result = registry.checkout("nonexistent");
assert!(matches!(result, Err(BranchError::BranchNotFound(_))));
}
#[test]
fn test_delete_branch() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
let deleted = registry.delete_branch("feature", true).unwrap();
assert_eq!(deleted.name, "feature");
assert!(!registry.contains("feature"));
}
#[test]
fn test_cannot_delete_current() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
registry.checkout("feature").unwrap();
let result = registry.delete_branch("feature", true);
assert!(matches!(result, Err(BranchError::CannotDeleteCurrent(_))));
}
#[test]
fn test_cannot_delete_default() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
registry.checkout("feature").unwrap();
let result = registry.delete_branch("main", true);
assert!(matches!(result, Err(BranchError::CannotDeleteDefault(_))));
}
#[test]
fn test_rename() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
registry.create_branch("child", 1704067200).unwrap();
{
let child = registry.get_mut("child").unwrap();
child.parent = Some("feature".into());
}
registry.rename("feature", "feature-v2").unwrap();
assert!(!registry.contains("feature"));
assert!(registry.contains("feature-v2"));
let child = registry.get("child").unwrap();
assert_eq!(child.parent, Some("feature-v2".into()));
}
#[test]
fn test_ancestry() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature", 1704067200).unwrap();
registry.checkout("feature").unwrap();
registry.create_branch("sub-feature", 1704067200).unwrap();
let ancestry = registry.ancestry("sub-feature");
assert_eq!(ancestry.len(), 3);
assert_eq!(ancestry[0].name, "sub-feature");
assert_eq!(ancestry[1].name, "feature");
assert_eq!(ancestry[2].name, "main");
}
#[test]
fn test_common_ancestor() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature1", 1704067200).unwrap();
registry.create_branch("feature2", 1704067200).unwrap();
let ancestor = registry.common_ancestor("feature1", "feature2");
assert!(ancestor.is_some());
assert_eq!(ancestor.unwrap().name, "main");
}
#[test]
fn test_children() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("feature1", 1704067200).unwrap();
registry.create_branch("feature2", 1704067200).unwrap();
let children = registry.children("main");
assert_eq!(children.len(), 2);
}
#[test]
fn test_set_default() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("develop", 1704067200).unwrap();
registry.set_default("develop").unwrap();
let develop = registry.get("develop").unwrap();
assert!(develop.is_default);
let main = registry.get("main").unwrap();
assert!(!main.is_default);
}
#[test]
fn test_update_head() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.update_head("main", 100).unwrap();
assert_eq!(registry.get("main").unwrap().head_txg, 100);
registry.update_current_head(200).unwrap();
assert_eq!(registry.get("main").unwrap().head_txg, 200);
}
#[test]
fn test_checkout_or_create() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.checkout_or_create("feature", 1704067200).unwrap();
assert_eq!(registry.current(), "feature");
assert!(registry.contains("feature"));
registry.checkout("main").unwrap();
registry.checkout_or_create("feature", 1704067200).unwrap();
assert_eq!(registry.current(), "feature");
}
#[test]
fn test_list_sorted() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("zebra", 1704067200).unwrap();
registry.create_branch("alpha", 1704067200).unwrap();
let sorted = registry.list_sorted();
assert_eq!(sorted[0].name, "alpha");
assert_eq!(sorted[1].name, "main");
assert_eq!(sorted[2].name, "zebra");
}
#[test]
fn test_list_by_activity() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("old", 1704067200).unwrap();
registry.create_branch("new", 1704067200).unwrap();
registry.update_head("new", 100).unwrap();
registry.update_head("old", 50).unwrap();
let by_activity = registry.list_by_activity();
assert_eq!(by_activity[0].name, "new");
assert_eq!(by_activity[1].name, "old");
}
#[test]
fn test_create_branch_from() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.create_branch("develop", 1704067200).unwrap();
registry.update_head("develop", 50).unwrap();
let guid = registry
.create_branch_from("feature", "develop", 1704067200)
.unwrap();
assert!(guid > 0);
let feature = registry.get("feature").unwrap();
assert_eq!(feature.parent, Some("develop".into()));
assert_eq!(feature.fork_txg, 50);
}
#[test]
fn test_create_branch_at_txg() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.update_head("main", 100).unwrap();
let guid = registry
.create_branch_at_txg("historical", "main", 50, 1704067200)
.unwrap();
assert!(guid > 0);
let historical = registry.get("historical").unwrap();
assert_eq!(historical.fork_txg, 50);
assert_eq!(historical.head_txg, 50);
}
#[test]
fn test_merge_base_txg() {
let mut registry = BranchRegistry::new(0, 1704067200);
registry.update_head("main", 100).unwrap();
registry.create_branch("feature1", 1704067200).unwrap();
registry.update_head("feature1", 150).unwrap();
registry.create_branch("feature2", 1704067200).unwrap();
registry.update_head("feature2", 120).unwrap();
let base = registry.merge_base_txg("feature1", "feature2");
assert!(base.is_some());
assert_eq!(base.unwrap(), 100);
}
}