diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c61d6f9a6..f62bd2aa73c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,3 +70,7 @@ - Fixed a bug where the "pattern match on variable" code action would generate invalid patterns by repeating a variable name already used in the same pattern. ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Made link href validation in gleam.toml files less strict so developers can + use relative links for internal documentation. + ([Tristan-Mihai Radulescu](https://github.com/Courtcircuits)) diff --git a/compiler-cli/src/publish.rs b/compiler-cli/src/publish.rs index ea96c2f3abd..84a1c3e1fe4 100644 --- a/compiler-cli/src/publish.rs +++ b/compiler-cli/src/publish.rs @@ -16,6 +16,7 @@ use gleam_core::{ type_, }; use hexpm::version::{Range, Version}; +use http::Uri; use itertools::Itertools; use sha2::Digest; use std::{collections::HashMap, io::Write, path::PathBuf, time::Instant}; @@ -526,7 +527,7 @@ fn metadata_config<'a>( source_files: &[Utf8PathBuf], generated_files: &[(Utf8PathBuf, String)], ) -> Result { - let repo_url = http::Uri::try_from( + let repo_url = Uri::try_from( config .repository .as_ref() @@ -547,6 +548,20 @@ fn metadata_config<'a>( }), }) .collect(); + let links: Vec<(&str, Uri)> = config + .links + .iter() + .map(|l| (l.title.as_str(), l.href.clone())) + .filter(|(_, href)| !href.clone().is_internal()) + .map(|(title, href)| { + ( + title, + href.as_uri().expect("Internal link not marked as internal"), + ) + }) + .chain(repo_url.into_iter().map(|u| ("Repository", u))) + .collect(); + let metadata = ReleaseMetadata { name: &config.name, version: &config.version, @@ -554,12 +569,7 @@ fn metadata_config<'a>( source_files, generated_files, licenses: &config.licences, - links: config - .links - .iter() - .map(|l| (l.title.as_str(), l.href.clone())) - .chain(repo_url.into_iter().map(|u| ("Repository", u))) - .collect(), + links, requirements: requirements?, build_tools: vec!["gleam"], } @@ -696,7 +706,7 @@ pub struct ReleaseMetadata<'a> { source_files: &'a [Utf8PathBuf], generated_files: &'a [(Utf8PathBuf, String)], licenses: &'a Vec, - links: Vec<(&'a str, http::Uri)>, + links: Vec<(&'a str, Uri)>, requirements: Vec>, build_tools: Vec<&'a str>, // What should this be? I can't find it in the API anywhere. @@ -705,7 +715,7 @@ pub struct ReleaseMetadata<'a> { impl ReleaseMetadata<'_> { pub fn as_erlang(&self) -> String { - fn link(link: &(&str, http::Uri)) -> String { + fn link(link: &(&str, Uri)) -> String { format!( "\n {{<<\"{name}\">>, <<\"{url}\">>}}", name = link.0, diff --git a/compiler-core/src/config.rs b/compiler-core/src/config.rs index 77dd4bc9838..8cf8dfe3cff 100644 --- a/compiler-core/src/config.rs +++ b/compiler-core/src/config.rs @@ -980,11 +980,44 @@ pub struct DocsPage { pub source: Utf8PathBuf, } +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] +#[serde(untagged)] +pub enum Href { + #[serde(with = "uri_serde")] + External(Uri), + Internal(std::path::PathBuf), +} + +impl fmt::Display for Href { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Href::External(uri) => write!(f, "{}", uri), + Href::Internal(path) => write!(f, "{}", path.display()), + } + } +} + +impl Href { + pub fn is_internal(&self) -> bool { + match self { + Href::Internal(_) => true, + Href::External(_) => false, + } + } + + // only URIs are considered as external links + pub fn as_uri(&self) -> Option { + match self { + Href::External(uri) => Some(uri.clone()), + Href::Internal(_) => None, + } + } +} + #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] pub struct Link { pub title: String, - #[serde(with = "uri_serde")] - pub href: Uri, + pub href: Href, } // Note we don't use http-serde since we also want to validate the scheme and host is set. @@ -1141,7 +1174,7 @@ licences = ["Apache-2.0", "MIT"] description = "Pretty complex config" target = "erlang" repository = { type = "github", user = "example", repo = "my_dep" } -links = [{ title = "Home page", href = "https://example.com" }] +links = [{ title = "Home page", href = "https://example.com" }, { title = "Internal link", href = "./internal" }, { title = "Second internal link", href = "../internal" }, { title = "Third internal link", href = "internal" } ] internal_modules = ["my_app/internal"] gleam = ">= 0.30.0" diff --git a/compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap b/compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap index dd11f13fcb2..0f75137aece 100644 --- a/compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap +++ b/compiler-core/src/snapshots/gleam_core__config__package_config_to_json.snap @@ -1,8 +1,6 @@ --- source: compiler-core/src/config.rs -assertion_line: 1171 expression: output -snapshot_kind: text --- --- GLEAM.TOML @@ -12,7 +10,7 @@ licences = ["Apache-2.0", "MIT"] description = "Pretty complex config" target = "erlang" repository = { type = "github", user = "example", repo = "my_dep" } -links = [{ title = "Home page", href = "https://example.com" }] +links = [{ title = "Home page", href = "https://example.com" }, { title = "Internal link", href = "./internal" }, { title = "Second internal link", href = "../internal" }, { title = "Third internal link", href = "internal" } ] internal_modules = ["my_app/internal"] gleam = ">= 0.30.0" @@ -86,6 +84,18 @@ allow_read = ["./database.sqlite"] { "title": "Home page", "href": "https://example.com/" + }, + { + "title": "Internal link", + "href": "./internal" + }, + { + "title": "Second internal link", + "href": "../internal" + }, + { + "title": "Third internal link", + "href": "internal" } ], "erlang": {