Mike Burns: 1 visit_url operates on current URL 9 files changed, 191 insertions(+), 381 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~julienxx/castor/patches/19451/mbox | git am -3Learn more about email & git
Before this commit, appending to history and visiting URLs were freely mixed. This lead to suble bugs, where pressing "back" would pop off the history then `visit_url` would re-add it, breaking the back button after its second use. Instead, make the `visit_url` function only work on the current URL in history. In order to navigate, first modify history, then call `visit_url`. These five URL inputs all operate the same: parse the input into an absolute `Url` struct, append it to the history, then call `visit_url`. - Input from a dialog. - Activating a button. - URL bar. - Command-line or default URL from settings. - Temporary redirect (30). These two URL "inputs" are unique: - Permanent redirect (31): modify the current `Url` in the history, then call `visit_url`. - Refresh: call `visit_url`. --- To get here, simplify the `protocol` and `absolute_url` ideas. We need a way to take a string and figure out what the absolute URL is. The existing logic is split across multiple files, but it is: - if there's a scheme, just parse it; - if there's a `//` or `://`, assume Gemini; - if there's a current URL, use that as the base. Encode that into `absolute_url::to_absolute_url`. We call that any time we append into the history, which means the history only communicates full, absolute `Url` structs. Once we have that guarantee, we no longer have a use for the `Protocol` trait nor the `AbsoluteUrl` trait. --- src/absolute_url.rs | 146 +++++++------------------ src/dialog.rs | 11 +- src/draw.rs | 27 +++-- src/finger/client.rs | 6 +- src/gemini/client.rs | 6 +- src/gopher/client.rs | 6 +- src/history.rs | 55 +++++----- src/main.rs | 249 ++++++++++++++++++------------------------- src/protocols.rs | 66 ------------ 9 files changed, 191 insertions(+), 381 deletions(-) delete mode 100644 src/protocols.rs diff --git a/src/absolute_url.rs b/src/absolute_url.rs index 33ebc2d..d2b5a04 100644 --- a/src/absolute_url.rs +++ b/src/absolute_url.rs @@ -1,74 +1,29 @@ -use crate::Finger; -use crate::Gemini; -use crate::Gopher; -use crate::Protocol; use url::Url; -pub trait AbsoluteUrl { - fn to_absolute_url(&self) -> Result<url::Url, url::ParseError>; -} - -impl AbsoluteUrl for Finger { - fn to_absolute_url(&self) -> Result<url::Url, url::ParseError> { - Ok(self.get_source_url()) - } -} - -impl AbsoluteUrl for Gemini { - fn to_absolute_url(&self) -> Result<url::Url, url::ParseError> { - let url = self.get_source_str(); - // Creates an absolute link if needed - match crate::history::get_current_host() { - Some(host) => { - if url.starts_with("gemini://") { - Url::parse(&url) - } else if url.starts_with("//") { - Url::parse(&format!("gemini:{}", url)) - } else if url.starts_with('/') { - Url::parse(&format!("gemini://{}{}", 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("gemini://") { - Url::parse(&url) - } else if url.starts_with("//") { - Url::parse(&format!("gemini:{}", url)) - } else { - Url::parse(url) - } +pub fn to_absolute_url(url: &str) -> Result<url::Url, url::ParseError> { + match crate::history::get_current_url() { + Some(current_url) => { + if url.starts_with("gemini://") || url.starts_with("gopher://") || url.starts_with("finger://") { + Url::parse(&url) + } else if url.starts_with("//") { + Url::parse(&format!("gemini:{}", url)) + } else if url.starts_with('/') { + let host = current_url.host(). + ok_or(url::ParseError::EmptyHost)?; + Url::parse(&format!("gemini://{}{}", host, url)) + } else { + current_url.join(url) } } - } -} - -impl AbsoluteUrl for Gopher { - fn to_absolute_url(&self) -> Result<url::Url, url::ParseError> { - let url = self.get_source_str(); - // 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)) - } + None => { + if url.starts_with("gemini://") || url.starts_with("gopher://") || url.starts_with("finger://") { + Url::parse(&url) + } else if url.starts_with("//") { + Url::parse(&format!("gemini:{}", url)) + } else if url.starts_with("/") { + Err(url::ParseError::RelativeUrlWithoutBase) + } else { + Url::parse(&format!("gemini://{}", url)) } } } @@ -79,10 +34,7 @@ fn test_make_absolute_slash_path_no_current_host() { crate::history::clear(); let url = "/foo"; - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url(); + let absolute_url = to_absolute_url(url); assert_eq!(absolute_url, Err(url::ParseError::RelativeUrlWithoutBase)); } #[test] @@ -90,66 +42,48 @@ fn test_make_absolute_just_path_no_current_host() { crate::history::clear(); let url = "foo"; - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url(); - assert_eq!(absolute_url, Err(url::ParseError::RelativeUrlWithoutBase)); + let expected_url = Url::parse("gemini://foo").unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); + assert_eq!(absolute_url, expected_url); } #[test] fn test_make_absolute_full_url() { crate::history::clear(); - crate::history::append("gemini://typed-hole.org"); + crate::history::append(&Url::parse("gemini://typed-hole.org").unwrap()); let url = "gemini://typed-hole.org/foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } #[test] fn test_make_absolute_full_url_no_protocol() { crate::history::clear(); - crate::history::append("gemini://typed-hole.org"); + crate::history::append(&Url::parse("gemini://typed-hole.org").unwrap()); let url = "//typed-hole.org/foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } #[test] fn test_make_absolute_slash_path() { crate::history::clear(); - crate::history::append("gemini://typed-hole.org"); + crate::history::append(&Url::parse("gemini://typed-hole.org").unwrap()); let url = "/foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } #[test] fn test_make_absolute_just_path() { crate::history::clear(); - crate::history::append("gemini://typed-hole.org"); + crate::history::append(&Url::parse("gemini://typed-hole.org").unwrap()); let url = "foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } #[test] @@ -158,11 +92,7 @@ fn test_make_absolute_full_url_no_current_host() { let url = "gemini://typed-hole.org/foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } #[test] @@ -171,10 +101,6 @@ fn test_make_absolute_full_url_no_protocol_no_current_host() { let url = "//typed-hole.org/foo"; let expected_url = Url::parse("gemini://typed-hole.org/foo").unwrap(); - let absolute_url = Gemini { - source: String::from(url), - } - .to_absolute_url() - .unwrap(); + let absolute_url = to_absolute_url(url).unwrap(); assert_eq!(expected_url, absolute_url); } diff --git a/src/dialog.rs b/src/dialog.rs index 485a5ec..f78eebf 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use url::{Position, Url}; use crate::gui::Gui; -use crate::protocols::Gemini; pub fn info(gui: &Arc<Gui>, message: &str) { let dialog = gtk::Dialog::new_with_buttons( @@ -63,7 +62,15 @@ pub fn input(gui: &Arc<Gui>, url: Url, message: &str) { let cleaned: &str = &url[..Position::AfterPath]; let full_url = format!("{}?{}", cleaned.to_string(), response); - crate::visit_url(&gui, Gemini { source: full_url }); + match Url::parse(full_url.as_ref()) { + Ok(url) => { + crate::history::append(&url); + crate::visit_url(&gui); + } + Err(e) => { + crate::dialog::error(&gui, &format!("Invalid URL: {}", e)); + } + }; } dialog.destroy(); diff --git a/src/draw.rs b/src/draw.rs index 451adef..feb0148 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -9,12 +9,10 @@ use std::convert::TryInto; extern crate textwrap; use textwrap::fill; -use crate::absolute_url::AbsoluteUrl; use crate::colors::*; use crate::gemini::link::Link as GeminiLink; use crate::gopher::link::Link as GopherLink; use crate::gui::Gui; -use crate::protocols::{Finger, Gemini, Gopher}; pub fn gemini_content( @@ -311,7 +309,7 @@ pub fn gemini_link(gui: &Arc<Gui>, link_item: String) { insert_external_button(&gui, url, &irc_label); } Ok(GeminiLink::Relative(url, label)) => { - let new_url = Gemini { source: url }.to_absolute_url().unwrap(); + let new_url = crate::absolute_url::to_absolute_url(url.as_ref()).unwrap(); insert_button(&gui, new_url, label); } Ok(GeminiLink::Unknown(_, _)) => (), @@ -362,7 +360,7 @@ pub fn gopher_link(gui: &Arc<Gui>, link_item: String) { insert_button(&gui, url, label); } Ok(GopherLink::Relative(url, label)) => { - let new_url = Gopher { source: url }.to_absolute_url().unwrap(); + let new_url = crate::absolute_url::to_absolute_url(url.as_ref()).unwrap(); insert_button(&gui, new_url, label); } Ok(GopherLink::Ftp(url, label)) => { @@ -404,12 +402,15 @@ pub fn insert_button(gui: &Arc<Gui>, url: Url, label: String) { button.set_tooltip_text(Some(&url.to_string())); button.connect_clicked(clone!(@weak gui => move |_| { - match url.scheme() { - "finger" => crate::visit_url(&gui, Finger { source: url.to_string() }), - "gemini" => crate::visit_url(&gui, Gemini { source: url.to_string() }), - "gopher" => crate::visit_url(&gui, Gopher { source: url.to_string() }), - _ => () - } + match crate::absolute_url::to_absolute_url(url.as_str()) { + Ok(url) => { + crate::history::append(&url); + crate::visit_url(&gui); + } + Err(e) => { + crate::dialog::error(&gui, &format!("Invalid URL: {}", e)); + } + }; })); let mut start_iter = buffer.get_end_iter(); @@ -433,10 +434,8 @@ pub fn insert_gopher_file_button(gui: &Arc<Gui>, url: Url, label: String) { button.set_tooltip_text(Some(&url.to_string())); button.connect_clicked(move |_| { - let (_meta, content) = crate::gopher::client::get_data(Gopher { - source: url.to_string(), - }) - .unwrap(); + let (_meta, content) = crate::gopher::client::get_data(&url) + .unwrap(); crate::client::download(content); }); diff --git a/src/finger/client.rs b/src/finger/client.rs index b3abefd..c645df7 100644 --- a/src/finger/client.rs +++ b/src/finger/client.rs @@ -3,10 +3,7 @@ use std::net::{SocketAddr::V4, SocketAddr::V6, TcpStream, ToSocketAddrs}; use std::thread; use std::time::Duration; -use crate::Protocol; - -pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { - let url = url.get_source_url(); +pub fn get_data(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { let host = url.host_str().unwrap().to_string(); let port = url.port().unwrap_or(79); let urlf = format!("{}:{}", host, port); @@ -22,6 +19,7 @@ pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), Strin }, }; + let url = url.clone(); match TcpStream::connect_timeout(&socket_addr, Duration::new(5, 0)) { Ok(mut stream) => thread::spawn(move || { let username = if url.username() == "" { diff --git a/src/gemini/client.rs b/src/gemini/client.rs index 882d81f..1a2ad2b 100644 --- a/src/gemini/client.rs +++ b/src/gemini/client.rs @@ -4,10 +4,7 @@ use std::net::{SocketAddr::V4, SocketAddr::V6, TcpStream, ToSocketAddrs}; use std::thread; use std::time::Duration; -use crate::protocols::*; - -pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { - let url = url.get_source_url(); +pub fn get_data(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { let host = url.host_str().unwrap_or(""); let port = url.port().unwrap_or(1965); let urlf = format!("{}:{}", host, port); @@ -41,6 +38,7 @@ pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), Strin Ok(stream) => { let mstream = connector.connect(&host, stream); + let url = url.clone(); match mstream { Ok(mut stream) => thread::spawn(move || { let url = format!("{}\r\n", url); diff --git a/src/gopher/client.rs b/src/gopher/client.rs index 3b0496b..d94ba95 100644 --- a/src/gopher/client.rs +++ b/src/gopher/client.rs @@ -4,10 +4,7 @@ use std::net::{SocketAddr::V4, SocketAddr::V6, TcpStream, ToSocketAddrs}; use std::thread; use std::time::Duration; -use crate::Protocol; - -pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { - let url = url.get_source_url(); +pub fn get_data(url: &url::Url) -> Result<(Option<Vec<u8>>, Vec<u8>), String> { let host = url.host_str().unwrap().to_string(); let port = url.port().unwrap_or(70); let urlf = format!("{}:{}", host, port); @@ -23,6 +20,7 @@ pub fn get_data<T: Protocol>(url: T) -> Result<(Option<Vec<u8>>, Vec<u8>), Strin }, }; + let url = url.clone(); match TcpStream::connect_timeout(&socket_addr, Duration::new(5, 0)) { Ok(mut stream) => thread::spawn(move || { let path = url.path().to_string(); diff --git a/src/history.rs b/src/history.rs index 304aa2a..b20d6c1 100644 --- a/src/history.rs +++ b/src/history.rs @@ -21,70 +21,65 @@ impl History { } } - fn get_previous_url(&mut self) -> Option<Url> { + fn get_previous_url(&mut self) { let p = self.past.pop(); if p.is_some() { if let Some(c) = self.current.take() { self.future.push(c); } - self.current = None; + self.current = p; }; - p } - fn get_next_url(&mut self) -> Option<Url> { + fn get_next_url(&mut self) { let f = self.future.pop(); if f.is_some() { if let Some(c) = self.current.take() { self.past.push(c); } - self.current = None; + self.current = f; }; - f } - fn append(&mut self, url: &str) { - let url = Url::parse(url).unwrap(); - if let Some(c) = self.current.replace(url) { + fn append(&mut self, url: &Url) { + if let Some(c) = self.current.replace(url.clone()) { self.past.push(c); self.future = vec![] }; } + fn replace_current(&mut self, url: &Url) { + self.current.replace(url.clone()); + } + fn current(&self) -> Option<&Url> { self.current.as_ref() } } -pub fn append(url: &str) { +pub fn append(url: &Url) { HISTORY.lock().unwrap().append(url) } -pub fn get_current_host() -> Option<String> { - HISTORY - .lock() - .unwrap() - .current() - .and_then(|u| u.host_str()) - .map(String::from) +pub fn replace_current(url: &Url) { + HISTORY.lock().unwrap().replace_current(url) } -pub fn get_current_url() -> Option<String> { +pub fn get_current_url() -> Option<Url> { HISTORY .lock() .unwrap() .current() - .and_then(|u| u.join("./").ok()) - .map(|p| p.to_string()) + .map(|u| u.clone()) } -pub fn get_previous_url() -> Option<Url> { +pub fn get_previous_url() { let mut history = HISTORY.lock().unwrap(); history.get_previous_url() } -pub fn get_next_url() -> Option<Url> { +pub fn get_next_url() { let mut history = HISTORY.lock().unwrap(); history.get_next_url() @@ -105,7 +100,7 @@ fn test_append_simple() { future: vec![], }; - append("gemini://typed-hole.org"); + append(&Url::parse("gemini://typed-hole.org").unwrap()); assert_eq!( *HISTORY.lock().unwrap(), @@ -127,7 +122,7 @@ fn test_append_clear_future() { future: vec![Url::parse("gemini://typed-hole.org/foo").unwrap()], }; - append("gemini://typed-hole.org/bar"); + append(&Url::parse("gemini://typed-hole.org/bar").unwrap()); assert_eq!( *HISTORY.lock().unwrap(), @@ -149,7 +144,7 @@ fn test_append_no_current() { future: vec![Url::parse("gemini://typed-hole.org/foo").unwrap()], }; - append("gemini://typed-hole.org"); + append(&Url::parse("gemini://typed-hole.org").unwrap()); assert_eq!( *HISTORY.lock().unwrap(), @@ -171,17 +166,17 @@ fn test_get_previous_url_simple() { future: vec![], }; - let previous = get_previous_url(); + get_previous_url(); assert_eq!( - previous, + get_current_url(), Some(Url::parse("gemini://typed-hole.org").unwrap()) ); assert_eq!( *HISTORY.lock().unwrap(), History { past: vec![], - current: None, + current: Some(Url::parse("gemini://typed-hole.org").unwrap()), future: vec![Url::parse("gemini://typed-hole.org/foo").unwrap()] }, ); @@ -198,8 +193,8 @@ fn test_get_previous_url_no_past() { }; *HISTORY.lock().unwrap() = simple.clone(); - let previous = get_previous_url(); + get_previous_url(); - assert_eq!(previous, None); + assert_eq!(get_current_url(), Some(Url::parse("gemini://typed-hole.org/foo").unwrap())); assert_eq!(*HISTORY.lock().unwrap(), simple); } diff --git a/src/main.rs b/src/main.rs index bcbc7c3..0fb4404 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,14 +8,12 @@ extern crate lazy_static; use std::env; use std::str::FromStr; use std::sync::Arc; -use url::Url; use gtk::prelude::*; mod gui; use gui::Gui; mod absolute_url; -use absolute_url::AbsoluteUrl; mod bookmarks; mod client; mod colors; @@ -25,8 +23,6 @@ mod finger; mod gemini; mod gopher; mod history; -mod protocols; -use protocols::{Finger, Gemini, Gopher, Protocol, Scheme}; mod settings; mod status; use status::Status; @@ -102,8 +98,8 @@ fn main() { let gui_clone = gui.clone(); let url_bar = gui.url_bar(); url_bar.connect_activate(move |b| { - let url = b.get_text().expect("get_text failed").to_string(); - route_url(&gui_clone, url) + let url = b.get_text().expect("get_text failed"); + route_url(&gui_clone, url.as_str()) }); } @@ -125,75 +121,41 @@ fn main() { // no argument passed, check settings 1 => { if let Some(url) = settings::start_url() { - route_url(&gui, url) + route_url(&gui, url.as_ref()) } } // Use argument as initial URL - _ => route_url(&gui, args[1].to_string()), + _ => route_url(&gui, &args[1]), } gui.start(); gtk::main(); } -fn route_url(gui: &Arc<Gui>, url: String) { - if url == "" { - } else if url.starts_with("gemini://") { - visit_url(&gui, Gemini { source: url }) - } else if url.starts_with("gopher://") { - visit_url(&gui, Gopher { source: url }) - } else if url.starts_with("finger://") { - visit_url(&gui, Finger { source: url }) - } else { - visit_url( - &gui, - Gemini { - source: format!("gemini://{}", url), - }, - ) +fn route_url(gui: &Arc<Gui>, url: &str) { + match absolute_url::to_absolute_url(url) { + Ok(url) => { + history::append(&url); + visit_url(&gui); + } + Err(e) => { + dialog::error(&gui, &format!("Invalid URL: {}", e)); + } }; } fn go_back(gui: &Arc<Gui>) { - if let Some(prev) = history::get_previous_url() { - visit(gui, &prev); - } + history::get_previous_url(); + visit_url(gui); } fn go_forward(gui: &Arc<Gui>) { - if let Some(next) = history::get_next_url() { - visit(gui, &next); - } -} - -fn visit(gui: &Arc<Gui>, url: &Url) { - match url.scheme() { - "finger" => visit_url( - gui, - Finger { - source: url.to_string(), - }, - ), - "gemini" => visit_url( - gui, - Gemini { - source: url.to_string(), - }, - ), - "gopher" => visit_url( - gui, - Gopher { - source: url.to_string(), - }, - ), - _ => (), - } + history::get_next_url(); + visit_url(gui); } fn refresh(gui: &Arc<Gui>) { - let url_bar = gui.url_bar(); - let url = url_bar.get_text().expect("get_text failed").to_string(); - route_url(&gui, url) + visit_url(&gui) } fn update_url_field(gui: &Arc<Gui>, url: &str) { @@ -229,129 +191,122 @@ fn show_bookmarks(gui: &Arc<Gui>) { content_view.show_all(); } -pub fn visit_url<T: AbsoluteUrl + Protocol>(gui: &Arc<Gui>, url: T) { - if url.get_source_str() == "gemini://::bookmarks" { +pub fn visit_url(gui: &Arc<Gui>) { + let potential_url = history::get_current_url(); + + let url = match potential_url { + Some(u) => { u } + None => { return } + }; + + if url.as_str() == "gemini://::bookmarks" { show_bookmarks(&gui); return; } let content_view = gui.content_view(); - match url.get_scheme() { - Scheme::Gemini => { - let absolute_url = url.to_absolute_url(); - - match absolute_url { - Ok(absolute_url) => match gemini::client::get_data(Gemini { - source: absolute_url.to_string(), - }) { - 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(absolute_url.as_str()); - update_url_field(&gui, absolute_url.as_str()); - let content_str = - String::from_utf8_lossy(&new_content).to_string(); - - clear_buffer(&content_view); - if meta.starts_with("text/gemini") { - let parsed_content = gemini::parser::parse(content_str); - draw::gemini_content(&gui, parsed_content); - } else { - // just a text file - draw::gemini_text_content(&gui, content_str.lines()); - } - - content_view.show_all(); + match url.scheme() { + "gemini" => { + match gemini::client::get_data(&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. + update_url_field(&gui, url.as_str()); + let content_str = + String::from_utf8_lossy(&new_content).to_string(); + + clear_buffer(&content_view); + if meta.starts_with("text/gemini") { + let parsed_content = gemini::parser::parse(content_str); + draw::gemini_content(&gui, parsed_content); } else { - // download and try to open the rest. - client::download(new_content); + // just a text file + draw::gemini_text_content(&gui, content_str.lines()); } + + content_view.show_all(); + } else { + // download and try to open the rest. + client::download(new_content); } - Status::Gone(_meta) => { - dialog::error(&gui, "\nSorry page is gone.\n"); - } - Status::RedirectTemporary(new_url) - | Status::RedirectPermanent(new_url) => { - history::append(absolute_url.as_str()); - visit_url(&gui, Gemini { source: new_url }); - } - Status::TransientCertificateRequired(_meta) + } + Status::Gone(_meta) => { + dialog::error(&gui, "\nSorry page is gone.\n"); + } + Status::RedirectTemporary(new_url) => { + route_url(&gui, new_url.as_ref()); + } + Status::RedirectPermanent(new_url) => { + match absolute_url::to_absolute_url(new_url.as_ref()) { + Ok(url) => { + history::replace_current(&url); + visit_url(&gui); + } + Err(e) => { + dialog::error(&gui, &format!("Invalid URL: {}", e)); + } + }; + } + Status::TransientCertificateRequired(_meta) | Status::AuthorisedCertificatedRequired(_meta) => { dialog::error( &gui, "\nYou need a valid certificate to access this page.\n", - ); - } - Status::Input(message) => { - dialog::input(&gui, absolute_url, &message); + ); } - _ => (), + Status::Input(message) => { + dialog::input(&gui, url, &message); } + _ => (), } } - Err(e) => { - dialog::error(&gui, &format!("\n{}\n", e)); - } - }, + } Err(e) => { dialog::error(&gui, &format!("\n{}\n", e)); } } } - Scheme::Gopher => { - let absolute_url = url.to_absolute_url(); - match absolute_url { - Ok(abs_url) => match gopher::client::get_data(url) { - Ok((_meta, new_content)) => { - history::append(abs_url.as_str()); - update_url_field(&gui, abs_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) => { - dialog::error(&gui, &format!("\n{}\n", e)); - } - }, + "gopher" => { + match gopher::client::get_data(&url) { + Ok((_meta, new_content)) => { + 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) => { dialog::error(&gui, &format!("\n{}\n", e)); } } } - Scheme::Finger => { - let absolute_url = url.to_absolute_url(); - match absolute_url { - Ok(abs_url) => match finger::client::get_data(url) { - Ok((_meta, new_content)) => { - history::append(abs_url.as_str()); - update_url_field(&gui, abs_url.as_str()); - let content_str = String::from_utf8_lossy(&new_content).to_string(); - - let parsed_content = finger::parser::parse(content_str); - clear_buffer(&content_view); - draw::finger_content(&gui, parsed_content); - - content_view.show_all(); - } - Err(e) => { - dialog::error(&gui, &format!("\n{}\n", e)); - } - }, + "finger" => { + match finger::client::get_data(&url) { + Ok((_meta, new_content)) => { + update_url_field(&gui, url.as_str()); + let content_str = String::from_utf8_lossy(&new_content).to_string(); + + let parsed_content = finger::parser::parse(content_str); + clear_buffer(&content_view); + draw::finger_content(&gui, parsed_content); + + content_view.show_all(); + } Err(e) => { dialog::error(&gui, &format!("\n{}\n", e)); } } } + _ => {} } } diff --git a/src/protocols.rs b/src/protocols.rs deleted file mode 100644 index 370fc2e..0000000 --- a/src/protocols.rs @@ -1,66 +0,0 @@ -use url::Url; - -pub trait Protocol { - fn get_source_str(&self) -> &str; - fn get_source_url(&self) -> Url; - fn get_scheme(&self) -> Scheme; -} - -pub struct Gemini { - pub source: String, -} -pub struct Gopher { - pub source: String, -} -pub struct Finger { - pub source: String, -} - -impl Protocol for Finger { - fn get_source_str(&self) -> &str { - &self.source - } - - fn get_source_url(&self) -> Url { - Url::parse(&self.source).unwrap() - } - - fn get_scheme(&self) -> Scheme { - Scheme::Finger - } -} - -impl Protocol for Gemini { - fn get_source_str(&self) -> &str { - &self.source - } - - fn get_source_url(&self) -> Url { - Url::parse(&self.source).unwrap() - } - - fn get_scheme(&self) -> Scheme { - Scheme::Gemini - } -} - -impl Protocol for Gopher { - fn get_source_str(&self) -> &str { - &self.source - } - - fn get_source_url(&self) -> Url { - Url::parse(&self.source).unwrap() - } - - fn get_scheme(&self) -> Scheme { - Scheme::Gopher - } -} - -#[derive(PartialEq)] -pub enum Scheme { - Finger, - Gemini, - Gopher, -} -- 2.20.1