use super::id::{ChangeId, CommitId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Bookmark {
pub name: String,
pub remote: Option<String>,
pub is_tracked: bool,
}
impl Bookmark {
pub fn full_name(&self) -> String {
match &self.remote {
Some(remote) => format!("{}@{}", self.name, remote),
None => self.name.clone(),
}
}
pub fn is_untracked_remote(&self) -> bool {
self.remote.is_some() && !self.is_tracked
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BookmarkInfo {
pub bookmark: Bookmark,
pub change_id: Option<ChangeId>,
pub commit_id: Option<CommitId>,
pub description: Option<String>,
}
impl BookmarkInfo {
pub fn is_jumpable(&self) -> bool {
self.change_id.is_some()
}
pub fn display_label(&self, max_width: usize) -> String {
let name = self.bookmark.full_name();
let desc = self.description.as_deref().unwrap_or("(no description)");
let prefix = format!("{}: ", name);
let available = max_width.saturating_sub(prefix.len());
if desc.len() <= available {
format!("{}{}", prefix, desc)
} else if available > 3 {
format!("{}{}...", prefix, &desc[..available - 3])
} else {
prefix
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_name_local() {
let bookmark = Bookmark {
name: "main".into(),
remote: None,
is_tracked: true,
};
assert_eq!(bookmark.full_name(), "main");
}
#[test]
fn test_full_name_remote() {
let bookmark = Bookmark {
name: "feature-x".into(),
remote: Some("origin".into()),
is_tracked: false,
};
assert_eq!(bookmark.full_name(), "feature-x@origin");
}
#[test]
fn test_is_untracked_remote() {
let local = Bookmark {
name: "main".into(),
remote: None,
is_tracked: true,
};
assert!(!local.is_untracked_remote());
let tracked_remote = Bookmark {
name: "main".into(),
remote: Some("origin".into()),
is_tracked: true,
};
assert!(!tracked_remote.is_untracked_remote());
let untracked_remote = Bookmark {
name: "feature".into(),
remote: Some("origin".into()),
is_tracked: false,
};
assert!(untracked_remote.is_untracked_remote());
}
#[test]
fn test_bookmark_info_is_jumpable() {
let jumpable = BookmarkInfo {
bookmark: Bookmark {
name: "main".into(),
remote: None,
is_tracked: true,
},
change_id: Some(ChangeId::new("abc12345".to_string())),
commit_id: Some(CommitId::new("def67890".to_string())),
description: Some("Test commit".into()),
};
assert!(jumpable.is_jumpable());
let not_jumpable = BookmarkInfo {
bookmark: Bookmark {
name: "remote-only".into(),
remote: Some("origin".into()),
is_tracked: false,
},
change_id: None,
commit_id: None,
description: None,
};
assert!(!not_jumpable.is_jumpable());
}
#[test]
fn test_bookmark_info_display_label() {
let info = BookmarkInfo {
bookmark: Bookmark {
name: "main".into(),
remote: None,
is_tracked: true,
},
change_id: Some(ChangeId::new("abc12345".to_string())),
commit_id: Some(CommitId::new("def67890".to_string())),
description: Some("Fix critical bug".into()),
};
let label = info.display_label(40);
assert_eq!(label, "main: Fix critical bug");
let label_short = info.display_label(20);
assert!(label_short.ends_with("..."));
}
#[test]
fn test_bookmark_info_display_label_no_description() {
let info = BookmarkInfo {
bookmark: Bookmark {
name: "orphan".into(),
remote: None,
is_tracked: true,
},
change_id: Some(ChangeId::new("abc12345".to_string())),
commit_id: Some(CommitId::new("def67890".to_string())),
description: None,
};
let label = info.display_label(40);
assert_eq!(label, "orphan: (no description)");
}
}