-- Leo's gemini proxy

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

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

commit 888e634449a8b8fd682e7a989ca4b63aaf1b7d3a

Author: Julien Blanchard <julien@sideburns.eu>

Date: Fri Dec 27 17:51:38 2019 +0100


Gopher initial support


diff --git a/src/absolute.rs b/src/gemini/absolute.rs

similarity index 89%

rename from src/absolute.rs

rename to src/gemini/absolute.rs

index 7e5bfcf..5f3cdac 100644

--- a/src/absolute.rs

+++ b/src/gemini/absolute.rs

@@ -2,7 +2,7 @@ use url::Url;


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

// Creates an absolute link if needed

- match super::history::get_current_host() {

+ match crate::history::get_current_host() {

Some(host) => {

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

Url::parse(url)

@@ -11,7 +11,7 @@ pub fn make(url: &str) -> Result<url::Url, url::ParseError> {

} else if url.starts_with('/') {

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

} else {

- let current_host_path = super::history::get_current_url().unwrap();

+ let current_host_path = crate::history::get_current_url().unwrap();

Url::parse(&format!("{}{}", current_host_path, url))

}

}

@@ -29,7 +29,7 @@ pub fn make(url: &str) -> Result<url::Url, url::ParseError> {


fn test_make_absolute_full_url() {

- super::history::append("gemini://typed-hole.org");

+ crate::history::append("gemini://typed-hole.org");

let url = "gemini://typed-hole.org/foo";

let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap();

let absolute_url = make(&url).unwrap();

@@ -37,7 +37,7 @@ fn test_make_absolute_full_url() {

}

fn test_make_absolute_full_url_no_protocol() {

- super::history::append("gemini://typed-hole.org");

+ crate::history::append("gemini://typed-hole.org");

let url = "//typed-hole.org/foo";

let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap();

let absolute_url = make(&url).unwrap();

@@ -45,7 +45,7 @@ fn test_make_absolute_full_url_no_protocol() {

}

fn test_make_absolute_slash_path() {

- super::history::append("gemini://typed-hole.org");

+ crate::history::append("gemini://typed-hole.org");

let url = "/foo";

let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap();

let absolute_url = make(&url).unwrap();

@@ -53,7 +53,7 @@ fn test_make_absolute_slash_path() {

}

fn test_make_absolute_just_path() {

- super::history::append("gemini://typed-hole.org");

+ crate::history::append("gemini://typed-hole.org");

let url = "foo";

let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap();

let absolute_url = make(&url).unwrap();

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

similarity index 94%

rename from src/content.rs

rename to src/gemini/client.rs

index 184d9e4..5b44690 100644

--- a/src/content.rs

+++ b/src/gemini/client.rs

@@ -5,7 +5,8 @@ use native_tls::TlsConnector;

use std::net::{TcpStream, ToSocketAddrs};

use std::time::Duration;


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

+

+pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

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

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


@@ -33,7 +34,7 @@ pub fn get_data(url: &url::Url) -> Result<(Vec<u8>, Vec<u8>), String> {

let clrf_idx = find_clrf(&res);

let content = res.split_off(clrf_idx.unwrap() + 2);


- Ok((res, content))

+ Ok((Some(res), content))

}

Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)),

}

diff --git a/src/link.rs b/src/gemini/link.rs

similarity index 100%

rename from src/link.rs

rename to src/gemini/link.rs

diff --git a/src/gemini/mod.rs b/src/gemini/mod.rs

new file mode 100644

index 0000000..9516af0

--- /dev/null

+++ b/src/gemini/mod.rs

@@ -0,0 +1,4 @@

+pub mod absolute;

+pub mod client;

+pub mod link;

+pub mod parser;

diff --git a/src/parser.rs b/src/gemini/parser.rs

similarity index 100%

rename from src/parser.rs

rename to src/gemini/parser.rs

diff --git a/src/gopher/absolute.rs b/src/gopher/absolute.rs

new file mode 100644

index 0000000..14003fc

--- /dev/null

+++ b/src/gopher/absolute.rs

@@ -0,0 +1,89 @@

+use url::Url;

+

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

+ // Creates an absolute link if needed

+ match crate::history::get_current_host() {

+ Some(host) => {

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

+ Url::parse(url)

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

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

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

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

+ } else {

+ let current_host_path = crate::history::get_current_url().unwrap();

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

+ }

+ }

+ None => {

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

+ Url::parse(url)

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

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

+ } else {

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

+ }

+ }

+ }

+}

+

+#[test]

+fn test_make_absolute_full_url() {

+ crate::history::append("gopher://typed-hole.org");

+ let url = "gopher://typed-hole.org/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_full_url_no_protocol() {

+ crate::history::append("gopher://typed-hole.org");

+ let url = "//typed-hole.org/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_slash_path() {

+ crate::history::append("gopher://typed-hole.org");

+ let url = "/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_just_path() {

+ crate::history::append("gopher://typed-hole.org");

+ let url = "foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_full_url_no_current_host() {

+ let url = "gopher://typed-hole.org/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_full_url_no_protocol_no_current_host() {

+ let url = "//typed-hole.org/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_slash_path_no_current_host() {

+ let url = "/foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

+#[test]

+fn test_make_absolute_just_path_no_current_host() {

+ let url = "foo";

+ let expected_url = Url::parse("gopher://typed-hole.org/foo").unwrap();

+ let absolute_url = make(&url).unwrap();

+ assert_eq!(expected_url, absolute_url);

+}

diff --git a/src/gopher/client.rs b/src/gopher/client.rs

new file mode 100644

index 0000000..724a892

--- /dev/null

+++ b/src/gopher/client.rs

@@ -0,0 +1,44 @@

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

+use tempfile::NamedTempFile;

+use std::net::{TcpStream};

+

+

+pub fn get(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> {

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

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

+ println!("{:?}", url.path());

+

+ match TcpStream::connect(&urlf) {

+ Ok(mut stream) => {

+ let mut url = format!("{}\r\n", url.path());

+ let url = if url.starts_with("/0/") || url.starts_with("/1/") {

+ url.split_off(2)

+ } else {

+ url

+ };

+ stream.write_all(url.as_bytes()).unwrap();

+ let mut res = vec![];

+ stream.read_to_end(&mut res).unwrap();

+

+ Ok((None, res))

+ }

+ Err(e) => Err(format!("Could not connect to {}\n{}", urlf, e)),

+ }

+}

+

+pub fn download(content: Vec<u8>) {

+ let path = write_tmp_file(content);

+ open::that(path).unwrap();

+}

+

+fn write_tmp_file(content: Vec<u8>) -> std::path::PathBuf {

+ let mut tmp_file = NamedTempFile::new().unwrap();

+ tmp_file.write_all(&content).unwrap();

+ let (_file, path) = tmp_file.keep().unwrap();

+ path

+}

+

+fn find_clrf(data: &[u8]) -> Option<usize> {

+ let clrf = b"\r\n";

+ data.windows(clrf.len()).position(|window| window == clrf)

+}

diff --git a/src/gopher/link.rs b/src/gopher/link.rs

new file mode 100644

index 0000000..f9fa012

--- /dev/null

+++ b/src/gopher/link.rs

@@ -0,0 +1,130 @@

+extern crate regex;

+use regex::Regex;

+use std::str::FromStr;

+use url::Url;

+

+#[derive(Debug)]

+pub enum Link {

+ Gemini(Url, String),

+ Gopher(Url, String),

+ Http(Url, String),

+ Relative(String, String),

+ Unknown(Url, String),

+}

+

+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

+pub struct ParseError;

+

+impl FromStr for Link {

+ type Err = ParseError;

+

+ // Parses a &str into an instance of 'Link'

+ fn from_str(line: &str) -> Result<Self, Self::Err> {

+ let mut els = line.split("\t");

+

+ if line.starts_with('0') || line.starts_with('1') {

+ let label = els.next().expect("no label");

+ println!("{:?}", label);

+ let path = els.next();

+ println!("{:?}", path);

+ let host = els.next();

+ println!("{:?}", host);

+ let port = els.next();

+ println!("{:?}", port);

+

+ if let Some(host) = host {

+ if let Some(path) = path {

+ let mut text = String::from(label);

+ text.remove(0);

+ Ok(Link::Gopher(Url::parse(&format!("gopher://{}{}", host, path)).unwrap(), String::from(text)))

+ } else {

+ Err(ParseError)

+ }

+ } else {

+ Err(ParseError)

+ }

+ } else if line.starts_with('h') {

+ let label = els.next();

+ println!("{:?}", label);

+ let url = els.next();

+ println!("{:?}", url);

+

+ if let Some(label) = label {

+ if let Some(url) = url {

+ let mut text = String::from(label);

+ text.remove(0);

+ let mut url = String::from(url);

+ let url = url.split_off(4);

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

+ match url.scheme() {

+ "gemini" => Ok(Link::Gemini(url, String::from(text))),

+ "http" => Ok(Link::Http(url, String::from(text))),

+ "https" => Ok(Link::Http(url, String::from(text))),

+ _ => Ok(Link::Unknown(url, String::from(text))),

+ }

+ } else {

+ Err(ParseError)

+ }

+ } else {

+ Err(ParseError)

+ }

+ } else if line.starts_with('[') {

+ let label = line;

+ println!("{:?}", label);

+ let mut url = String::from(line);

+ let url = url.split_off(4);

+ println!("{:?}", url);

+ let url = Url::parse(&url);

+

+ if let Ok(url) = url {

+ println!("SCHEME {}", url.scheme());

+ match url.scheme() {

+ "gemini" => Ok(Link::Gemini(url, String::from(line))),

+ "gopher" => Ok(Link::Gopher(url, String::from(line))),

+ "http" => Ok(Link::Http(url, String::from(line))),

+ "https" => Ok(Link::Http(url, String::from(line))),

+ _ => Ok(Link::Unknown(url, String::from(line))),

+ }

+ } else {

+ Err(ParseError)

+ }

+ } else {

+ Err(ParseError)

+ }

+

+ // match link_regexp.captures(&line) {

+ // Some(caps) => {

+ // 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()

+ // };

+

+ // match make_link(url, label) {

+ // Some(link) => Ok(link),

+ // None => Err(ParseError),

+ // }

+ // }

+ // None => Err(ParseError),

+ // }

+ }

+}

+

+fn make_link(url: String, label: String) -> Option<Link> {

+ let urlp = Url::parse(&url);

+ match urlp {

+ Ok(url) => match url.scheme() {

+ "gemini" => Some(Link::Gemini(url, label)),

+ "gopher" => Some(Link::Gopher(url, label)),

+ "http" => Some(Link::Http(url, label)),

+ "https" => Some(Link::Http(url, label)),

+ _ => Some(Link::Unknown(url, label)),

+ },

+ Err(url::ParseError::RelativeUrlWithoutBase) => Some(Link::Relative(url, label)),

+ _ => None,

+ }

+}

diff --git a/src/gopher/mod.rs b/src/gopher/mod.rs

new file mode 100644

index 0000000..9516af0

--- /dev/null

+++ b/src/gopher/mod.rs

@@ -0,0 +1,4 @@

+pub mod absolute;

+pub mod client;

+pub mod link;

+pub mod parser;

diff --git a/src/gopher/parser.rs b/src/gopher/parser.rs

new file mode 100644

index 0000000..99e8d98

--- /dev/null

+++ b/src/gopher/parser.rs

@@ -0,0 +1,51 @@

+ use std::str::FromStr;

+

+

+#[derive(Debug)]

+pub enum TextElement {

+ ExternalLinkItem(String),

+ LinkItem(String),

+ Image(String),

+ Text(String),

+}

+

+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

+pub struct ParseError;

+

+impl FromStr for TextElement {

+ type Err = ParseError;

+

+ // Parses a &str into an instance of 'TextElement'

+ fn from_str(line: &str) -> Result<TextElement, ParseError> {

+ if line.starts_with('0') {

+ Ok(TextElement::LinkItem(String::from(line)))

+ } else if line.starts_with('1') {

+ Ok(TextElement::LinkItem(String::from(line)))

+ } else if line.starts_with('i') {

+ let mut els = line.split("\t");

+ let text = els.next().expect("");

+ let mut text = String::from(text);

+ text.remove(0);

+ Ok(TextElement::Text(String::from(text)))

+ } else if line.starts_with('h') {

+ Ok(TextElement::ExternalLinkItem(String::from(line)))

+ } else if line.starts_with('I') {

+ let mut els = line.split("\t");

+ let text = els.next().expect("");

+ let mut text = String::from(text);

+ text.remove(0);

+ Ok(TextElement::Image(String::from(text)))

+ } else {

+ Ok(TextElement::Text(String::from(line)))

+ }

+ }

+}

+

+pub fn parse(content: String) -> Vec<Result<TextElement, ParseError>> {

+ let mut parsed = Vec::new();

+

+ for line in content.lines() {

+ parsed.push(TextElement::from_str(line));

+ }

+ parsed

+}

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

index 0e32179..196f821 100644

--- a/src/main.rs

+++ b/src/main.rs

@@ -15,14 +15,12 @@ use url::{Position, Url};


mod gui;

use gui::Gui;

-mod absolute;

mod bookmarks;

-mod content;

+mod gemini;

+mod gopher;

mod history;

-mod link;

-use link::Link;

-mod parser;

-use parser::{ParseError, TextElement, TextElement::*};

+use gemini::link::Link as GeminiLink;

+use gopher::link::Link as GopherLink;

mod status;

use status::Status;

mod tags;

@@ -68,13 +66,13 @@ fn main() {

let url_bar = gui.url_bar();

url_bar.connect_activate(move |b| {

let url = b.get_text().expect("get_text failed").to_string();

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

- url

- } else {

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

- };

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

+ // url

+ // } else {

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

+ // };


- visit_url(&gui_clone, full_url);

+ visit_url(&gui_clone, url);

});

}


@@ -109,10 +107,10 @@ fn show_bookmarks(gui: &Arc<Gui>) {

let content_view = gui.content_view();


let bookmarks_list = format!("# Bookmarks\n\n{}", bookmarks::content());

- let parsed_content = parser::parse(bookmarks_list);

+ let parsed_content = gemini::parser::parse(bookmarks_list);


clear_buffer(&content_view);

- draw_content(&gui, parsed_content);

+ draw_gemini_content(&gui, parsed_content);


update_url_field(&gui, "::bookmarks");


@@ -121,79 +119,106 @@ fn show_bookmarks(gui: &Arc<Gui>) {


fn visit_url(gui: &Arc<Gui>, url: String) {

{

- let content_view = gui.content_view();

-

if url == "gemini://::bookmarks" {

show_bookmarks(&gui);

- return

+ return;

}


- match absolute::make(url.as_str()) {

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

- Ok((meta, new_content)) => {

- let meta_str = String::from_utf8_lossy(&meta).to_string();

- if let Ok(status) = Status::from_str(&meta_str) {

- match status {

- Status::Success(meta) => {

- if meta.starts_with("text/") {

- // display text files.

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

- update_url_field(&gui, url.as_str());

- let content_str =

- String::from_utf8_lossy(&new_content).to_string();

-

- let parsed_content = parser::parse(content_str);

- clear_buffer(&content_view);

- draw_content(&gui, parsed_content);

-

- content_view.show_all();

- } else {

- // download and try to open the rest.

- content::download(new_content);

+ let content_view = gui.content_view();

+

+ if url.starts_with("gemini") {

+ let absolute_url = gemini::absolute::make(url.as_str());

+

+ match absolute_url {

+ Ok(url) => match gemini::client::get(&url) {

+ Ok((meta, new_content)) => {

+ let meta_str = String::from_utf8_lossy(&meta.unwrap()).to_string();

+ if let Ok(status) = Status::from_str(&meta_str) {

+ match status {

+ Status::Success(meta) => {

+ if meta.starts_with("text/") {

+ // display text files.

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

+ update_url_field(&gui, url.as_str());

+ let content_str =

+ String::from_utf8_lossy(&new_content).to_string();

+

+ let parsed_content = gemini::parser::parse(content_str);

+ clear_buffer(&content_view);

+ draw_gemini_content(&gui, parsed_content);

+

+ content_view.show_all();

+ } else {

+ // download and try to open the rest.

+ gemini::client::download(new_content);

+ }

}

+ Status::Gone(_meta) => {

+ error_dialog(&gui, "\nSorry page is gone.\n");

+ }

+ Status::RedirectTemporary(new_url)

+ | Status::RedirectPermanent(new_url) => {

+ visit_url(&gui, new_url);

+ }

+ Status::TransientCertificateRequired(_meta)

+ | Status::AuthorisedCertificatedRequired(_meta) => {

+ error_dialog(

+ &gui,

+ "\nYou need a valid certificate to access this page.\n",

+ );

+ }

+ Status::Input(message) => {

+ input_dialog(&gui, url, &message);

+ }

+ _ => (),

}

- Status::Gone(_meta) => {

- error_dialog(

- &gui,

- "\nSorry page is gone.\n",

- );

- }

- Status::RedirectTemporary(new_url)

- | Status::RedirectPermanent(new_url) => {

- visit_url(&gui, new_url);

- }

- Status::TransientCertificateRequired(_meta)

- | Status::AuthorisedCertificatedRequired(_meta) => {

- error_dialog(

- &gui,

- "\nYou need a valid certificate to access this page.\n",

- );

- }

- Status::Input(message) => {

- input_dialog(&gui, url, &message);

- }

- _ => (),

}

}

+ Err(e) => {

+ error_dialog(&gui, &format!("\n{}\n", e));

+ }

+ },

+ Err(e) => {

+ error_dialog(&gui, &format!("\n{}\n", e));

}

- Err(_) => {

- error_dialog(&gui, "\nInvalid URL.\n");

+ }

+ } else {

+ let absolute_url = gopher::absolute::make(url.as_str());

+ match absolute_url {

+ Ok(url) => match gopher::client::get(&url) {

+ Ok((_meta, new_content)) => {

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

+ update_url_field(&gui, url.as_str());

+ let content_str = String::from_utf8_lossy(&new_content).to_string();

+

+ let parsed_content = gopher::parser::parse(content_str);

+ clear_buffer(&content_view);

+ draw_gopher_content(&gui, parsed_content);

+

+ content_view.show_all();

+ }

+ Err(e) => {

+ error_dialog(&gui, &format!("\n{}\n", e));

+ }

+ },

+ Err(e) => {

+ error_dialog(&gui, &format!("\n{}\n", e));

}

- },

- Err(_) => {

- error_dialog(&gui, "\nInvalid URL.\n");

}

- }

+ };

}

}


-fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -> TextBuffer {

+fn draw_gemini_content(

+ gui: &Arc<Gui>,

+ content: Vec<Result<gemini::parser::TextElement, gemini::parser::ParseError>>,

+) -> TextBuffer {

let content_view = gui.content_view();

let buffer = content_view.get_buffer().unwrap();


for el in content {

match el {

- Ok(H1(header)) => {

+ Ok(gemini::parser::TextElement::H1(header)) => {

let mut end_iter = buffer.get_end_iter();

buffer.insert_markup(

&mut end_iter,

@@ -203,7 +228,7 @@ fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -

),

);

}

- Ok(H2(header)) => {

+ Ok(gemini::parser::TextElement::H2(header)) => {

let mut end_iter = buffer.get_end_iter();

buffer.insert_markup(

&mut end_iter,

@@ -213,7 +238,7 @@ fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -

),

);

}

- Ok(H3(header)) => {

+ Ok(gemini::parser::TextElement::H3(header)) => {

let mut end_iter = buffer.get_end_iter();

buffer.insert_markup(

&mut end_iter,

@@ -223,19 +248,19 @@ fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -

),

);

}

- Ok(ListItem(item)) => {

+ Ok(gemini::parser::TextElement::ListItem(item)) => {

let mut end_iter = buffer.get_end_iter();

buffer.insert_markup(

&mut end_iter,

&format!("<span foreground=\"green\">■ {}</span>\n", item),

);

}

- Ok(Text(text)) => {

+ Ok(gemini::parser::TextElement::Text(text)) => {

let mut end_iter = buffer.get_end_iter();

buffer.insert(&mut end_iter, &format!("{}\n", text));

}

- Ok(LinkItem(link_item)) => {

- draw_link(&gui, link_item);

+ Ok(gemini::parser::TextElement::LinkItem(link_item)) => {

+ draw_gemini_link(&gui, link_item);

}

Err(_) => println!("Something failed."),

}

@@ -243,9 +268,75 @@ fn draw_content(gui: &Arc<Gui>, content: Vec<Result<TextElement, ParseError>>) -

buffer

}


-fn draw_link(gui: &Arc<Gui>, link_item: String) {

- match Link::from_str(&link_item) {

- Ok(Link::Http(url, label)) => {

+fn draw_gopher_content(

+ gui: &Arc<Gui>,

+ content: Vec<Result<gopher::parser::TextElement, gopher::parser::ParseError>>,

+) -> TextBuffer {

+ let content_view = gui.content_view();

+ let buffer = content_view.get_buffer().unwrap();

+

+ for el in content {

+ match el {

+ Ok(gopher::parser::TextElement::Text(text)) => {

+ if text.contains("://") {

+ draw_gopher_link(&gui, text);

+ } else {

+ let mut end_iter = buffer.get_end_iter();

+ buffer.insert(&mut end_iter, &format!("{}\n", text));

+ }

+ }

+ Ok(gopher::parser::TextElement::LinkItem(link_item)) => {

+ draw_gopher_link(&gui, link_item);

+ }

+ Ok(gopher::parser::TextElement::ExternalLinkItem(link_item)) => {

+ draw_gopher_link(&gui, link_item);

+ }

+ Ok(gopher::parser::TextElement::Image(_link_item)) => {

+ // draw_gopher_link(&gui, link_item);

+ ();

+ }

+ Err(_) => println!("Something failed."),

+ }

+ }

+ buffer

+}

+

+fn draw_gemini_link(gui: &Arc<Gui>, link_item: String) {

+ match GeminiLink::from_str(&link_item) {

+ Ok(GeminiLink::Http(url, label)) => {

+ let button_label = if label.is_empty() {

+ url.clone().to_string()

+ } else {

+ label

+ };

+ let www_label = format!("{} [WWW]", button_label);

+

+ insert_external_button(&gui, url, &www_label);

+ }

+ Ok(GeminiLink::Gopher(url, label)) => {

+ let button_label = if label.is_empty() {

+ url.clone().to_string()

+ } else {

+ label

+ };

+ let gopher_label = format!("{} [Gopher]", button_label);

+ insert_gemini_button(&gui, url, gopher_label);

+ }

+ Ok(GeminiLink::Gemini(url, label)) => {

+ insert_gemini_button(&gui, url, label);

+ }

+ Ok(GeminiLink::Relative(url, label)) => {

+ let new_url = gemini::absolute::make(&url).unwrap();

+ insert_gemini_button(&gui, new_url, label);

+ }

+ Ok(GeminiLink::Unknown(_, _)) => (),

+ Err(_) => (),

+ }

+}

+

+fn draw_gopher_link(gui: &Arc<Gui>, link_item: String) {

+ match GopherLink::from_str(&link_item) {

+ Ok(GopherLink::Http(url, label)) => {

let button_label = if label.is_empty() {

url.clone().to_string()

} else {

@@ -255,23 +346,23 @@ fn draw_link(gui: &Arc<Gui>, link_item: String) {


insert_external_button(&gui, url, &www_label);

}

- Ok(Link::Gopher(url, label)) => {

+ Ok(GopherLink::Gopher(url, label)) => {

let button_label = if label.is_empty() {

url.clone().to_string()

} else {

label

};

let gopher_label = format!("{} [Gopher]", button_label);

- insert_external_button(&gui, url, &gopher_label);

+ insert_gemini_button(&gui, url, gopher_label);

}

- Ok(Link::Gemini(url, label)) => {

+ Ok(GopherLink::Gemini(url, label)) => {

insert_gemini_button(&gui, url, label);

}

- Ok(Link::Relative(url, label)) => {

- let new_url = absolute::make(&url).unwrap();

+ Ok(GopherLink::Relative(url, label)) => {

+ let new_url = gemini::absolute::make(&url).unwrap();

insert_gemini_button(&gui, new_url, label);

}

- Ok(Link::Unknown(_, _)) => (),

+ Ok(GopherLink::Unknown(_, _)) => (),

Err(_) => (),

}

}

@@ -357,7 +448,10 @@ fn input_dialog(gui: &Arc<Gui>, url: Url, message: &str) {

Some(message),

Some(gui.window()),

gtk::DialogFlags::MODAL,

- &[("Close", ResponseType::Close),("Send", ResponseType::Accept)],

+ &[

+ ("Close", ResponseType::Close),

+ ("Send", ResponseType::Accept),

+ ],

);


let content_area = dialog.get_content_area();



---

Served by Pollux Gemini Server.

-- Response ended

-- Page fetched on Sun May 19 07:01:57 2024