history browsing

pull/6/head
dvkt 5 years ago
parent edbc2bae6a
commit 26120fa03a

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

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

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

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

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

@ -1,3 +1,4 @@
use std::fmt;
use ui::{Action, Key, View, MAX_COLS, SCROLL_LINES};
pub struct Text {
@ -10,6 +11,12 @@ pub struct Text {
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 {
fn url(&self) -> String {
self.url.to_string()

@ -17,6 +17,7 @@ use termion::terminal_size;
use gopher;
use gopher::Type;
use help;
use history;
use menu::Menu;
use text::Text;
@ -172,7 +173,7 @@ impl UI {
&url.trim_start_matches("gopher://help/")
.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 {
Err(error!("Help file not found: {}", url))
}
@ -236,65 +237,10 @@ impl UI {
self.size.1 as u16
}
fn startup(&mut self) {
self.load_history();
}
fn startup(&mut self) {}
fn shutdown(&self) {
self.save_history();
}
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());
}
history::save(&self.views);
}
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('e')) => self.open("gopher://help/1/history")?,
Action::Keypress(Key::Ctrl('u')) => {
if let Some(page) = self.views.get(self.focused) {
let url = page.url();

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

Loading…
Cancel
Save