-- Leo's gemini proxy
-- Connecting to typed-hole.org:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
commit 01cde9900638bec5a2b4048e6be2c0d8302616a0
Author: Julien Blanchard <julien@sideburns.eu>
Date: Mon Jul 29 15:49:37 2019 +0200
Highlight links
diff --git a/src/main.rs b/src/main.rs
index 5d6b8b2..db5eadf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,9 @@
use cursive::align::HAlign;
-use cursive::event::EventResult;
use cursive::traits::*;
use cursive::view::Scrollable;
-use cursive::views::{Dialog, EditView, OnEventView, Panel, SelectView, TextView};
+use cursive::views::{Dialog, EditView, Panel, SelectView};
+use cursive::utils::markup::StyledString;
+use cursive::theme::Effect;
use cursive::Cursive;
extern crate native_tls;
@@ -20,25 +21,63 @@ use std::str;
use std::str::FromStr;
use url::Url;
-struct Status {
+#[derive(Debug)]
+struct Header {
code: i8,
- meta: String,
+ meta: String
}
-impl FromStr for Status {
+#[derive(Debug)]
+struct Link {
+ url: String,
+ label: String
+}
+
+impl FromStr for Header {
+ type Err = std::string::ParseError;
+
+ // Parses a string into an instance of 'Header'
+ fn from_str(line: &str) -> Result<Self, Self::Err> {
+ let text = format!("{}", line);
+ let link_regexp = Regex::new(r"^(\d*)\t(.*)$").unwrap();
+
+ let caps = link_regexp.captures(&text).unwrap();
+ let code_str = caps.get(1).map_or("", |m| m.as_str());
+ let meta_str = caps.get(2).map_or("", |m| m.as_str());
+
+ let code = code_str.parse::<i8>().unwrap();
+ let meta = meta_str.to_string();
+
+ Ok(Header {code, meta})
+ }
+}
+
+impl FromStr for Link {
type Err = std::string::ParseError;
- // Parses a string into an instance of 'Status'
- fn from_str(status_line: &str) -> Result<Self, Self::Err> {
- let mut splits = status_line.split("\t");
- let code: i8 = splits.nth(1).unwrap().parse().unwrap();
- let meta: String = String::from(splits.nth(2).unwrap());
+ // Parses a string into an instance of 'Link'
+ fn from_str(line: &str) -> Result<Self, Self::Err> {
+ let text = format!("{}", line);
+ let link_regexp = Regex::new(r"^=>\s(\S*)\s*(.*)?$").unwrap();
+
+ let caps = link_regexp.captures(&text).unwrap();
+ let url_str = caps.get(1).map_or("", |m| m.as_str());
+ let label_str = caps.get(2).map_or("", |m| m.as_str());
+
+ let url = url_str.to_string();
+ let label = if label_str.is_empty() {
+ url_str.to_string()
+ } else {
+ label_str.to_string()
+ };
- Ok(Status {code, meta})
+ Ok(Link {url, label})
}
}
const HELP: &'static str = "
+Welcome to Asuka Gemini browser!
+
Press g to visit an URL
Press b to go back
Press q to exit
@@ -54,16 +93,6 @@ fn main() {
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(
select.with_id("main").scrollable().full_screen()
@@ -74,11 +103,6 @@ fn main() {
.with_id("container")
);
- // Show a welcome popup
- // siv.add_layer(Dialog::info(
- // "Welcome to Asuka, the first ncurses Gemini browser!\n",
- // ));
-
// We can quit by pressing q
siv.add_global_callback('q', |s| s.quit());
// pressing g prompt for an URL
@@ -97,12 +121,23 @@ fn prompt_for_url(s: &mut Cursive) {
.padding((1, 1, 1, 0))
.content(
EditView::new()
- .on_submit(visit_url)
+ .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()
+ } else {
+ format!("gemini://{}", url)
+ };
+
+ visit_url(s, &url_s)
+}
+
fn visit_url(s: &mut Cursive, url_s: &str) {
// Close URL popup if any
if s.find_id::<Dialog>("url_popup").is_some() {
@@ -113,13 +148,7 @@ fn visit_url(s: &mut Cursive, url_s: &str) {
Ok(url) => {
match get_data(url) {
Ok(new_content) => {
- let mut main_view = s.find_id::<SelectView>("main").unwrap();
- let mut container = s.find_id::<Dialog>("container").unwrap();
-
- container.set_title(url_s);
- main_view.clear();
- main_view.add_all_str(new_content.lines());
- main_view.set_on_submit(follow_link);
+ draw_content(s, url_s, new_content);
}
Err(msg) => {
s.add_layer(Dialog::info(msg));
@@ -132,21 +161,8 @@ fn visit_url(s: &mut Cursive, url_s: &str) {
}
}
-fn follow_link(s: &mut Cursive, line: &str) {
- let text = format!("{}", line);
- let link_regexp = Regex::new(r"^=>\s(\S*)(.*)?$").unwrap();
-
- if link_regexp.is_match(&text) {
- let caps = link_regexp.captures(&text).unwrap();
- let url = caps.get(1).map_or("", |m| m.as_str());
- let next_url = parse_link(url);
- visit_url(s, next_url.expect("Not an URL").as_str())
- } else {
- ()
- }
-}
-
fn parse_link(url: &str) -> Result<url::Url, url::ParseError> {
+ // Creates an absolute link if needed
match get_last_host() {
Some(host) => {
let url_s = if url.starts_with("gemini://") {
@@ -171,6 +187,60 @@ fn parse_link(url: &str) -> Result<url::Url, url::ParseError> {
}
}
+fn draw_content(s: &mut Cursive, url: &str, 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);
+ main_view.clear();
+
+ for line in content.lines() {
+ if is_header(line) {
+ // let _header = Header::from_str(line);
+ } else if is_link(line) {
+ let link = Link::from_str(line).unwrap();
+ let mut formatted = StyledString::new();
+ formatted.append(StyledString::styled(
+ link.label,
+ Effect::Underline,
+ ));
+
+ main_view.add_item(formatted, link.url)
+ } else {
+ main_view.add_item(line, "0".to_owned())
+ }
+ }
+
+ main_view.set_on_submit(follow_link);
+}
+
+fn is_header(line: &str) -> bool {
+ let text = format!("{}", line);
+ let header_regexp = Regex::new(r"^(\d*)\t(\S*)$").unwrap();
+
+ header_regexp.is_match(&text)
+}
+
+fn is_link(line: &str) -> bool {
+ let text = format!("{}", line);
+ let link_regexp = Regex::new(r"^=>\s(\S*)(.*)?$").unwrap();
+
+ link_regexp.is_match(&text)
+}
+
+fn is_gemini_link(line: &str) -> bool {
+ line != "0"
+}
+
+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())
+ } else {
+ ()
+ }
+}
+
fn get_data(url: url::Url) -> Result<String, String> {
let host = url.host_str().unwrap();
let path = url.path();
@@ -237,6 +307,7 @@ fn get_previous_url() -> Option<String> {
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
---
Served by Pollux Gemini Server.
-- Response ended
-- Page fetched on Sun May 19 11:41:45 2024