2
0
mirror of https://github.com/xvxx/phetch synced 2024-11-10 13:10:54 +00:00

history browsing

This commit is contained in:
dvkt 2019-12-21 17:41:17 -08:00
parent edbc2bae6a
commit 26120fa03a
8 changed files with 156 additions and 89 deletions

View File

@ -50,28 +50,53 @@ impl Type {
} }
pub fn type_for_char(c: char) -> Option<Type> { pub fn type_for_char(c: char) -> Option<Type> {
match c { Some(match c {
'0' => Some(Type::Text), '0' => Type::Text,
'1' => Some(Type::Menu), '1' => Type::Menu,
'2' => Some(Type::CSOEntity), '2' => Type::CSOEntity,
'3' => Some(Type::Error), '3' => Type::Error,
'4' => Some(Type::Binhex), '4' => Type::Binhex,
'5' => Some(Type::DOSFile), '5' => Type::DOSFile,
'6' => Some(Type::UUEncoded), '6' => Type::UUEncoded,
'7' => Some(Type::Search), '7' => Type::Search,
'8' => Some(Type::Telnet), '8' => Type::Telnet,
'9' => Some(Type::Binary), '9' => Type::Binary,
'+' => Some(Type::Mirror), '+' => Type::Mirror,
'g' => Some(Type::GIF), 'g' => Type::GIF,
'T' => Some(Type::Telnet3270), 'T' => Type::Telnet3270,
'h' => Some(Type::HTML), 'h' => Type::HTML,
'I' => Some(Type::Image), 'I' => Type::Image,
'p' => Some(Type::PNG), 'p' => Type::PNG,
'i' => Some(Type::Info), 'i' => Type::Info,
's' => Some(Type::Sound), 's' => Type::Sound,
'd' => Some(Type::Document), 'd' => Type::Document,
_ => None, _ => return None,
} })
}
pub fn char_for_type(t: Type) -> Option<char> {
Some(match t {
Type::Text => '0',
Type::Menu => '1',
Type::CSOEntity => '2',
Type::Error => '3',
Type::Binhex => '4',
Type::DOSFile => '5',
Type::UUEncoded => '6',
Type::Search => '7',
Type::Telnet => '8',
Type::Binary => '9',
Type::Mirror => '+',
Type::GIF => 'g',
Type::Telnet3270 => 'T',
Type::HTML => 'h',
Type::Image => 'I',
Type::PNG => 'p',
Type::Info => 'i',
Type::Sound => 's',
Type::Document => 'd',
_ => return None,
})
} }
macro_rules! error { macro_rules! error {

View File

@ -1,11 +1,14 @@
pub fn lookup(name: &str) -> Option<&str> { use history;
match name {
"" | "/" | "help" => Some(HELP), pub fn lookup(name: &str) -> Option<String> {
"types" => Some(TYPES), Some(match name {
"nav" => Some(NAV), "" | "/" | "help" => HELP.into(),
"home" => Some(HOME), "types" => TYPES.into(),
_ => None, "nav" => NAV.into(),
} "home" => HOME.into(),
"history" => history::load_as_raw_menu().unwrap_or_else(|| String::new()),
_ => return None,
})
} }
pub const HOME: &str = " pub const HOME: &str = "

76
src/history.rs Normal file
View File

@ -0,0 +1,76 @@
use gopher;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
pub fn load_as_raw_menu() -> Option<String> {
let mut out = vec![];
if let Some(reader) = load() {
let mut lines = reader.lines();
while let Some(Ok(url)) = lines.next() {
let (t, host, port, sel) = gopher::parse_url(&url);
out.insert(
0,
format!(
"{}{}\t{}\t{}\t{}",
gopher::char_for_type(t).unwrap_or('i'),
url,
sel,
host,
port
),
);
}
}
out.insert(0, "i~/.config/phetch/history:\r\ni".into());
Some(out.join("\r\n"))
}
pub fn load() -> Option<BufReader<File>> {
let dotdir = config_dir_path();
if dotdir.is_none() {
return None;
}
let history = dotdir.unwrap().join("history");
if let Ok(file) = std::fs::OpenOptions::new().read(true).open(history) {
return Some(BufReader::new(file));
}
None
}
pub fn save(urls: &[impl std::fmt::Display]) {
let dotdir = config_dir_path();
if dotdir.is_none() {
return;
}
let dotdir = dotdir.unwrap();
let mut out = String::new();
for url in urls {
out.push_str(url.to_string().as_ref());
out.push('\n');
}
let history = dotdir.join("history");
if let Ok(mut file) = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(history)
{
file.write_all(out.as_ref());
}
}
pub fn config_dir_path() -> Option<std::path::PathBuf> {
let homevar = std::env::var("HOME");
if homevar.is_err() {
return None;
}
let dotdir = "~/.config/phetch".replace('~', &homevar.unwrap());
let dotdir = std::path::Path::new(&dotdir);
if dotdir.exists() {
Some(std::path::PathBuf::from(dotdir))
} else {
None
}
}

View File

@ -5,6 +5,7 @@ extern crate termion;
#[macro_use] #[macro_use]
mod gopher; mod gopher;
mod help; mod help;
mod history;
mod menu; mod menu;
mod text; mod text;
mod ui; mod ui;

View File

@ -1,5 +1,6 @@
use gopher; use gopher;
use gopher::Type; use gopher::Type;
use std::fmt;
use std::io::stdout; use std::io::stdout;
use std::io::Write; use std::io::Write;
use ui; use ui;
@ -33,6 +34,12 @@ enum LinkDir {
Visible, Visible,
} }
impl fmt::Display for Menu {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.url())
}
}
impl View for Menu { impl View for Menu {
fn raw(&self) -> String { fn raw(&self) -> String {
self.raw.to_string() self.raw.to_string()

View File

@ -1,3 +1,4 @@
use std::fmt;
use ui::{Action, Key, View, MAX_COLS, SCROLL_LINES}; use ui::{Action, Key, View, MAX_COLS, SCROLL_LINES};
pub struct Text { pub struct Text {
@ -10,6 +11,12 @@ pub struct Text {
pub wide: bool, // in wide mode? turns off margins pub wide: bool, // in wide mode? turns off margins
} }
impl fmt::Display for Text {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.url())
}
}
impl View for Text { impl View for Text {
fn url(&self) -> String { fn url(&self) -> String {
self.url.to_string() self.url.to_string()

View File

@ -17,6 +17,7 @@ use termion::terminal_size;
use gopher; use gopher;
use gopher::Type; use gopher::Type;
use help; use help;
use history;
use menu::Menu; use menu::Menu;
use text::Text; use text::Text;
@ -172,7 +173,7 @@ impl UI {
&url.trim_start_matches("gopher://help/") &url.trim_start_matches("gopher://help/")
.trim_start_matches("1/"), .trim_start_matches("1/"),
) { ) {
Ok(Box::new(Menu::from(url.to_string(), source.to_string()))) Ok(Box::new(Menu::from(url.to_string(), source)))
} else { } else {
Err(error!("Help file not found: {}", url)) Err(error!("Help file not found: {}", url))
} }
@ -236,65 +237,10 @@ impl UI {
self.size.1 as u16 self.size.1 as u16
} }
fn startup(&mut self) { fn startup(&mut self) {}
self.load_history();
}
fn shutdown(&self) { fn shutdown(&self) {
self.save_history(); history::save(&self.views);
}
fn config_dir_path(&self) -> Option<std::path::PathBuf> {
let homevar = std::env::var("HOME");
if homevar.is_err() {
return None;
}
let dotdir = "~/.config/phetch".replace('~', &homevar.unwrap());
let dotdir = std::path::Path::new(&dotdir);
if dotdir.exists() {
Some(std::path::PathBuf::from(dotdir))
} else {
None
}
}
fn load_history(&mut self) {
// let dotdir = self.config_dir_path();
// if dotdir.is_none() {
// return;
// }
// let history = dotdir.unwrap().join("history");
// if let Ok(file) = std::fs::OpenOptions::new().read(true).open(history) {
// let buffered = BufReader::new(file);
// let mut lines = buffered.lines();
// while let Some(Ok(url)) = lines.next() {}
// }
}
fn save_history(&self) {
let dotdir = self.config_dir_path();
if dotdir.is_none() {
return;
}
let dotdir = dotdir.unwrap();
let mut out = String::new();
for page in &self.views {
let url = page.url();
if url.starts_with("gopher://help/") {
continue;
}
out.push_str(&page.url());
out.push('\n');
}
let history = dotdir.join("history");
if let Ok(mut file) = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(history)
{
file.write_all(out.as_ref());
}
} }
fn term_size(&mut self, cols: usize, rows: usize) { fn term_size(&mut self, cols: usize, rows: usize) {
@ -378,6 +324,7 @@ impl UI {
} }
} }
Action::Keypress(Key::Ctrl('h')) => self.open("gopher://help/")?, Action::Keypress(Key::Ctrl('h')) => self.open("gopher://help/")?,
Action::Keypress(Key::Ctrl('e')) => self.open("gopher://help/1/history")?,
Action::Keypress(Key::Ctrl('u')) => { Action::Keypress(Key::Ctrl('u')) => {
if let Some(page) = self.views.get(self.focused) { if let Some(page) = self.views.get(self.focused) {
let url = page.url(); let url = page.url();

View File

@ -1,6 +1,7 @@
use std::fmt;
use ui; use ui;
pub trait View { pub trait View: fmt::Display {
fn respond(&mut self, key: ui::Key) -> ui::Action; fn respond(&mut self, key: ui::Key) -> ui::Action;
fn render(&self) -> String; fn render(&self) -> String;
fn url(&self) -> String; fn url(&self) -> String;