diff --git a/hac-client/benches/collection_viewer_bench.rs b/hac-client/benches/collection_viewer_bench.rs index 90524f0..bbbe17a 100644 --- a/hac-client/benches/collection_viewer_bench.rs +++ b/hac-client/benches/collection_viewer_bench.rs @@ -1,8 +1,15 @@ -use std::sync::{Arc, RwLock}; +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, RwLock}, +}; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}; use hac_client::{ - pages::{collection_viewer::CollectionViewer, Eventful, Page}, + pages::{ + collection_viewer::{collection_store::CollectionStore, CollectionViewer}, + Eventful, Page, + }, utils::build_syntax_highlighted_lines, }; use hac_core::{ @@ -27,7 +34,7 @@ fn create_sample_collection() -> Collection { description: None, }, path: "any_path".into(), - requests: Some(vec![ + requests: Some(Arc::new(RwLock::new(vec![ RequestKind::Single(Arc::new(RwLock::new(Request { id: "any id".to_string(), name: "testing".to_string(), @@ -44,7 +51,7 @@ fn create_sample_collection() -> Collection { body: Some("[\r\n {\r\n \"id\": 1,\r\n \"name\": \"Leanne Graham\",\r\n \"username\": \"Bret\",\r\n \"email\": \"Sincere@april.biz\",\r\n \"address\": {\r\n \"street\": \"Kulas Light\",\r\n \"suite\": \"Apt. 556\",\r\n \"city\": \"Gwenborough\",\r\n \"zipcode\": \"92998-3874\",\r\n \"geo\": {\r\n \"lat\": \"-37.3159\",\r\n \"lng\": \"81.1496\"\r\n }\r\n },\r\n \"phone\": \"1-770-736-8031 x56442\",\r\n \"website\": \"hildegard.org\",\r\n \"company\": {\r\n \"name\": \"Romaguera-Crona\",\r\n \"catchPhrase\": \"Multi-layered client-server neural-net\",\r\n \"bs\": \"harness real-time e-markets\"\r\n }\r\n },\r\n {\r\n \"id\": 2,\r\n \"name\": \"Ervin Howell\",\r\n \"username\": \"Antonette\",\r\n \"email\": \"Shanna@melissa.tv\",\r\n \"address\": {\r\n \"street\": \"Victor Plains\",\r\n \"suite\": \"Suite 879\",\r\n \"city\": \"Wisokyburgh\",\r\n \"zipcode\": \"90566-7771\",\r\n \"geo\": {\r\n \"lat\": \"-43.9509\",\r\n \"lng\": \"-34.4618\"\r\n }\r\n },\r\n \"phone\": \"010-692-6593 x09125\",\r\n \"website\": \"anastasia.net\",\r\n \"company\": {\r\n \"name\": \"Deckow-Crist\",\r\n \"catchPhrase\": \"Proactive didactic contingency\",\r\n \"bs\": \"synergize scalable supply-chains\"\r\n }\r\n },\r\n {\r\n \"id\": 3,\r\n \"name\": \"Clementine Bauch\",\r\n \"username\": \"Samantha\",\r\n \"email\": \"Nathan@yesenia.net\",\r\n \"address\": {\r\n \"street\": \"Douglas Extension\",\r\n \"suite\": \"Suite 847\",\r\n \"city\": \"McKenziehaven\",\r\n \"zipcode\": \"59590-4157\",\r\n \"geo\": {\r\n \"lat\": \"-68.6102\",\r\n \"lng\": \"-47.0653\"\r\n }\r\n },\r\n \"phone\": \"1-463-123-4447\",\r\n \"website\": \"ramiro.info\",\r\n \"company\": {\r\n \"name\": \"Romaguera-Jacobson\",\r\n \"catchPhrase\": \"Face to face bifurcated interface\",\r\n \"bs\": \"e-enable strategic applications\"\r\n }\r\n },\r\n {\r\n \"id\": 4,\r\n \"name\": \"Patricia Lebsack\",\r\n \"username\": \"Karianne\",\r\n \"email\": \"Julianne.OConner@kory.org\",\r\n \"address\": {\r\n \"street\": \"Hoeger Mall\",\r\n \"suite\": \"Apt. 692\",\r\n \"city\": \"South Elvis\",\r\n \"zipcode\": \"53919-4257\",\r\n \"geo\": {\r\n \"lat\": \"29.4572\",\r\n \"lng\": \"-164.2990\"\r\n }\r\n },\r\n \"phone\": \"493-170-9623 x156\",\r\n \"website\": \"kale.biz\",\r\n \"company\": {\r\n \"name\": \"Robel-Corkery\",\r\n \"catchPhrase\": \"Multi-tiered zero tolerance productivity\",\r\n \"bs\": \"transition cutting-edge web services\"\r\n }\r\n },\r\n {\r\n \"id\": 5,\r\n \"name\": \"Chelsey Dietrich\",\r\n \"username\": \"Kamren\",\r\n \"email\": \"Lucio_Hettinger@annie.ca\",\r\n \"address\": {\r\n \"street\": \"Skiles Walks\",\r\n \"suite\": \"Suite 351\",\r\n \"city\": \"Roscoeview\",\r\n \"zipcode\": \"33263\",\r\n \"geo\": {\r\n \"lat\": \"-31.8129\",\r\n \"lng\": \"62.5342\"\r\n }\r\n },\r\n \"phone\": \"(254)954-1289\",\r\n \"website\": \"demarco.info\",\r\n \"company\": {\r\n \"name\": \"Keebler LLC\",\r\n \"catchPhrase\": \"User-centric fault-tolerant solution\",\r\n \"bs\": \"revolutionize end-to-end systems\"\r\n }\r\n },\r\n {\r\n \"id\": 6,\r\n \"name\": \"Mrs. Dennis Schulist\",\r\n \"username\": \"Leopoldo_Corkery\",\r\n \"email\": \"Karley_Dach@jasper.info\",\r\n \"address\": {\r\n \"street\": \"Norberto Crossing\",\r\n \"suite\": \"Apt. 950\",\r\n \"city\": \"South Christy\",\r\n \"zipcode\": \"23505-1337\",\r\n \"geo\": {\r\n \"lat\": \"-71.4197\",\r\n \"lng\": \"71.7478\"\r\n }\r\n },\r\n \"phone\": \"1-477-935-8478 x6430\",\r\n \"website\": \"ola.org\",\r\n \"company\": {\r\n \"name\": \"Considine-Lockman\",\r\n \"catchPhrase\": \"Synchronised bottom-line interface\",\r\n \"bs\": \"e-enable innovative applications\"\r\n }\r\n },\r\n {\r\n \"id\": 7,\r\n \"name\": \"Kurtis Weissnat\",\r\n \"username\": \"Elwyn.Skiles\",\r\n \"email\": \"Telly.Hoeger@billy.biz\",\r\n \"address\": {\r\n \"street\": \"Rex Trail\",\r\n \"suite\": \"Suite 280\",\r\n \"city\": \"Howemouth\",\r\n \"zipcode\": \"58804-1099\",\r\n \"geo\": {\r\n \"lat\": \"24.8918\",\r\n \"lng\": \"21.8984\"\r\n }\r\n },\r\n \"phone\": \"210.067.6132\",\r\n \"website\": \"elvis.io\",\r\n \"company\": {\r\n \"name\": \"Johns Group\",\r\n \"catchPhrase\": \"Configurable multimedia task-force\",\r\n \"bs\": \"generate enterprise e-tailers\"\r\n }\r\n },\r\n {\r\n \"id\": 8,\r\n \"name\": \"Nicholas Runolfsdottir V\",\r\n \"username\": \"Maxime_Nienow\",\r\n \"email\": \"Sherwood@rosamond.me\",\r\n \"address\": {\r\n \"street\": \"Ellsworth Summit\",\r\n \"suite\": \"Suite 729\",\r\n \"city\": \"Aliyaview\",\r\n \"zipcode\": \"45169\",\r\n \"geo\": {\r\n \"lat\": \"-14.3990\",\r\n \"lng\": \"-120.7677\"\r\n }\r\n },\r\n \"phone\": \"586.493.6943 x140\",\r\n \"website\": \"jacynthe.com\",\r\n \"company\": {\r\n \"name\": \"Abernathy Group\",\r\n \"catchPhrase\": \"Implemented secondary concept\",\r\n \"bs\": \"e-enable extensible e-tailers\"\r\n }\r\n },\r\n {\r\n \"id\": 9,\r\n \"name\": \"Glenna Reichert\",\r\n \"username\": \"Delphine\",\r\n \"email\": \"Chaim_McDermott@dana.io\",\r\n \"address\": {\r\n \"street\": \"Dayna Park\",\r\n \"suite\": \"Suite 449\",\r\n \"city\": \"Bartholomebury\",\r\n \"zipcode\": \"76495-3109\",\r\n \"geo\": {\r\n \"lat\": \"24.6463\",\r\n \"lng\": \"-168.8889\"\r\n }\r\n },\r\n \"phone\": \"(775)976-6794 x41206\",\r\n \"website\": \"conrad.com\",\r\n \"company\": {\r\n \"name\": \"Yost and Sons\",\r\n \"catchPhrase\": \"Switchable contextually-based project\",\r\n \"bs\": \"aggregate real-time technologies\"\r\n }\r\n },\r\n {\r\n \"id\": 10,\r\n \"name\": \"Clementina DuBuque\",\r\n \"username\": \"Moriah.Stanton\",\r\n \"email\": \"Rey.Padberg@karina.biz\",\r\n \"address\": {\r\n \"street\": \"Kattie Turnpike\",\r\n \"suite\": \"Suite 198\",\r\n \"city\": \"Lebsackbury\",\r\n \"zipcode\": \"31428-2261\",\r\n \"geo\": {\r\n \"lat\": \"-38.2386\",\r\n \"lng\": \"57.2232\"\r\n }\r\n },\r\n \"phone\": \"024-648-3804\",\r\n \"website\": \"ambrose.net\",\r\n \"company\": {\r\n \"name\": \"Hoeger LLC\",\r\n \"catchPhrase\": \"Centralized empowering task-force\",\r\n \"bs\": \"target end-to-end models\"\r\n }\r\n }\r\n]".to_string()), body_type: Some(BodyType::Json), }))), - ]) + ]))) } } @@ -67,7 +74,10 @@ fn handling_key_events() { let collection = create_sample_collection(); let size = Rect::new(0, 0, 80, 24); let config = hac_config::load_config(); - let mut api_explorer = CollectionViewer::new(size, collection, &colors, &config, false); + let mut store = CollectionStore::default(); + store.set_state(collection); + let mut api_explorer = + CollectionViewer::new(size, Rc::new(RefCell::new(store)), &colors, &config, false); let mut terminal = Terminal::new(TestBackend::new(size.width, size.height)).unwrap(); let mut frame = terminal.get_frame(); @@ -93,7 +103,10 @@ fn creating_with_highlight() { let collection = create_sample_collection(); let size = Rect::new(0, 0, 80, 24); let config = hac_config::load_config(); - let mut api_explorer = CollectionViewer::new(size, collection, &colors, &config, false); + let mut store = CollectionStore::default(); + store.set_state(collection); + let mut api_explorer = + CollectionViewer::new(size, Rc::new(RefCell::new(store)), &colors, &config, false); let mut terminal = Terminal::new(TestBackend::new(size.width, size.height)).unwrap(); let _frame = terminal.get_frame(); diff --git a/hac-client/src/pages/collection_viewer/collection_store.rs b/hac-client/src/pages/collection_viewer/collection_store.rs index 77ccba3..12acbf1 100644 --- a/hac-client/src/pages/collection_viewer/collection_store.rs +++ b/hac-client/src/pages/collection_viewer/collection_store.rs @@ -275,3 +275,160 @@ fn find_next_entry( found.then(|| path.pop()).flatten() } + +#[cfg(test)] +mod tests { + use super::*; + use hac_core::collection::types::{Directory, Request, RequestMethod}; + use std::collections::HashMap; + + fn create_root_one() -> RequestKind { + RequestKind::Single(Arc::new(RwLock::new(Request { + id: "root".to_string(), + method: RequestMethod::Get, + name: "Root1".to_string(), + uri: "/root1".to_string(), + body_type: None, + body: None, + }))) + } + + fn create_child_one() -> RequestKind { + RequestKind::Single(Arc::new(RwLock::new(Request { + id: "child_one".to_string(), + method: RequestMethod::Post, + name: "Child1".to_string(), + uri: "/nested1/child1".to_string(), + body_type: None, + body: None, + }))) + } + + fn create_child_two() -> RequestKind { + RequestKind::Single(Arc::new(RwLock::new(Request { + id: "child_two".to_string(), + method: RequestMethod::Put, + name: "Child2".to_string(), + uri: "/nested1/child2".to_string(), + body_type: None, + body: None, + }))) + } + + fn create_not_used() -> RequestKind { + RequestKind::Single(Arc::new(RwLock::new(Request { + id: "not_used".to_string(), + method: RequestMethod::Put, + name: "NotUsed".to_string(), + uri: "/not/used".to_string(), + body_type: None, + body: None, + }))) + } + + fn create_dir() -> Directory { + Directory { + id: "dir".to_string(), + name: "Nested1".to_string(), + requests: Arc::new(RwLock::new(vec![create_child_one(), create_child_two()])), + } + } + + fn create_nested() -> RequestKind { + RequestKind::Nested(create_dir()) + } + + fn create_root_two() -> RequestKind { + RequestKind::Single(Arc::new(RwLock::new(Request { + id: "root_two".to_string(), + method: RequestMethod::Delete, + name: "Root2".to_string(), + uri: "/root2".to_string(), + body_type: None, + body: None, + }))) + } + + fn create_test_tree() -> Vec { + vec![create_root_one(), create_nested(), create_root_two()] + } + + #[test] + fn test_visit_next_no_expanded() { + let tree = create_test_tree(); + let mut dirs_expanded = HashMap::new(); + dirs_expanded.insert(create_dir().id, false); + let needle = create_nested(); + let expected = create_root_two(); + + let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); + + assert!(next.is_some()); + assert_eq!(next.unwrap().get_id(), expected.get_id()); + } + + #[test] + fn test_visit_node_nested_next() { + let tree = create_test_tree(); + let mut dirs_expanded = HashMap::new(); + dirs_expanded.insert(create_dir().id, true); + let needle = create_nested(); + let expected = create_child_one(); + + let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); + + assert!(next.is_some()); + assert_eq!(next.unwrap().get_id(), expected.get_id()); + } + + #[test] + fn test_visit_node_no_match() { + let tree = create_test_tree(); + let mut dirs_expanded = HashMap::new(); + dirs_expanded.insert(create_dir().id, true); + let needle = create_not_used(); + + let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); + + assert!(next.is_none()); + } + + #[test] + fn test_visit_node_nested_prev() { + let tree = create_test_tree(); + let mut dirs_expanded = HashMap::new(); + dirs_expanded.insert(create_dir().id, true); + let needle = create_child_one(); + let expected = create_nested(); + + let next = find_next_entry(&tree, VisitNode::Prev, &dirs_expanded, &needle.get_id()); + + assert!(next.is_some()); + assert_eq!(next.unwrap().get_id(), expected.get_id()); + } + + #[test] + fn test_visit_prev_into_nested() { + let tree = create_test_tree(); + let mut dirs_expanded = HashMap::new(); + dirs_expanded.insert(create_dir().id, true); + let needle = create_root_two(); + let expected = create_child_two(); + + let next = find_next_entry(&tree, VisitNode::Prev, &dirs_expanded, &needle.get_id()); + + assert!(next.is_some()); + assert_eq!(next.unwrap().get_id(), expected.get_id()); + } + + #[test] + fn test_empty_tree() { + let tree = vec![]; + let dirs_expanded = HashMap::new(); + let needle = create_root_two(); + + let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); + + assert!(next.is_none()); + } +} diff --git a/hac-client/src/pages/collection_viewer/collection_viewer.rs b/hac-client/src/pages/collection_viewer/collection_viewer.rs index e5c193a..c795044 100644 --- a/hac-client/src/pages/collection_viewer/collection_viewer.rs +++ b/hac-client/src/pages/collection_viewer/collection_viewer.rs @@ -136,7 +136,6 @@ pub struct CollectionViewer<'cv> { sidebar: Sidebar<'cv>, colors: &'cv hac_colors::Colors, - config: &'cv hac_config::Config, layout: ExplorerLayout, global_command_sender: Option>, collection_sync_timer: std::time::Instant, @@ -167,26 +166,6 @@ impl<'cv> CollectionViewer<'cv> { let layout = build_layout(size); let (request_tx, response_rx) = unbounded_channel::(); - //let selected_request = collection.requests.as_ref().and_then(|requests| { - // requests.read().unwrap().first().and_then(|req| { - // if let RequestKind::Single(req) = req { - // Some(Arc::clone(req)) - // } else { - // None - // } - // }) - //}); - // - //let dirs_expanded = Rc::new(RefCell::new(HashMap::default())); - // - //let hovered_request = collection.requests.as_ref().and_then(|items| { - // items - // .read() - // .unwrap() - // .first() - // .map(|item| Rc::new(RefCell::new(item.get_id()))) - //}); - CollectionViewer { request_editor: ReqEditor::new( colors, @@ -198,7 +177,6 @@ impl<'cv> CollectionViewer<'cv> { sidebar: sidebar::Sidebar::new(colors, true, false, collection_store.clone()), colors, - config, layout, global_command_sender: None, collection_sync_timer: std::time::Instant::now(), @@ -964,160 +942,3 @@ pub fn build_layout(size: Rect) -> ExplorerLayout { create_req_form, } } - -#[cfg(test)] -mod tests { - use super::*; - use hac_core::collection::types::{Directory, Request, RequestMethod}; - use std::collections::HashMap; - - fn create_root_one() -> RequestKind { - RequestKind::Single(Arc::new(RwLock::new(Request { - id: "root".to_string(), - method: RequestMethod::Get, - name: "Root1".to_string(), - uri: "/root1".to_string(), - body_type: None, - body: None, - }))) - } - - fn create_child_one() -> RequestKind { - RequestKind::Single(Arc::new(RwLock::new(Request { - id: "child_one".to_string(), - method: RequestMethod::Post, - name: "Child1".to_string(), - uri: "/nested1/child1".to_string(), - body_type: None, - body: None, - }))) - } - - fn create_child_two() -> RequestKind { - RequestKind::Single(Arc::new(RwLock::new(Request { - id: "child_two".to_string(), - method: RequestMethod::Put, - name: "Child2".to_string(), - uri: "/nested1/child2".to_string(), - body_type: None, - body: None, - }))) - } - - fn create_not_used() -> RequestKind { - RequestKind::Single(Arc::new(RwLock::new(Request { - id: "not_used".to_string(), - method: RequestMethod::Put, - name: "NotUsed".to_string(), - uri: "/not/used".to_string(), - body_type: None, - body: None, - }))) - } - - fn create_dir() -> Directory { - Directory { - id: "dir".to_string(), - name: "Nested1".to_string(), - requests: vec![create_child_one(), create_child_two()], - } - } - - fn create_nested() -> RequestKind { - RequestKind::Nested(create_dir()) - } - - fn create_root_two() -> RequestKind { - RequestKind::Single(Arc::new(RwLock::new(Request { - id: "root_two".to_string(), - method: RequestMethod::Delete, - name: "Root2".to_string(), - uri: "/root2".to_string(), - body_type: None, - body: None, - }))) - } - - fn create_test_tree() -> Vec { - vec![create_root_one(), create_nested(), create_root_two()] - } - - #[test] - fn test_visit_next_no_expanded() { - let tree = create_test_tree(); - let mut dirs_expanded = HashMap::new(); - dirs_expanded.insert(create_dir().id, false); - let needle = create_nested(); - let expected = create_root_two(); - - let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); - - assert!(next.is_some()); - assert_eq!(next.unwrap().get_id(), expected.get_id()); - } - - #[test] - fn test_visit_node_nested_next() { - let tree = create_test_tree(); - let mut dirs_expanded = HashMap::new(); - dirs_expanded.insert(create_dir().id, true); - let needle = create_nested(); - let expected = create_child_one(); - - let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); - - assert!(next.is_some()); - assert_eq!(next.unwrap().get_id(), expected.get_id()); - } - - #[test] - fn test_visit_node_no_match() { - let tree = create_test_tree(); - let mut dirs_expanded = HashMap::new(); - dirs_expanded.insert(create_dir().id, true); - let needle = create_not_used(); - - let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); - - assert!(next.is_none()); - } - - #[test] - fn test_visit_node_nested_prev() { - let tree = create_test_tree(); - let mut dirs_expanded = HashMap::new(); - dirs_expanded.insert(create_dir().id, true); - let needle = create_child_one(); - let expected = create_nested(); - - let next = find_next_entry(&tree, VisitNode::Prev, &dirs_expanded, &needle.get_id()); - - assert!(next.is_some()); - assert_eq!(next.unwrap().get_id(), expected.get_id()); - } - - #[test] - fn test_visit_prev_into_nested() { - let tree = create_test_tree(); - let mut dirs_expanded = HashMap::new(); - dirs_expanded.insert(create_dir().id, true); - let needle = create_root_two(); - let expected = create_child_two(); - - let next = find_next_entry(&tree, VisitNode::Prev, &dirs_expanded, &needle.get_id()); - - assert!(next.is_some()); - assert_eq!(next.unwrap().get_id(), expected.get_id()); - } - - #[test] - fn test_empty_tree() { - let tree = vec![]; - let dirs_expanded = HashMap::new(); - let needle = create_root_two(); - - let next = find_next_entry(&tree, VisitNode::Next, &dirs_expanded, &needle.get_id()); - - assert!(next.is_none()); - } -} diff --git a/hac-client/src/pages/collection_viewer/request_editor.rs b/hac-client/src/pages/collection_viewer/request_editor.rs index 98d849b..ac0b780 100644 --- a/hac-client/src/pages/collection_viewer/request_editor.rs +++ b/hac-client/src/pages/collection_viewer/request_editor.rs @@ -157,10 +157,6 @@ impl<'re> ReqEditor<'re> { self.layout = build_layout(new_size); } - pub fn mode(&self) -> &EditorMode { - &self.editor_mode - } - fn draw_statusline(&self, buf: &mut Buffer, size: Rect) { let cursor_pos = self.cursor.readable_position();