mirror of
https://github.com/xvxx/phetch
synced 2024-11-10 13:10:54 +00:00
history browsing
This commit is contained in:
parent
edbc2bae6a
commit
26120fa03a
@ -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 {
|
||||||
|
19
src/help.rs
19
src/help.rs
@ -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
76
src/history.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
63
src/ui.rs
63
src/ui.rs
@ -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();
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user