From 86d4e25aa0aba71c11ae467f7f304d7e0ce1be24 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 20:35:27 -0500 Subject: [PATCH 1/6] Add support for a target variable in regular links. This will initially be used for supporting "post://" links. --- src/context/regular_link.rs | 7 +++++++ src/intermediate/regular_link.rs | 25 ++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/context/regular_link.rs b/src/context/regular_link.rs index 9a84f51..e247cb8 100644 --- a/src/context/regular_link.rs +++ b/src/context/regular_link.rs @@ -11,6 +11,7 @@ use super::RenderObject; #[serde(tag = "type")] #[serde(rename = "regular_link")] pub(crate) struct RenderRegularLink { + target: String, raw_link: String, children: Vec, post_blank: organic::types::PostBlank, @@ -25,7 +26,13 @@ render!(RenderRegularLink, IRegularLink, original, render_context, { ret }; + let target = original + .target + .generate_final_target()? + .unwrap_or_else(|| "".to_owned()); + Ok(RenderRegularLink { + target: target, raw_link: original.raw_link.clone(), children, post_blank: original.post_blank, diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index 2b2c249..c0c11e9 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -9,6 +9,7 @@ use crate::error::CustomError; pub(crate) struct IRegularLink { pub(crate) raw_link: String, pub(crate) children: Vec, + pub(crate) target: LinkTarget, pub(crate) post_blank: organic::types::PostBlank, } @@ -25,10 +26,32 @@ intermediate!( } ret }; + let raw_link = original.get_raw_link(); + let target = LinkTarget::from_string(&raw_link)?; Ok(IRegularLink { - raw_link: original.get_raw_link().into_owned(), + raw_link: raw_link.into_owned(), children, + target, post_blank: original.get_post_blank(), }) } ); + +#[derive(Debug, Clone)] +pub(crate) enum LinkTarget { + Raw(String), + Post { post_id: String }, +} + +impl LinkTarget { + pub(crate) fn from_string>(input: S) -> Result { + fn impl_from_string(input: &str) -> Result { + Ok(LinkTarget::Raw(input.to_owned())) + } + impl_from_string(input.as_ref()) + } + + pub(crate) fn generate_final_target(&self) -> Result, CustomError> { + todo!() + } +} From 93e0a2fe98e0ec9454d46546fbc514cbbe319cda Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 20:45:20 -0500 Subject: [PATCH 2/6] Add URL parsing. --- Cargo.lock | 67 ++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/error/error.rs | 7 ++++ src/intermediate/regular_link.rs | 2 + 4 files changed, 77 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 98e120c..2cad9e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "1.1.0" @@ -312,6 +321,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "include_dir" version = "0.7.3" @@ -400,6 +419,7 @@ dependencies = [ "serde_json", "tokio", "toml", + "url", "walkdir", ] @@ -455,6 +475,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -584,6 +610,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.33.0" @@ -630,12 +671,38 @@ dependencies = [ "winnow", ] +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 9b552d9..5f39eee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ serde = { version = "1.0.189", default-features = false, features = ["std", "der serde_json = "1.0.107" tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] } toml = "0.8.2" +url = "2.5.0" walkdir = "2.4.0" # Optimized build for any sort of release. diff --git a/src/error/error.rs b/src/error/error.rs index a6816ee..44064e3 100644 --- a/src/error/error.rs +++ b/src/error/error.rs @@ -16,6 +16,7 @@ pub(crate) enum CustomError { DusterCompile(duster::renderer::CompileError), DusterRender(duster::renderer::RenderError), PathStripPrefix(std::path::StripPrefixError), + UrlParseError(url::ParseError), } impl From for CustomError { @@ -95,3 +96,9 @@ impl From for CustomError { CustomError::PathStripPrefix(value) } } + +impl From for CustomError { + fn from(value: url::ParseError) -> Self { + CustomError::UrlParseError(value) + } +} diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index c0c11e9..bd86a2d 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -1,4 +1,5 @@ use organic::types::StandardProperties; +use url::Url; use super::macros::intermediate; @@ -46,6 +47,7 @@ pub(crate) enum LinkTarget { impl LinkTarget { pub(crate) fn from_string>(input: S) -> Result { fn impl_from_string(input: &str) -> Result { + let parsed = Url::parse(input)?; Ok(LinkTarget::Raw(input.to_owned())) } impl_from_string(input.as_ref()) From 6a086d57de81f97cc4d7d68a6d504fed32efdcc2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 20:55:32 -0500 Subject: [PATCH 3/6] Generate Post link when the protocol is post. --- src/intermediate/regular_link.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index bd86a2d..7fb3d5a 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -41,14 +41,24 @@ intermediate!( #[derive(Debug, Clone)] pub(crate) enum LinkTarget { Raw(String), - Post { post_id: String }, + Post { post_id: Option }, } impl LinkTarget { pub(crate) fn from_string>(input: S) -> Result { fn impl_from_string(input: &str) -> Result { let parsed = Url::parse(input)?; - Ok(LinkTarget::Raw(input.to_owned())) + match parsed.scheme() { + "post" => { + let post_id = parsed + .path_segments() + .ok_or_else(|| "cannot be base")? + .next() + .map(str::to_owned); + Ok(LinkTarget::Post { post_id }) + } + _ => Ok(LinkTarget::Raw(input.to_owned())), + } } impl_from_string(input.as_ref()) } From 24218f297965bd2b2b15a1974c7e4aa1cb13c554 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 21:06:53 -0500 Subject: [PATCH 4/6] Generate post links, so far only top-level. --- .../templates/html/regular_link.dust | 2 +- src/context/regular_link.rs | 2 +- src/intermediate/regular_link.rs | 39 +++++++++++++++---- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/default_environment/templates/html/regular_link.dust b/default_environment/templates/html/regular_link.dust index 2566d30..5a0dcb7 100644 --- a/default_environment/templates/html/regular_link.dust +++ b/default_environment/templates/html/regular_link.dust @@ -1 +1 @@ -{#.children}{>object/}{/.children} +{#.children}{>object/}{/.children} diff --git a/src/context/regular_link.rs b/src/context/regular_link.rs index e247cb8..2fe5a82 100644 --- a/src/context/regular_link.rs +++ b/src/context/regular_link.rs @@ -28,7 +28,7 @@ render!(RenderRegularLink, IRegularLink, original, render_context, { let target = original .target - .generate_final_target()? + .generate_final_target(render_context.clone())? .unwrap_or_else(|| "".to_owned()); Ok(RenderRegularLink { diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index 7fb3d5a..dfed5c3 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -1,9 +1,11 @@ use organic::types::StandardProperties; use url::Url; +use super::get_web_path; use super::macros::intermediate; use super::IObject; +use crate::context::RenderContext; use crate::error::CustomError; #[derive(Debug, Clone)] @@ -47,14 +49,16 @@ pub(crate) enum LinkTarget { impl LinkTarget { pub(crate) fn from_string>(input: S) -> Result { fn impl_from_string(input: &str) -> Result { - let parsed = Url::parse(input)?; + let parsed = Url::parse(input); + if let Err(url::ParseError::RelativeUrlWithoutBase) = parsed { + // For URLs to targets. + // TODO: This shouldn't be raw but instead a variant for targets. + return Ok(LinkTarget::Raw(input.to_owned())); + } + let parsed = parsed?; match parsed.scheme() { "post" => { - let post_id = parsed - .path_segments() - .ok_or_else(|| "cannot be base")? - .next() - .map(str::to_owned); + let post_id = parsed.host_str().map(str::to_owned); Ok(LinkTarget::Post { post_id }) } _ => Ok(LinkTarget::Raw(input.to_owned())), @@ -63,7 +67,26 @@ impl LinkTarget { impl_from_string(input.as_ref()) } - pub(crate) fn generate_final_target(&self) -> Result, CustomError> { - todo!() + pub(crate) fn generate_final_target( + &self, + render_context: RenderContext<'_>, + ) -> Result, CustomError> { + match self { + LinkTarget::Raw(raw_link) => Ok(Some(raw_link.clone())), + LinkTarget::Post { post_id } => { + let path = post_id + .as_ref() + .map(|post_id| { + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + render_context.config.get_relative_path_to_post(post_id), + ) + }) + .map_or(Ok(None), |r| r.map(Some))?; + Ok(path) + } + } } } From 3b63bbdfde35c2a1d5bad739eaf68ff632aecf1a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 21:29:59 -0500 Subject: [PATCH 5/6] Add support for subpaths. --- src/intermediate/regular_link.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index dfed5c3..50a52a7 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -43,7 +43,10 @@ intermediate!( #[derive(Debug, Clone)] pub(crate) enum LinkTarget { Raw(String), - Post { post_id: Option }, + Post { + post_id: Option, + subpath: String, + }, } impl LinkTarget { @@ -59,7 +62,18 @@ impl LinkTarget { match parsed.scheme() { "post" => { let post_id = parsed.host_str().map(str::to_owned); - Ok(LinkTarget::Post { post_id }) + let subpath = { + let subpath = parsed.path(); + if subpath.starts_with('/') { + &subpath[1..] + } else { + subpath + } + }; + Ok(LinkTarget::Post { + post_id, + subpath: subpath.to_owned(), + }) } _ => Ok(LinkTarget::Raw(input.to_owned())), } @@ -73,15 +87,19 @@ impl LinkTarget { ) -> Result, CustomError> { match self { LinkTarget::Raw(raw_link) => Ok(Some(raw_link.clone())), - LinkTarget::Post { post_id } => { + LinkTarget::Post { post_id, subpath } => { let path = post_id .as_ref() .map(|post_id| { + let path_to_post = render_context + .config + .get_relative_path_to_post(post_id) + .join(subpath); get_web_path( render_context.config, render_context.output_root_directory, render_context.output_file, - render_context.config.get_relative_path_to_post(post_id), + path_to_post, ) }) .map_or(Ok(None), |r| r.map(Some))?; From 35cf675c8724a2f3398da3147d491213a664f7d2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 23 Dec 2023 21:36:24 -0500 Subject: [PATCH 6/6] Fix clippy. --- src/context/regular_link.rs | 2 +- src/intermediate/regular_link.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/regular_link.rs b/src/context/regular_link.rs index 2fe5a82..fc77598 100644 --- a/src/context/regular_link.rs +++ b/src/context/regular_link.rs @@ -32,7 +32,7 @@ render!(RenderRegularLink, IRegularLink, original, render_context, { .unwrap_or_else(|| "".to_owned()); Ok(RenderRegularLink { - target: target, + target, raw_link: original.raw_link.clone(), children, post_blank: original.post_blank, diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index 50a52a7..faab0a7 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -64,8 +64,8 @@ impl LinkTarget { let post_id = parsed.host_str().map(str::to_owned); let subpath = { let subpath = parsed.path(); - if subpath.starts_with('/') { - &subpath[1..] + if let Some(subpath) = subpath.strip_prefix('/') { + subpath } else { subpath }