added view of yaml files (dummy view for now); possible switching between tags
This commit is contained in:
parent
30f3050c6c
commit
88cd2122e4
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -2,6 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -532,6 +541,7 @@ name = "query-docker-tags"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -606,6 +616,23 @@ dependencies = [
|
|||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -12,3 +12,4 @@ reqwest = { version = "0.11.4", features = ["blocking", "json"] }
|
|||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
tui = "0.16"
|
tui = "0.16"
|
||||||
termion = "1.5"
|
termion = "1.5"
|
||||||
|
regex = "1.5.4"
|
||||||
|
46
src/ui.rs
46
src/ui.rs
@ -10,26 +10,30 @@ use tui::Terminal;
|
|||||||
|
|
||||||
use crate::tags;
|
use crate::tags;
|
||||||
use crate::widget::repo_entry;
|
use crate::widget::repo_entry;
|
||||||
|
use crate::widget::service_switcher;
|
||||||
use crate::widget::tag_list;
|
use crate::widget::tag_list;
|
||||||
|
|
||||||
pub struct Ui {
|
pub struct Ui {
|
||||||
state: State,
|
state: State,
|
||||||
repo: crate::widget::repo_entry::RepoEntry,
|
repo: crate::widget::repo_entry::RepoEntry,
|
||||||
tags: crate::widget::tag_list::TagList,
|
tags: crate::widget::tag_list::TagList,
|
||||||
|
services: crate::widget::service_switcher::ServiceSwitcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
EditRepo,
|
EditRepo,
|
||||||
SelectTag,
|
SelectTag,
|
||||||
|
SelectService,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ui {
|
impl Ui {
|
||||||
pub fn run(repo_id: &str) {
|
pub fn run(repo_id: &str) {
|
||||||
let mut ui = Ui {
|
let mut ui = Ui {
|
||||||
state: State::EditRepo,
|
state: State::SelectService,
|
||||||
repo: repo_entry::RepoEntry::new(repo_id),
|
repo: repo_entry::RepoEntry::new(repo_id),
|
||||||
tags: tag_list::TagList::new(vec![String::from("Fetching Tags")]),
|
tags: tag_list::TagList::new(vec![String::from("Fetching Tags")]),
|
||||||
|
services: service_switcher::ServiceSwitcher::new(),
|
||||||
};
|
};
|
||||||
ui.tags = tag_list::TagList::new_with_result(tags::Tags::get_tags(ui.repo.get()));
|
ui.tags = tag_list::TagList::new_with_result(tags::Tags::get_tags(ui.repo.get()));
|
||||||
|
|
||||||
@ -48,12 +52,21 @@ impl Ui {
|
|||||||
.draw(|rect| {
|
.draw(|rect| {
|
||||||
let chunks = Layout::default()
|
let chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Min(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Max(7),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
.split(rect.size());
|
.split(rect.size());
|
||||||
|
|
||||||
rect.render_widget(ui.repo.render(&ui.state), chunks[0]);
|
let (list, state) = ui.services.render(&ui.state);
|
||||||
|
rect.render_stateful_widget(list, chunks[0], state);
|
||||||
|
rect.render_widget(ui.repo.render(&ui.state), chunks[1]);
|
||||||
let (list, state) = ui.tags.render(&ui.state);
|
let (list, state) = ui.tags.render(&ui.state);
|
||||||
rect.render_stateful_widget(list, chunks[1], state);
|
rect.render_stateful_widget(list, chunks[2], state);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -62,7 +75,8 @@ impl Ui {
|
|||||||
Ok(Key::Char('\t')) => {
|
Ok(Key::Char('\t')) => {
|
||||||
ui.state = match ui.state {
|
ui.state = match ui.state {
|
||||||
State::EditRepo => State::SelectTag,
|
State::EditRepo => State::SelectTag,
|
||||||
State::SelectTag => State::EditRepo,
|
State::SelectTag => State::SelectService,
|
||||||
|
State::SelectService => State::EditRepo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(Key::Ctrl('q')) => break 'core, //quit program without saving
|
Ok(Key::Ctrl('q')) => break 'core, //quit program without saving
|
||||||
@ -92,6 +106,26 @@ impl Ui {
|
|||||||
ui.repo.handle_input(&ui.state, Key::Backspace);
|
ui.repo.handle_input(&ui.state, Key::Backspace);
|
||||||
ui.tags.handle_input(&ui.state, Key::Backspace);
|
ui.tags.handle_input(&ui.state, Key::Backspace);
|
||||||
}
|
}
|
||||||
|
Ok(Key::Up) => {
|
||||||
|
if ui.state == State::SelectService && ui.services.find_previous_match() {
|
||||||
|
match ui.services.extract_repo() {
|
||||||
|
Err(_) => (), //TODO handle
|
||||||
|
Ok(s) => ui.repo.set(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.repo.handle_input(&ui.state, Key::Up);
|
||||||
|
ui.tags.handle_input(&ui.state, Key::Up);
|
||||||
|
}
|
||||||
|
Ok(Key::Down) => {
|
||||||
|
if ui.state == State::SelectService && ui.services.find_next_match() {
|
||||||
|
match ui.services.extract_repo() {
|
||||||
|
Err(_) => (), //TODO handle
|
||||||
|
Ok(s) => ui.repo.set(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.repo.handle_input(&ui.state, Key::Down);
|
||||||
|
ui.tags.handle_input(&ui.state, Key::Down);
|
||||||
|
}
|
||||||
Ok(key) => {
|
Ok(key) => {
|
||||||
ui.repo.handle_input(&ui.state, key);
|
ui.repo.handle_input(&ui.state, key);
|
||||||
ui.tags.handle_input(&ui.state, key);
|
ui.tags.handle_input(&ui.state, key);
|
||||||
@ -99,7 +133,7 @@ impl Ui {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
//sleep for 64ms (15 fps)
|
//sleep for 32ms (30 fps)
|
||||||
thread::sleep(std::time::Duration::from_millis(32));
|
thread::sleep(std::time::Duration::from_millis(32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub mod repo_entry;
|
pub mod repo_entry;
|
||||||
|
pub mod service_switcher;
|
||||||
pub mod tag_list;
|
pub mod tag_list;
|
||||||
|
@ -24,6 +24,11 @@ impl RepoEntry {
|
|||||||
self.text.clone()
|
self.text.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, entry: String) {
|
||||||
|
self.text = entry.clone();
|
||||||
|
self.old_text = entry;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render(&self, state: &crate::ui::State) -> Paragraph {
|
pub fn render(&self, state: &crate::ui::State) -> Paragraph {
|
||||||
let title = match self.changed {
|
let title = match self.changed {
|
||||||
true => "Repository*",
|
true => "Repository*",
|
||||||
|
141
src/widget/service_switcher.rs
Normal file
141
src/widget/service_switcher.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// use std::fs::File;
|
||||||
|
// use std::io::BufWriter;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use termion::event::Key;
|
||||||
|
use tui::style::{Color, Style};
|
||||||
|
use tui::widgets::{Block, Borders, List, ListState};
|
||||||
|
|
||||||
|
use crate::ui::State;
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
NoneSelected,
|
||||||
|
Parsing(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ServiceSwitcher {
|
||||||
|
list: Vec<String>,
|
||||||
|
state: ListState,
|
||||||
|
regex: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceSwitcher {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let list: Vec<String> = vec![
|
||||||
|
String::from("dies"),
|
||||||
|
String::from("ist"),
|
||||||
|
String::from(" image: rocketchat/rocket.chat:latest"),
|
||||||
|
String::from("ein"),
|
||||||
|
String::from("test"),
|
||||||
|
String::from(" image: sdfsfdsf:latest"),
|
||||||
|
];
|
||||||
|
Self {
|
||||||
|
list,
|
||||||
|
state: ListState::default(),
|
||||||
|
regex: Regex::new(r"^ *image *:.*").unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, state: &State) -> (List, &mut ListState) {
|
||||||
|
let border_style = if state == &State::SelectService {
|
||||||
|
Style::default().fg(Color::Green)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(Color::Gray)
|
||||||
|
};
|
||||||
|
|
||||||
|
let items: Vec<tui::widgets::ListItem> = self
|
||||||
|
.list
|
||||||
|
.iter()
|
||||||
|
.map(|l| {
|
||||||
|
tui::widgets::ListItem::new(l.as_ref())
|
||||||
|
.style(Style::default().fg(Color::White).bg(Color::Black))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Create a List from all list items and highlight the currently selected one
|
||||||
|
let items = List::new(items)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.title("Tags")
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(border_style),
|
||||||
|
)
|
||||||
|
.style(Style::default().fg(Color::White).bg(Color::Black))
|
||||||
|
.highlight_style(Style::default().bg(Color::Black))
|
||||||
|
.highlight_symbol(">>");
|
||||||
|
|
||||||
|
(items, &mut self.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_next_match(&mut self) -> bool {
|
||||||
|
let current_line: usize = match self.state.selected() {
|
||||||
|
None => 0,
|
||||||
|
Some(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut i = (current_line + 1) % self.list.len();
|
||||||
|
loop {
|
||||||
|
if i == current_line {
|
||||||
|
//looped through the list
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if line matches
|
||||||
|
if self.regex.is_match(&self.list[i]) {
|
||||||
|
self.state.select(Some(i));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = (i + 1) % self.list.len(); //iterate
|
||||||
|
}
|
||||||
|
|
||||||
|
//nothing found
|
||||||
|
self.state.select(None);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_previous_match(&mut self) -> bool {
|
||||||
|
let current_line: usize = match self.state.selected() {
|
||||||
|
None => 0,
|
||||||
|
Some(i) => i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut i: usize = if current_line == 0 {
|
||||||
|
self.list.len() - 1
|
||||||
|
} else {
|
||||||
|
current_line - 1
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if i == current_line {
|
||||||
|
//looped through the list
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if line matches
|
||||||
|
if self.regex.is_match(&self.list[i]) {
|
||||||
|
self.state.select(Some(i));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//iterate
|
||||||
|
i = if i == 0 { self.list.len() - 1 } else { i - 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
//nothing found
|
||||||
|
self.state.select(None);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_repo(&self) -> Result<String, Error> {
|
||||||
|
let regex = Regex::new(r"( *image *): *(.*[:.*]?) *").unwrap();
|
||||||
|
match self.state.selected() {
|
||||||
|
None => return Err(Error::NoneSelected),
|
||||||
|
Some(i) => {
|
||||||
|
let caps = regex.captures(&self.list[i]).unwrap();
|
||||||
|
let result: String = caps.get(2).unwrap().as_str().to_string();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user