-- 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