Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 91 additions & 3 deletions oxen-rust/src/cli/src/cmd/workspace/list.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use async_trait::async_trait;
use clap::{ArgMatches, Command};
use colored::Colorize;
use liboxen::view::RemoteStagedStatus;
use std::path::Path;

use liboxen::api;
use liboxen::constants;
use liboxen::{error::OxenError, model::LocalRepository};

use crate::cmd::RunCmd;
Expand Down Expand Up @@ -45,16 +49,100 @@ impl RunCmd for WorkspaceListCmd {
return Ok(());
}

println!("id\tname\tcommit_id\tcommit_message");
println!("id\tname\tcommit_id\tcommit_message\tstatus");
for workspace in workspaces {
let ws_changes: WorkspaceChanges = api::client::workspaces::changes::list(
&remote_repo,
&workspace.id,
Path::new(""),
constants::DEFAULT_PAGE_NUM,
constants::DEFAULT_PAGE_SIZE,
)
.await
.into();

println!(
"{}\t{}\t{}\t{}",
"{}\t{}\t{}\t{}\t{}",
workspace.id,
workspace.name.unwrap_or("".to_string()),
workspace.commit.id,
workspace.commit.message
workspace.commit.message,
ws_changes,
);
}
Ok(())
}
}

/// The status of file changes in a workspace.
#[derive(Debug, Clone, PartialEq, Eq)]
enum WorkspaceChanges {
/// There are no changes in the workspace as compared to its commit.
Clean,
/// There are some file changes: tracks added, modified, and deleted files.
Changes {
added: usize,
modified: usize,
deleted: usize,
},
/// Unable to determine the status of the workspace.
Unknown,
}

/// Converts a Result<RemoteStagedStatus, OxenError> into either a Clean or Changes if its Ok otherwise its Unknown.
impl From<Result<RemoteStagedStatus, OxenError>> for WorkspaceChanges {
fn from(result: Result<RemoteStagedStatus, OxenError>) -> Self {
match result {
Ok(status) => (&status).into(),
Err(_) => WorkspaceChanges::Unknown,
}
}
}

/// Converts a RemoteStagedStatus into either a Clean or Changes variant of a WorkspaceChanges.
impl From<&RemoteStagedStatus> for WorkspaceChanges {
fn from(status: &RemoteStagedStatus) -> Self {
let added = status.added_files.total_entries;
let modified = status.modified_files.total_entries;
let deleted = status.removed_files.total_entries;

if added == 0 && modified == 0 && deleted == 0 {
WorkspaceChanges::Clean
} else {
WorkspaceChanges::Changes {
added,
modified,
deleted,
}
}
}
}

impl std::fmt::Display for WorkspaceChanges {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WorkspaceChanges::Clean => write!(f, "clean"),
WorkspaceChanges::Changes {
added,
modified,
deleted,
} => {
let status_entry = {
let mut parts = Vec::new();
if *added > 0 {
parts.push(format!("{}", format!("+{}", added).green()));
}
if *modified > 0 {
parts.push(format!("{}", format!("~{}", modified).yellow()));
}
if *deleted > 0 {
parts.push(format!("{}", format!("-{}", deleted).red()));
}
parts.join(" ")
};
write!(f, "{}", status_entry)
}
WorkspaceChanges::Unknown => write!(f, "unknown"),
}
}
}
59 changes: 59 additions & 0 deletions oxen-rust/src/lib/src/api/client/workspaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ mod tests {

use super::*;

use std::path::Path;

use crate::api;
use crate::command;
use crate::constants;
Expand Down Expand Up @@ -622,4 +624,61 @@ mod tests {
})
.await
}

#[tokio::test]
async fn test_list_workspaces_with_file_changes() -> Result<(), OxenError> {
test::run_readme_remote_repo_test(|_local_repo, remote_repo| async move {
let branch_name = DEFAULT_BRANCH_NAME;

// Create two workspaces - one clean, one with changes
let workspace_clean_id = "workspace_clean";
let workspace_with_changes_id = "workspace_with_changes";

create(&remote_repo, branch_name, workspace_clean_id).await?;
create(&remote_repo, branch_name, workspace_with_changes_id).await?;

// Add a file to workspace_with_changes
let test_file = test::test_img_file();
api::client::workspaces::files::upload_single_file(
&remote_repo,
workspace_with_changes_id,
"",
&test_file,
)
.await?;

// List all workspaces
let workspaces = list(&remote_repo).await?;
assert_eq!(workspaces.len(), 2);

// Verify the clean workspace has no changes
let clean_status = api::client::workspaces::changes::list(
&remote_repo,
workspace_clean_id,
Path::new(""),
constants::DEFAULT_PAGE_NUM,
constants::DEFAULT_PAGE_SIZE,
)
.await?;
assert_eq!(clean_status.added_files.total_entries, 0);
assert_eq!(clean_status.modified_files.total_entries, 0);
assert_eq!(clean_status.removed_files.total_entries, 0);

// Verify the workspace with changes has 1 added file
let changes_status = api::client::workspaces::changes::list(
&remote_repo,
workspace_with_changes_id,
Path::new(""),
constants::DEFAULT_PAGE_NUM,
constants::DEFAULT_PAGE_SIZE,
)
.await?;
assert_eq!(changes_status.added_files.total_entries, 1);
assert_eq!(changes_status.modified_files.total_entries, 0);
assert_eq!(changes_status.removed_files.total_entries, 0);

Ok(remote_repo)
})
.await
}
}
Loading