Skip to content

Commit

Permalink
Merge pull request #44 from lusingander/version
Browse files Browse the repository at this point in the history
Fix contents of copy detail dialog when selecting a version
  • Loading branch information
lusingander authored Jan 12, 2025
2 parents dac376b + f4352bf commit 3caa830
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,13 @@ impl Client {
let version_id = v.version_id().unwrap().to_string(); // returns "null" if empty...
let size_byte = v.size().unwrap() as usize;
let last_modified = convert_datetime(v.last_modified().unwrap());
let e_tag = v.e_tag().unwrap().trim_matches('"').to_string();
let is_latest = v.is_latest().unwrap();
FileVersion {
version_id,
size_byte,
last_modified,
e_tag,
is_latest,
}
})
Expand Down
14 changes: 14 additions & 0 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,24 @@ pub struct FileVersion {
pub version_id: String,
pub size_byte: usize,
pub last_modified: DateTime<Local>,
pub e_tag: String,
#[allow(dead_code)]
pub is_latest: bool,
}

impl FileVersion {
pub fn s3_uri(&self, base_file_detail: &FileDetail) -> String {
format!("{}?versionId={}", base_file_detail.s3_uri, self.version_id)
}

pub fn object_url(&self, base_file_detail: &FileDetail) -> String {
format!(
"{}?versionId={}",
base_file_detail.object_url, self.version_id
)
}
}

