|
|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
use cursive::traits::*; |
|
|
|
|
use cursive::align::HAlign; |
|
|
|
|
use cursive::event::EventResult; |
|
|
|
|
use cursive::traits::*; |
|
|
|
|
use cursive::view::Scrollable; |
|
|
|
|
use cursive::views::{Dialog, Panel, TextView, EditView, SelectView, OnEventView}; |
|
|
|
|
use cursive::event::{EventResult}; |
|
|
|
|
use cursive::views::{Dialog, EditView, OnEventView, Panel, SelectView, TextView}; |
|
|
|
|
use cursive::Cursive; |
|
|
|
|
|
|
|
|
|
extern crate native_tls; |
|
|
|
|
@ -11,8 +11,8 @@ extern crate regex;
|
|
|
|
|
use native_tls::TlsConnector; |
|
|
|
|
use regex::Regex; |
|
|
|
|
|
|
|
|
|
use std::net::TcpStream; |
|
|
|
|
use std::io::{Read, Write}; |
|
|
|
|
use std::net::TcpStream; |
|
|
|
|
|
|
|
|
|
use std::fs::File; |
|
|
|
|
use std::fs::OpenOptions; |
|
|
|
|
@ -22,7 +22,7 @@ use url::Url;
|
|
|
|
|
|
|
|
|
|
struct Status { |
|
|
|
|
code: i8, |
|
|
|
|
meta: String |
|
|
|
|
meta: String, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl FromStr for Status { |
|
|
|
|
@ -49,12 +49,29 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
let mut siv = Cursive::default(); |
|
|
|
|
|
|
|
|
|
let mut select = SelectView::new(); |
|
|
|
|
|
|
|
|
|
select.add_all_str(HELP.lines()); |
|
|
|
|
select.set_on_submit(follow_link); |
|
|
|
|
|
|
|
|
|
// let select_view = OnEventView::new(select)
|
|
|
|
|
// .on_pre_event_inner('k', |s, _| {
|
|
|
|
|
// s.select_up(1);
|
|
|
|
|
// Some(EventResult::Consumed(None))
|
|
|
|
|
// })
|
|
|
|
|
// .on_pre_event_inner('j', |s, _| {
|
|
|
|
|
// s.select_down(1);
|
|
|
|
|
// Some(EventResult::Consumed(None))
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
siv.add_fullscreen_layer( |
|
|
|
|
Dialog::around(Panel::new(TextView::new(HELP).with_id("content").scrollable().full_screen())) |
|
|
|
|
.title("Asuka Browser") |
|
|
|
|
// This is the alignment for the button
|
|
|
|
|
.h_align(HAlign::Center) |
|
|
|
|
.button("Quit", |s| s.quit()), |
|
|
|
|
Dialog::around(Panel::new( |
|
|
|
|
select.with_id("main").scrollable().full_screen() |
|
|
|
|
)) |
|
|
|
|
.title("Asuka Browser") |
|
|
|
|
.h_align(HAlign::Center) |
|
|
|
|
.button("Quit", |s| s.quit()) |
|
|
|
|
.with_id("container") |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Show a welcome popup
|
|
|
|
|
@ -81,38 +98,27 @@ fn prompt_for_url(s: &mut Cursive) {
|
|
|
|
|
.content( |
|
|
|
|
EditView::new() |
|
|
|
|
.on_submit(visit_url) |
|
|
|
|
.with_id("url_field") |
|
|
|
|
.fixed_width(20) |
|
|
|
|
)); |
|
|
|
|
).with_id("url_popup") |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn visit_url(s: &mut Cursive, url: &str) { |
|
|
|
|
// s.pop_layer();
|
|
|
|
|
// Close URL popup if any
|
|
|
|
|
if s.find_id::<Dialog>("url_popup").is_some() { |
|
|
|
|
s.pop_layer(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match get_data(&url) { |
|
|
|
|
Ok(new_content) => { |
|
|
|
|
let mut select = SelectView::new() |
|
|
|
|
.h_align(HAlign::Left); |
|
|
|
|
|
|
|
|
|
s.add_layer(Dialog::info(url)); |
|
|
|
|
|
|
|
|
|
select.add_all_str(new_content.lines()); |
|
|
|
|
select.set_on_submit(follow_link); |
|
|
|
|
|
|
|
|
|
let select = OnEventView::new(select) |
|
|
|
|
.on_pre_event_inner('k', |s, _| { |
|
|
|
|
s.select_up(1); |
|
|
|
|
Some(EventResult::Consumed(None)) |
|
|
|
|
}) |
|
|
|
|
.on_pre_event_inner('j', |s, _| { |
|
|
|
|
s.select_down(1); |
|
|
|
|
Some(EventResult::Consumed(None)) |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
s.add_layer( |
|
|
|
|
Dialog::around(select.scrollable().full_screen()).title(url), |
|
|
|
|
); |
|
|
|
|
}, |
|
|
|
|
let mut main_view = s.find_id::<SelectView>("main").unwrap(); |
|
|
|
|
let mut container = s.find_id::<Dialog>("container").unwrap(); |
|
|
|
|
|
|
|
|
|
container.set_title(url); |
|
|
|
|
main_view.clear(); |
|
|
|
|
main_view.add_all_str(new_content.lines()); |
|
|
|
|
main_view.set_on_submit(follow_link); |
|
|
|
|
} |
|
|
|
|
Err(msg) => { |
|
|
|
|
s.add_layer(Dialog::info(msg)); |
|
|
|
|
} |
|
|
|
|
@ -122,11 +128,10 @@ fn visit_url(s: &mut Cursive, url: &str) {
|
|
|
|
|
fn follow_link(s: &mut Cursive, line: &str) { |
|
|
|
|
let text = format!("{}", line); |
|
|
|
|
let re1 = Regex::new(r"^\[(.*)\|(URL:)?(.*)\]$").unwrap(); // `[Foo|URL:link]` links
|
|
|
|
|
let re2 = Regex::new(r"^=>\s(\S*)\s(\w*)$").unwrap(); // `=> link Foo` links
|
|
|
|
|
let re2 = Regex::new(r"^=>\s(\S*)\s(\w*)$").unwrap(); // `=> link Foo` links
|
|
|
|
|
|
|
|
|
|
if re1.is_match(&text) { |
|
|
|
|
let caps = re1.captures(&text).unwrap(); |
|
|
|
|
s.pop_layer(); |
|
|
|
|
let url = caps.get(3).map_or("", |m| m.as_str()); |
|
|
|
|
let next_url = if url.starts_with("gemini://") { |
|
|
|
|
url.to_owned() |
|
|
|
|
@ -138,7 +143,6 @@ fn follow_link(s: &mut Cursive, line: &str) {
|
|
|
|
|
visit_url(s, &next_url) |
|
|
|
|
} else if re2.is_match(&text) { |
|
|
|
|
let caps = re2.captures(&text).unwrap(); |
|
|
|
|
s.pop_layer(); |
|
|
|
|
let url = caps.get(1).map_or("", |m| m.as_str()); |
|
|
|
|
let next_url = if url.starts_with("gemini://") { |
|
|
|
|
url.to_owned() |
|
|
|
|
@ -156,7 +160,7 @@ fn follow_link(s: &mut Cursive, line: &str) {
|
|
|
|
|
fn is_link(line: &str) -> bool { |
|
|
|
|
let text = format!("{}", line); |
|
|
|
|
let re1 = Regex::new(r"^\[(.*)\|(URL:)?(.*)\]$").unwrap(); // `[Foo|URL:link]` links
|
|
|
|
|
let re2 = Regex::new(r"^=>\s(\S*)\s(\w*)$").unwrap(); // `=> link Foo` links
|
|
|
|
|
let re2 = Regex::new(r"^=>\s(\S*)\s(\w*)$").unwrap(); // `=> link Foo` links
|
|
|
|
|
|
|
|
|
|
re1.is_match(&text) || re2.is_match(&text) |
|
|
|
|
} |
|
|
|
|
@ -187,34 +191,39 @@ fn get_data(url: &str) -> Result<String, String> {
|
|
|
|
|
let mut res = vec![]; |
|
|
|
|
stream.read_to_end(&mut res).unwrap(); |
|
|
|
|
Ok(String::from_utf8_lossy(&res).to_string()) |
|
|
|
|
}, |
|
|
|
|
Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)) |
|
|
|
|
} |
|
|
|
|
Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)), |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)) |
|
|
|
|
} |
|
|
|
|
Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn go_back(s: &mut Cursive) { |
|
|
|
|
match get_previous_url() { |
|
|
|
|
Some(url) => visit_url(s, &url), |
|
|
|
|
None => () |
|
|
|
|
None => (), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_last_host() -> String { |
|
|
|
|
let mut file = File::open("/tmp/asuka_history").expect("file not found"); |
|
|
|
|
let mut content = String::new(); |
|
|
|
|
file.read_to_string(&mut content).expect("Unable to read file"); |
|
|
|
|
file.read_to_string(&mut content) |
|
|
|
|
.expect("Unable to read file"); |
|
|
|
|
let url = content.lines().last().unwrap(); |
|
|
|
|
let urlp = Url::parse(url).unwrap(); |
|
|
|
|
urlp.host_str().unwrap().to_owned() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_previous_url() -> Option<String> { |
|
|
|
|
let mut file = OpenOptions::new().read(true).open("/tmp/asuka_history").unwrap(); |
|
|
|
|
let mut file = OpenOptions::new() |
|
|
|
|
.read(true) |
|
|
|
|
.open("/tmp/asuka_history") |
|
|
|
|
.unwrap(); |
|
|
|
|
let mut content = String::new(); |
|
|
|
|
file.read_to_string(&mut content).expect("Unable to read file"); |
|
|
|
|
file.read_to_string(&mut content) |
|
|
|
|
.expect("Unable to read file"); |
|
|
|
|
let lines_count = content.lines().count(); |
|
|
|
|
|
|
|
|
|
if lines_count > 1 { |
|
|
|
|
@ -234,5 +243,6 @@ fn append_history(url: &str) {
|
|
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
let line = format!("{}\n", url); |
|
|
|
|
file.write_all(line.as_bytes()).expect("Unable to write file"); |
|
|
|
|
file.write_all(line.as_bytes()) |
|
|
|
|
.expect("Unable to write file"); |
|
|
|
|
} |
|
|
|
|
|