added view of yaml files (dummy view for now); possible switching between tags

This commit is contained in:
Thomas Eppers 2021-08-30 00:28:12 +02:00
parent 30f3050c6c
commit 88cd2122e4
6 changed files with 215 additions and 6 deletions

27
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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));
} }
} }

View File

@ -1,2 +1,3 @@
pub mod repo_entry; pub mod repo_entry;
pub mod service_switcher;
pub mod tag_list; pub mod tag_list;

View File

@ -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*",

View 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);
}
}
}
}