mirror of https://github.com/xvxx/phetch
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.5 KiB
Rust
201 lines
6.5 KiB
Rust
5 years ago
|
use gopher;
|
||
|
use gopher::Type;
|
||
5 years ago
|
use std::io;
|
||
5 years ago
|
use ui::{Action, Key, View};
|
||
|
|
||
|
#[derive(Debug)]
|
||
5 years ago
|
pub struct MenuView {
|
||
5 years ago
|
pub input: String, // user's inputted value
|
||
5 years ago
|
pub menu: Menu, // data
|
||
5 years ago
|
pub line: usize, // selected line
|
||
|
pub scroll: usize, // scrolling offset
|
||
|
}
|
||
|
|
||
|
#[derive(Debug)]
|
||
5 years ago
|
pub struct Menu {
|
||
5 years ago
|
lines: Vec<Line>, // lines
|
||
|
raw: String, // raw gopher response
|
||
|
url: String, // gopher url
|
||
|
}
|
||
5 years ago
|
|
||
|
#[derive(Debug)]
|
||
5 years ago
|
pub struct Line {
|
||
|
pos: usize, // which line in the page
|
||
5 years ago
|
name: String,
|
||
5 years ago
|
host: String,
|
||
5 years ago
|
port: String,
|
||
5 years ago
|
selector: String,
|
||
|
typ: Type,
|
||
|
}
|
||
|
|
||
5 years ago
|
impl View for MenuView {
|
||
5 years ago
|
fn process_input(&mut self, key: Key) -> Action {
|
||
|
match key {
|
||
|
Key::Char('\n') => return Action::Open,
|
||
|
Key::Backspace => {
|
||
|
if self.input.is_empty() {
|
||
|
Action::Back
|
||
|
} else {
|
||
|
self.input.pop();
|
||
|
Action::None
|
||
|
}
|
||
|
}
|
||
|
Key::Delete => {
|
||
|
self.input.pop();
|
||
|
Action::None
|
||
|
}
|
||
|
Key::Ctrl('c') => {
|
||
|
if self.input.len() > 0 {
|
||
|
self.input.clear();
|
||
|
Action::None
|
||
|
} else {
|
||
|
Action::Quit
|
||
|
}
|
||
|
}
|
||
|
Key::Char('-') => {
|
||
|
if self.input.is_empty() {
|
||
|
Action::PageUp
|
||
|
} else {
|
||
|
self.input.push('-');
|
||
|
Action::None
|
||
|
}
|
||
|
}
|
||
|
Key::Char(' ') => {
|
||
|
if self.input.is_empty() {
|
||
|
return Action::PageDown;
|
||
|
} else {
|
||
|
self.input.push(' ');
|
||
|
Action::None
|
||
|
}
|
||
|
}
|
||
|
Key::Char(c) => {
|
||
|
self.input.push(c);
|
||
5 years ago
|
for (i, link) in self.menu.lines.iter().enumerate() {
|
||
5 years ago
|
// jump to number
|
||
5 years ago
|
let count = self.menu.lines.len();
|
||
5 years ago
|
if count < 10 && c == '1' && i == 0 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 20 && c == '2' && i == 1 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 30 && c == '3' && i == 2 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 40 && c == '4' && i == 3 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 50 && c == '5' && i == 4 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 60 && c == '6' && i == 5 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 70 && c == '7' && i == 6 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 80 && c == '8' && i == 7 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if count < 90 && c == '9' && i == 8 {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if self.input.len() > 1 && self.input == (i + 1).to_string() {
|
||
|
return Action::FollowLink(i);
|
||
|
} else if self.input.len() == 1 && self.input == (i + 1).to_string() {
|
||
|
return Action::FollowLink(i);
|
||
|
} else {
|
||
|
if link
|
||
|
.name
|
||
|
.to_ascii_lowercase()
|
||
|
.contains(&self.input.to_ascii_lowercase())
|
||
|
{
|
||
|
return Action::FollowLink(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Action::None
|
||
|
}
|
||
|
_ => Action::None,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
impl MenuView {
|
||
|
pub fn from(url: String, response: String) -> MenuView {
|
||
|
MenuView {
|
||
|
menu: Menu::from(url, response),
|
||
5 years ago
|
input: String::new(),
|
||
|
line: 0,
|
||
|
scroll: 0,
|
||
|
}
|
||
|
}
|
||
5 years ago
|
}
|
||
5 years ago
|
|
||
5 years ago
|
impl Menu {
|
||
|
pub fn from(url: String, gopher_response: String) -> Menu {
|
||
|
Self::parse(url, gopher_response)
|
||
5 years ago
|
}
|
||
|
|
||
5 years ago
|
// Loads a Menu given a URL.
|
||
|
pub fn load(host: &str, port: &str, selector: &str) -> io::Result<Menu> {
|
||
5 years ago
|
let url = format!("{}:{}{}", host, port, selector);
|
||
5 years ago
|
match gopher::fetch(host, port, selector) {
|
||
|
Ok(res) => Ok(Menu::from(url, res)),
|
||
5 years ago
|
Err(e) => Err(e),
|
||
5 years ago
|
}
|
||
|
}
|
||
5 years ago
|
|
||
5 years ago
|
// Parses the lines in a raw Gopher menu response.
|
||
5 years ago
|
fn parse(url: String, raw: String) -> Menu {
|
||
5 years ago
|
let mut lines = vec![];
|
||
|
let mut line = (0, 0, Type::Menu); // (name start pos, name end, type)
|
||
|
let mut start = true; // are we at beginning of a line?
|
||
|
let mut count = 0; // which line # are we
|
||
|
let mut skip_line = false;
|
||
5 years ago
|
|
||
|
for (i, c) in raw.char_indices() {
|
||
|
if start {
|
||
5 years ago
|
line.0 = i + 1;
|
||
5 years ago
|
match c {
|
||
|
'0' => {
|
||
5 years ago
|
line.2 = Type::Text;
|
||
5 years ago
|
}
|
||
|
'1' => {
|
||
5 years ago
|
line.2 = Type::Menu;
|
||
5 years ago
|
}
|
||
|
'h' => {
|
||
5 years ago
|
line.2 = Type::HTML;
|
||
|
}
|
||
|
'i' => {
|
||
|
line.2 = Type::Info;
|
||
5 years ago
|
}
|
||
|
'\n' => continue,
|
||
5 years ago
|
_ => {
|
||
|
eprintln!("unknown line type: {}", c);
|
||
|
skip_line = true;
|
||
|
}
|
||
5 years ago
|
}
|
||
|
start = false;
|
||
|
} else if c == '\n' {
|
||
|
start = true;
|
||
5 years ago
|
if skip_line {
|
||
|
skip_line = false;
|
||
|
continue;
|
||
|
}
|
||
|
if i > line.0 {
|
||
|
line.1 = i;
|
||
|
let mut parts = [""; 4];
|
||
|
for (j, s) in raw[line.0..line.1].split('\t').enumerate() {
|
||
|
if j < parts.len() {
|
||
|
parts[j] = s;
|
||
5 years ago
|
}
|
||
|
}
|
||
5 years ago
|
lines.push(Line {
|
||
|
name: parts[0].to_string(),
|
||
|
selector: parts[1].to_string(),
|
||
|
host: parts[2].to_string(),
|
||
|
port: parts[3].trim_end_matches('\r').to_string(),
|
||
|
typ: line.2,
|
||
5 years ago
|
pos: count,
|
||
|
});
|
||
|
count += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
Menu { raw, url, lines }
|
||
5 years ago
|
}
|
||
5 years ago
|
}
|