forgecode / crates /forge_main /src /sync_display.rs
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
d90101d verified
Raw
History Blame Contribute Delete
6.39 kB
use forge_domain::SyncProgress;
/// Extensions for formatting `SyncProgress` events as human-readable strings.
///
/// This module contains display logic for sync operation events, converting
/// them into user-friendly messages for the UI layer.
pub trait SyncProgressDisplay {
/// Returns a human-readable status message for this event.
///
/// Returns `None` for internal events that don't need user-facing messages.
fn message(&self) -> Option<String>;
}
impl SyncProgressDisplay for SyncProgress {
fn message(&self) -> Option<String> {
match self {
Self::Starting => Some("Initializing sync".to_string()),
Self::WorkspaceCreated { workspace_id } => {
Some(format!("Created workspace: {}", workspace_id))
}
Self::DiscoveringFiles { path: _, workspace_id } => {
Some(format!("Analyzing workspace: {workspace_id}"))
}
Self::FilesDiscovered { count: _ } => None,
Self::ComparingFiles { .. } => None,
Self::DiffComputed { added, deleted, modified } => {
let total = added + deleted + modified;
if total == 0 {
Some("Index is up to date".to_string())
} else {
let mut parts = Vec::new();
if *added > 0 {
parts.push(format!("{} added", added));
}
if *modified > 0 {
parts.push(format!("{} modified", modified));
}
if *deleted > 0 {
parts.push(format!("{} removed", deleted));
}
Some(format!("Change scan completed [{}]", parts.join(", ")))
}
}
Self::Syncing { current, total } => {
let width = total.to_string().len();
let file_word = pluralize(*total);
Some(format!(
"Syncing {:>width$}/{} {}",
current, total, file_word
))
}
Self::Completed { uploaded_files, total_files, failed_files } => {
if *uploaded_files == 0 && *failed_files == 0 {
Some(format!(
"Index up to date [{} {}]",
total_files,
pluralize(*total_files)
))
} else if *failed_files == 0 {
Some(format!(
"Sync completed successfully [{uploaded_files}/{total_files} {} updated]",
pluralize(*uploaded_files),
))
} else {
Some(format!(
"Sync completed with errors [{uploaded_files}/{total_files} {} updated, {failed_files} failed]",
pluralize(*uploaded_files),
))
}
}
}
}
}
/// Returns "file" or "files" based on count.
fn pluralize(count: usize) -> &'static str {
if count == 1 { "file" } else { "files" }
}
#[cfg(test)]
mod tests {
use forge_api::WorkspaceId;
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_starting_message() {
let fixture = SyncProgress::Starting;
let actual = fixture.message();
let expected = Some("Initializing sync".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_diff_computed_no_changes() {
let fixture = SyncProgress::DiffComputed { added: 0, deleted: 0, modified: 0 };
let actual = fixture.message();
let expected = Some("Index is up to date".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_diff_computed_with_changes() {
let fixture = SyncProgress::DiffComputed { added: 3, deleted: 1, modified: 2 };
let actual = fixture.message();
let expected = Some("Change scan completed [3 added, 2 modified, 1 removed]".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_syncing_single_file() {
let fixture = SyncProgress::Syncing { current: 1, total: 1 };
let actual = fixture.message();
let expected = Some("Syncing 1/1 file".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_syncing_multiple_files() {
let fixture = SyncProgress::Syncing { current: 5, total: 10 };
let actual = fixture.message();
let expected = Some("Syncing 5/10 files".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_completed_no_uploads() {
let fixture =
SyncProgress::Completed { uploaded_files: 0, total_files: 100, failed_files: 0 };
let actual = fixture.message();
let expected = Some("Index up to date [100 files]".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_completed_with_uploads() {
let fixture =
SyncProgress::Completed { uploaded_files: 5, total_files: 100, failed_files: 0 };
let actual = fixture.message();
let expected = Some("Sync completed successfully [5/100 files updated]".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_completed_with_failures() {
let fixture =
SyncProgress::Completed { uploaded_files: 5, total_files: 100, failed_files: 3 };
let actual = fixture.message();
let expected =
Some("Sync completed with errors [5/100 files updated, 3 failed]".to_string());
assert_eq!(actual, expected);
}
#[test]
fn test_discovering_files_returns_none() {
let workspace_id = WorkspaceId::generate();
let fixture = SyncProgress::DiscoveringFiles {
path: std::path::PathBuf::from("/some/path"),
workspace_id: workspace_id.clone(),
};
assert!(
fixture
.message()
.unwrap()
.contains(workspace_id.to_string().as_str())
);
}
#[test]
fn test_pluralize() {
assert_eq!(pluralize(0), "files");
assert_eq!(pluralize(1), "file");
assert_eq!(pluralize(2), "files");
assert_eq!(pluralize(100), "files");
}
}