From 7518101b552fc58a42ce9d7baa68aa5e2ee6229f Mon Sep 17 00:00:00 2001 From: yggverse Date: Mon, 3 Feb 2025 02:44:33 +0200 Subject: [PATCH] implement `to_uri` method --- src/client/connection/response/redirect.rs | 62 ++++++++++++++++++- .../connection/response/redirect/error.rs | 4 ++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/client/connection/response/redirect.rs b/src/client/connection/response/redirect.rs index 7c694fd..b0856be 100644 --- a/src/client/connection/response/redirect.rs +++ b/src/client/connection/response/redirect.rs @@ -1,7 +1,7 @@ pub mod error; pub use error::Error; -use glib::GStringPtr; +use glib::{GStringPtr, Uri, UriFlags}; const TEMPORARY: (u8, &str) = (30, "Temporary redirect"); const PERMANENT: (u8, &str) = (31, "Permanent redirect"); @@ -35,6 +35,32 @@ impl Redirect { .0 } + /// Resolve [specification-compatible](https://geminiprotocol.net/docs/protocol-specification.gmi#redirection), + /// absolute [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `target` using `base` + /// * fragment implementation uncompleted @TODO + pub fn to_uri(&self, base: &Uri) -> Result { + match Uri::build( + UriFlags::NONE, + base.scheme().as_str(), + None, // unexpected + base.host().as_deref(), + base.port(), + base.path().as_str(), + // > If a server sends a redirection in response to a request with a query string, + // > the client MUST NOT apply the query string to the new location + None, + // > A server SHOULD NOT include fragments in redirections, + // > but if one is given, and a client already has a fragment it could apply (from the original URI), + // > it is up to the client which fragment to apply. + None, // @TODO + ) + .parse_relative(self.target(), UriFlags::NONE) + { + Ok(absolute) => Ok(absolute), + Err(e) => Err(Error::Glib(e)), + } + } + // Getters pub fn target(&self) -> &str { @@ -114,3 +140,37 @@ fn test_from_str() { assert_eq!(permanent.to_code(), PERMANENT.0); assert_eq!(permanent.to_string(), PERMANENT.1); } + +#[test] +fn test_to_uri() { + use std::str::FromStr; + + let request = Uri::build( + UriFlags::NONE, + "gemini", + None, + Some("geminiprotocol.net"), + -1, + "/path/", + Some("?query"), + Some("?fragment"), + ); + + let resolve = Redirect::from_str("30 /uri\r\n").unwrap(); + assert_eq!( + resolve.to_uri(&request).unwrap().to_string(), + "gemini://geminiprotocol.net/uri" + ); + + let resolve = Redirect::from_str("30 uri\r\n").unwrap(); + assert_eq!( + resolve.to_uri(&request).unwrap().to_string(), + "gemini://geminiprotocol.net/path/uri" + ); + + let resolve = Redirect::from_str("30 gemini://test.host/uri\r\n").unwrap(); + assert_eq!( + resolve.to_uri(&request).unwrap().to_string(), + "gemini://test.host/uri" + ); +} diff --git a/src/client/connection/response/redirect/error.rs b/src/client/connection/response/redirect/error.rs index b3d3579..029abf6 100644 --- a/src/client/connection/response/redirect/error.rs +++ b/src/client/connection/response/redirect/error.rs @@ -5,6 +5,7 @@ use std::{ #[derive(Debug)] pub enum Error { + Glib(glib::Error), Protocol, Target, Utf8Error(Utf8Error), @@ -13,6 +14,9 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut Formatter) -> Result { match self { + Self::Glib(e) => { + write!(f, "Glib error: {e}") + } Self::Utf8Error(e) => { write!(f, "UTF-8 error: {e}") }