-- Leo's gemini proxy

-- Connecting to typed-hole.org:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

commit 0083b81a6ef53acd1db106b2621108d802119216

Author: Julien Blanchard <julien@sideburns.eu>

Date: Fri Aug 2 12:54:22 2019 +0200


Rework history


Use a Vec instead of a file, use a history popup.


diff --git a/Cargo.lock b/Cargo.lock

index 7adc517..c6d8d84 100644

--- a/Cargo.lock

+++ b/Cargo.lock

@@ -23,6 +23,7 @@ name = "asuka"

version = "0.1.0"

dependencies = [

"cursive 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",

+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",

"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",

"regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",

"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",

diff --git a/Cargo.toml b/Cargo.toml

index eaa6704..f99e4da 100644

--- a/Cargo.toml

+++ b/Cargo.toml

@@ -8,4 +8,5 @@ edition = "2018"

cursive = "0.12"

native-tls = "0.2"

url = "*"

-regex = "*"

\ No newline at end of file

+regex = "*"

+lazy_static = "*"

\ No newline at end of file

diff --git a/src/content.rs b/src/content.rs

index fc183c3..59c1c42 100644

--- a/src/content.rs

+++ b/src/content.rs

@@ -3,7 +3,7 @@ use std::io::{Read, Write};

use native_tls::TlsConnector;

use std::net::TcpStream;


-pub fn get_data(url: url::Url) -> Result<String, String> {

+pub fn get_data(url: &url::Url) -> Result<String, String> {

let host = url.host_str().unwrap();

let path = url.path();

let urlf = format!("{}:1965", host);

diff --git a/src/history.rs b/src/history.rs

index f256be1..4f81daf 100644

--- a/src/history.rs

+++ b/src/history.rs

@@ -1,80 +1,34 @@

-use std::fs::File;

-use std::fs::OpenOptions;

-use std::io::{Read, Write};

-

use url::Url;

+use std::sync::Mutex;


-pub fn create_history_file() {

- File::create("/tmp/asuka_history").expect("unable to create file");

+lazy_static! {

+ static ref HISTORY: Mutex<Vec<Url>> = Mutex::new(vec![]);

}


-pub fn append(url: &str) {

- let mut file = OpenOptions::new()

- .append(true)

- .open("/tmp/asuka_history")

- .expect("file not found");

-

- let line = format!("{}\n", url);

- file.write_all(line.as_bytes())

- .expect("Unable to write file");

+pub fn init() {

+ let mut history = HISTORY.lock().unwrap();

+ let start_page = Url::parse("gemini://start_page").unwrap();

+ history.push(start_page);

}


-pub fn get_last_host() -> Option<String> {

- match get_history_content() {

- Some(content) => {

- let last_url = content.lines().last();

-

- match last_url {

- Some(url) => {

- let urlp = Url::parse(url).expect("not an URL");

- let url = urlp.host_str().unwrap();

- Some(String::from(url))

- }

- None => None,

- }

- }

- None => None,

- }

+pub fn content() -> Vec<Url> {

+ HISTORY.lock().unwrap().to_vec()

}


-pub fn get_current_url() -> Option<String> {

- match get_history_content() {

- Some(content) => {

- let last_url = content.lines().last();

-

- match last_url {

- Some(url) => Some(String::from(url)),

- None => None,

- }

- }

- None => None,

- }

+pub fn append(url: &str) {

+ let url = Url::parse(url).unwrap();

+ HISTORY.lock().unwrap().push(url)

}


-pub fn get_previous_url() -> Option<String> {

- match get_history_content() {

- Some(content) => {

- let lines_count = content.lines().count();

-

- if lines_count > 1 {

- // Return before last line

- Some(content.lines().nth(lines_count - 2).unwrap().to_owned())

- } else {

- None

+pub fn get_current_host() -> Option<String> {

+ let history = HISTORY.lock().unwrap();

+ match history.last() {

+ Some(current_url) => {

+ match current_url.host_str() {

+ Some(host) => Some(host.to_owned()),

+ None => None

}

}

None => None

}

}

-

-fn get_history_content() -> Option<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");

- if content.len() > 0 {

- Some(content)

- } else {

- None

- }

-}

diff --git a/src/main.rs b/src/main.rs

index 67a9efc..22171e5 100644

--- a/src/main.rs

+++ b/src/main.rs

@@ -1,3 +1,6 @@

+#[macro_use]

+extern crate lazy_static;

+

extern crate native_tls;

extern crate regex;


@@ -25,19 +28,21 @@ const HELP: &'static str = "

Welcome to Asuka Gemini browser!


Press g to visit an URL

-Press b to go back

+Press h to show/hide history

Press q to exit

";


fn main() {

- history::create_history_file();

+ history::init();


let mut siv = Cursive::default();


let mut select = SelectView::new();


select.add_all_str(HELP.lines());

- select.set_on_submit(follow_link);

+ select.set_on_submit(|s, link| {

+ follow_link(s, link);

+ });


siv.add_fullscreen_layer(

Dialog::around(Panel::new(

@@ -53,8 +58,8 @@ fn main() {

siv.add_global_callback('q', |s| s.quit());

// pressing g prompt for an URL

siv.add_global_callback('g', |s| prompt_for_url(s));

- // pressing b goes back in history

- siv.add_global_callback('b', |s| go_back(s));

+ // pressing h shows/hides history

+ siv.add_global_callback('h', |s| show_history(s));


siv.run();

}

@@ -65,75 +70,76 @@ fn prompt_for_url(s: &mut Cursive) {

.title("Enter URL")

// Padding is (left, right, top, bottom)

.padding((1, 1, 1, 0))

- .content(EditView::new().on_submit(goto_url).fixed_width(20))

+ .content(EditView::new()

+ .on_submit(goto_url)

+ .fixed_width(20))

.with_id("url_popup"),

);

}


fn goto_url(s: &mut Cursive, url: &str) {

// Prepend gemini scheme if needed

- let url_s = if url.starts_with("gemini://") {

- url.to_owned()

+ if url.starts_with("gemini://") {

+ visit_url(s, &Url::parse(url).unwrap())

} else {

- format!("gemini://{}", url)

+ let url = format!("gemini://{}", url);

+ visit_url(s, &Url::parse(&url).unwrap())

+ };

+}

+

+fn show_history(s: &mut Cursive) {

+ // Hide popup when pressing h on an opened popup

+ if s.find_id::<Dialog>("history_popup").is_some() {

+ s.pop_layer();

+ return

+ }

+

+ let mut select = SelectView::new();

+

+ for url in history::content() {

+ let url_s = url.as_str();

+ select.add_item_str(url_s);

};


- visit_url(s, &url_s)

+ select.set_on_submit(|s, link| {

+ s.pop_layer();

+ follow_link(s, link);

+ });

+

+ s.add_layer(

+ Dialog::around(select.scrollable().fixed_size((50, 10)))

+ .title("History")

+ .with_id("history_popup"),

+ );

}


-fn visit_url(s: &mut Cursive, url_s: &str) {

+fn visit_url(s: &mut Cursive, url: &Url) {

// Close URL popup if any

if s.find_id::<Dialog>("url_popup").is_some() {

s.pop_layer();

}


- match parse_link(url_s) {

- Ok(url) => match content::get_data(url) {

+ match make_absolute(url.as_str()) {

+ Ok(url) => match content::get_data(&url) {

Ok(new_content) => {

- history::append(url_s);

- draw_content(s, url_s, new_content);

+ history::append(url.as_str());

+ draw_content(s, url, new_content);

}

Err(msg) => {

s.add_layer(Dialog::info(msg));

}

},

Err(_) => {

- s.add_layer(Dialog::info(format!("Could not parse {}", url_s)));

+ s.add_layer(Dialog::info(format!("Could not parse {}", url.as_str())));

}

}

}


-fn parse_link(url: &str) -> Result<url::Url, url::ParseError> {

- // Creates an absolute link if needed

- match history::get_last_host() {

- Some(host) => {

- let url_s = if url.starts_with("gemini://") {

- url.to_owned()

- } else if url.starts_with("/") {

- format!("gemini://{}{}", host, url)

- } else {

- format!("{}/{}", history::get_current_url().unwrap(), url)

- };

-

- Url::parse(&url_s)

- }

- None => {

- let url_s = if url.starts_with("gemini://") {

- url.to_owned()

- } else {

- format!("gemini://{}", url)

- };

-

- Url::parse(&url_s)

- }

- }

-}

-

-fn draw_content(s: &mut Cursive, url: &str, content: String) {

+fn draw_content(s: &mut Cursive, url: Url, content: String) {

let mut main_view = s.find_id::<SelectView>("main").unwrap();

let mut container = s.find_id::<Dialog>("container").unwrap();


- container.set_title(url);

+ container.set_title(url.as_str());

main_view.clear();


match Header::from_str(content.lines().next().unwrap()) {

@@ -152,8 +158,6 @@ fn draw_content(s: &mut Cursive, url: &str, content: String) {

Err(_) => main_view.add_item(str::replace(line, "\t", " "), "0".to_owned()),

}

}

-

- main_view.set_on_submit(follow_link);

}


fn is_gemini_link(line: &str) -> bool {

@@ -161,17 +165,32 @@ fn is_gemini_link(line: &str) -> bool {

}


fn follow_link(s: &mut Cursive, line: &str) {

- if is_gemini_link(&line) {

- let next_url = parse_link(&line);

- visit_url(s, next_url.expect("Not an URL").as_str())

+ if is_gemini_link(line) {

+ let next_url = make_absolute(line).expect("Not an URL");

+ visit_url(s, &next_url)

} else {

()

}

}


-fn go_back(s: &mut Cursive) {

- match history::get_previous_url() {

- Some(url) => visit_url(s, &url),

- None => (),

+fn make_absolute(url: &str) -> Result<url::Url, url::ParseError> {

+ // Creates an absolute link if needed

+ match history::get_current_host() {

+ Some(host) => {

+ if url.starts_with("gemini://") {

+ Url::parse(url)

+ } else if url.starts_with("/") {

+ Url::parse(&format!("gemini://{}{}", host, url))

+ } else {

+ Url::parse(&format!("gemini://{}/{}", host, url))

+ }

+ }

+ None => {

+ if url.starts_with("gemini://") {

+ Url::parse(url)

+ } else {

+ Url::parse(&format!("gemini://{}", url))

+ }

+ }

}

}



---

Served by Pollux Gemini Server.

-- Response ended

-- Page fetched on Sun May 19 09:12:33 2024