#[derive(Debug, Default)]
pub struct AppObjects {
bucket_items: Vec<BucketItem>,
Expand Down
114 changes: 105 additions & 9 deletions src/pages/object_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Tab {
enum ViewState {
Default,
SaveDialog(InputDialogState),
CopyDetailDialog(CopyDetailDialogState),
CopyDetailDialog(Box<CopyDetailDialogState>),
}

impl ObjectDetailPage {
Expand Down Expand Up @@ -355,9 +355,19 @@ impl ObjectDetailPage {
}

fn open_copy_detail_dialog(&mut self) {
self.view_state = ViewState::CopyDetailDialog(CopyDetailDialogState::object_detail(
self.file_detail.clone(),
));
match self.tab {
Tab::Detail(_) => {
self.view_state = ViewState::CopyDetailDialog(Box::new(
CopyDetailDialogState::object_detail(self.file_detail.clone()),
));
}
Tab::Version(_) => {
let version = self.current_selected_version().unwrap().clone();
self.view_state = ViewState::CopyDetailDialog(Box::new(
CopyDetailDialogState::object_version(self.file_detail.clone(), version),
));
}
}
}

fn close_copy_detail_dialog(&mut self) {
Expand Down Expand Up @@ -398,16 +408,18 @@ impl ObjectDetailPage {
.send(AppEventType::ObjectDetailOpenManagementConsole);
}

fn current_selected_version_id(&self) -> Option<String> {
fn current_selected_version(&self) -> Option<&FileVersion> {
match &self.tab {
Tab::Detail(_) => None,
Tab::Version(state) => self
.file_versions
.get(state.selected)
.map(|v| v.version_id.clone()),
Tab::Version(state) => self.file_versions.get(state.selected),
}
}

fn current_selected_version_id(&self) -> Option<String> {
self.current_selected_version()
.map(|v| v.version_id.clone())
}

pub fn current_object_key(&self) -> &ObjectKey {
&self.object_key
}
Expand Down Expand Up @@ -1158,6 +1170,88 @@ mod tests {
Ok(())
}

#[test]
fn test_render_copy_detail_dialog_version_tab() -> std::io::Result<()> {
let ctx = Rc::default();
let (tx, _) = event::new();
let mut terminal = setup_terminal()?;

let (items, file_detail, file_versions, object_key) = fixtures();
let items_len = items.len();
let mut page = ObjectDetailPage::new(
file_detail,
items,
object_key,
ScrollListState::new(items_len),
ctx,
tx,
);
page.set_versions(file_versions);
page.select_versions_tab();

let area = Rect::new(0, 0, 60, 20);

// Call render once to update the StatefulWidget
terminal.draw(|f| {
page.render(f, area);
})?;

page.handle_key(KeyEvent::from(KeyCode::Char('j')));
page.open_copy_detail_dialog();

terminal.draw(|f| {
page.render(f, area);
})?;

#[rustfmt::skip]
let mut expected = Buffer::with_lines([
"┌───────────────────── 1 / 3 ┐┌────────────────────────────┐",
"│ file1 ││ Detail │ Version │",
"│ file2 ││────────────────────────────│",
"│ file3 ││ Version ID: 60f36bc2-0f│",
"│ ╭Copy──────────────────────────────────────────────────╮ │",
"│ │ Key: │ │",
"│ │ file1 │ │",
"│ │ S3 URI: │ │",
"│ │ s3://bucket-1/file1?versionId=1c5d3bcc-2bb3-4cd5-8 │ │",
"│ │ ARN: │ │",
"│ │ arn:aws:s3:::bucket-1/file1 │ │",
"│ │ Object URL: │ │",
"│ │ https://bucket-1.s3.ap-northeast-1.amazonaws.com/f │ │",
"│ │ ETag: │ │",
"│ │ 6c5db847-d206-4a27-9723-713e3a6cad86 │ │",
"│ ╰──────────────────────────────────────────────────────╯ │",
"│ ││ │",
"│ ││ │",
"│ ││ │",
"└────────────────────────────┘└────────────────────────────┘",
]);
set_cells! { expected =>
// selected item
(2..28, [1]) => bg: Color::DarkGray, fg: Color::Black,
// "Version" is selected
(41..48, [1]) => fg: Color::Cyan, modifier: Modifier::BOLD,
// "Version ID" label
(33..48, [3]) => modifier: Modifier::BOLD,
// "Key" label
(4..8, [5]) => modifier: Modifier::BOLD,
// "S3 URI" label
(4..11, [7]) => modifier: Modifier::BOLD,
// "ARN" label
(4..8, [9]) => modifier: Modifier::BOLD,
// "Object URL" label
(4..15, [11]) => modifier: Modifier::BOLD,
// "ETag" label
(4..9, [13]) => modifier: Modifier::BOLD,
// "Key" is selected
(4..56, [5, 6]) => fg: Color::Cyan,
}

terminal.backend().assert_buffer(&expected);

Ok(())
}

fn setup_terminal() -> std::io::Result<Terminal<TestBackend>> {
let backend = TestBackend::new(60, 20);
let mut terminal = Terminal::new(backend)?;
Expand Down Expand Up @@ -1195,12 +1289,14 @@ mod tests {
version_id: "60f36bc2-0f38-47b8-9bf0-e24e334b86d5".to_string(),
size_byte: 1024 + 10,
last_modified: parse_datetime("2024-01-02 13:01:02"),
e_tag: "bef684de-a260-48a4-8178-8a535ecccadb".to_string(),
is_latest: true,
},
FileVersion {
version_id: "1c5d3bcc-2bb3-4cd5-875f-a95a6ae53f65".to_string(),
size_byte: 1024,
last_modified: parse_datetime("2024-01-01 23:59:59"),
e_tag: "6c5db847-d206-4a27-9723-713e3a6cad86".to_string(),
is_latest: false,
},
];
Expand Down
48 changes: 47 additions & 1 deletion src/widget/copy_detail_dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use ratatui::{

use crate::{
color::ColorTheme,
object::{BucketItem, FileDetail, ObjectItem},
object::{BucketItem, FileDetail, FileVersion, ObjectItem},
widget::{common::calc_centered_dialog_rect, Dialog},
};

Expand Down Expand Up @@ -121,10 +121,39 @@ impl ObjectDetailItemType {
}
}

#[derive(Default)]
#[zero_indexed_enum]
enum ObjectVersionItemType {
#[default]
Key,
S3Uri,
Arn,
ObjectUrl,
Etag,
}

impl ObjectVersionItemType {
fn name_and_value(
&self,
file_detail: &FileDetail,
file_version: &FileVersion,
) -> (String, String) {
let (name, value) = match self {
Self::Key => ("Key", &file_detail.key),
Self::S3Uri => ("S3 URI", &file_version.s3_uri(file_detail)),
Self::Arn => ("ARN", &file_detail.arn),
Self::ObjectUrl => ("Object URL", &file_version.object_url(file_detail)),
Self::Etag => ("ETag", &file_version.e_tag),
};
(name.into(), value.into())
}
}

#[derive(Debug)]
pub enum CopyDetailDialogState {
BucketList(BucketListItemType, BucketItem),
ObjectDetail(ObjectDetailItemType, FileDetail),
ObjectVersion(ObjectVersionItemType, FileDetail, FileVersion),
ObjectListFile(ObjectListFileItemType, ObjectItem),
ObjectListDir(ObjectListDirItemType, ObjectItem),
}
Expand All @@ -145,13 +174,18 @@ impl CopyDetailDialogState {
pub fn object_detail(file_detail: FileDetail) -> Self {
Self::ObjectDetail(ObjectDetailItemType::default(), file_detail)
}

pub fn object_version(file_detail: FileDetail, file_version: FileVersion) -> Self {
Self::ObjectVersion(ObjectVersionItemType::default(), file_detail, file_version)
}
}

impl CopyDetailDialogState {
pub fn select_next(&mut self) {
match self {
Self::BucketList(selected, _) => *selected = selected.next(),
Self::ObjectDetail(selected, _) => *selected = selected.next(),
Self::ObjectVersion(selected, _, _) => *selected = selected.next(),
Self::ObjectListFile(selected, _) => *selected = selected.next(),
Self::ObjectListDir(selected, _) => *selected = selected.next(),
}
Expand All @@ -161,6 +195,7 @@ impl CopyDetailDialogState {
match self {
Self::BucketList(selected, _) => *selected = selected.prev(),
Self::ObjectDetail(selected, _) => *selected = selected.prev(),
Self::ObjectVersion(selected, _, _) => *selected = selected.prev(),
Self::ObjectListFile(selected, _) => *selected = selected.prev(),
Self::ObjectListDir(selected, _) => *selected = selected.prev(),
}
Expand All @@ -170,6 +205,7 @@ impl CopyDetailDialogState {
match self {
Self::BucketList(selected, _) => selected.val(),
Self::ObjectDetail(selected, _) => selected.val(),
Self::ObjectVersion(selected, _, _) => selected.val(),
Self::ObjectListFile(selected, _) => selected.val(),
Self::ObjectListDir(selected, _) => selected.val(),
}
Expand All @@ -179,6 +215,9 @@ impl CopyDetailDialogState {
match self {
Self::BucketList(selected, bucket_item) => selected.name_and_value(bucket_item),
Self::ObjectDetail(selected, file_detail) => selected.name_and_value(file_detail),
Self::ObjectVersion(selected, file_detail, file_version) => {
selected.name_and_value(file_detail, file_version)
}
Self::ObjectListFile(selected, object_item) => selected.name_and_value(object_item),
Self::ObjectListDir(selected, object_item) => selected.name_and_value(object_item),
}
Expand All @@ -194,6 +233,12 @@ impl CopyDetailDialogState {
.into_iter()
.map(|t| t.name_and_value(file_detail))
.collect(),
Self::ObjectVersion(_, file_detail, file_version) => {
ObjectVersionItemType::vars_array()
.into_iter()
.map(|t| t.name_and_value(file_detail, file_version))
.collect()
}
Self::ObjectListFile(_, object_item) => ObjectListFileItemType::vars_array()
.into_iter()
.map(|t| t.name_and_value(object_item))
Expand All @@ -209,6 +254,7 @@ impl CopyDetailDialogState {
match self {
Self::BucketList(_, _) => BucketListItemType::len(),
Self::ObjectDetail(_, _) => ObjectDetailItemType::len(),
Self::ObjectVersion(_, _, _) => ObjectVersionItemType::len(),
Self::ObjectListFile(_, _) => ObjectListFileItemType::len(),
Self::ObjectListDir(_, _) => ObjectListDirItemType::len(),
}
Expand Down

0 comments on commit 3caa830

Please sign in to comment.