pub const DEFAULT_DOCS_SAMPLE_BASE_URL: &str = "https://example.com";
pub const SAMPLE_BASE_URL_CONFIG_KEY: &str = "[crates.e2e.snippets].sample_base_url";
pub const SAMPLE_URL_MOCK_ONLY_CONFIG_KEY: &str = "[crates.e2e.snippets].mock_only";
pub const DOCS_SAMPLE_URL_FIXTURE_KEY: &str = "docs.sample_url";
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum InvalidSampleBaseUrl {
#[error("`{key}` is empty; remove the key rather than declaring an address of no characters")]
Empty { key: &'static str },
#[error(
"`{key}` must contain no whitespace, got `{value}`; \
it is prefixed onto a fixture's relative path to form a URL a reader pastes into a shell"
)]
Whitespace { key: &'static str, value: String },
#[error(
"`{key}` must be absolute and name a scheme (e.g. \
`https://samples.example.org`), got `{value}`"
)]
Relative { key: &'static str, value: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DocsSampleBaseUrl<'a> {
base: &'a str,
configured: bool,
}
impl<'a> DocsSampleBaseUrl<'a> {
pub fn resolve(configured: Option<&'a str>) -> Result<Self, InvalidSampleBaseUrl> {
Self::resolve_at(configured, SAMPLE_BASE_URL_CONFIG_KEY)
}
pub fn resolve_at(configured: Option<&'a str>, key: &'static str) -> Result<Self, InvalidSampleBaseUrl> {
let Some(value) = configured else {
return Ok(Self {
base: DEFAULT_DOCS_SAMPLE_BASE_URL,
configured: false,
});
};
let trimmed = value.trim();
if trimmed.is_empty() {
return Err(InvalidSampleBaseUrl::Empty { key });
}
if trimmed.chars().any(char::is_whitespace) {
return Err(InvalidSampleBaseUrl::Whitespace {
key,
value: trimmed.to_string(),
});
}
if !has_url_scheme(trimmed) {
return Err(InvalidSampleBaseUrl::Relative {
key,
value: trimmed.to_string(),
});
}
Ok(Self {
base: trimmed.trim_end_matches('/'),
configured: true,
})
}
pub fn base(&self) -> &'a str {
self.base
}
pub fn is_placeholder(&self) -> bool {
!self.configured
}
pub fn join(&self, path: &str) -> String {
if path.is_empty() {
return self.base.to_string();
}
if path.starts_with('/') {
format!("{}{path}", self.base)
} else {
format!("{}/{path}", self.base)
}
}
}
pub fn has_url_scheme(value: &str) -> bool {
value.contains("://")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_unconfigured_base_falls_back_to_the_reserved_documentation_domain() {
let resolved = DocsSampleBaseUrl::resolve(None).expect("no configuration always resolves");
assert_eq!(resolved.base(), "https://example.com");
assert!(
resolved.is_placeholder(),
"the fallback must announce itself as a placeholder so the run can report it"
);
}
#[test]
fn a_configured_base_is_used_verbatim_and_is_not_a_placeholder() {
let resolved = DocsSampleBaseUrl::resolve(Some("https://samples.example.org")).expect("valid base resolves");
assert_eq!(resolved.base(), "https://samples.example.org");
assert!(!resolved.is_placeholder());
}
#[test]
fn a_trailing_slash_is_normalized_away_so_joins_do_not_double_it() {
let resolved = DocsSampleBaseUrl::resolve(Some("https://samples.example.org/")).expect("valid base resolves");
assert_eq!(resolved.base(), "https://samples.example.org");
assert_eq!(resolved.join("/report.pdf"), "https://samples.example.org/report.pdf");
}
#[test]
fn joining_a_path_without_a_leading_slash_still_inserts_the_separator() {
let resolved = DocsSampleBaseUrl::resolve(Some("https://samples.example.org")).expect("valid base resolves");
assert_eq!(resolved.join("report.pdf"), "https://samples.example.org/report.pdf");
}
#[test]
fn joining_an_empty_path_yields_the_bare_base() {
let resolved = DocsSampleBaseUrl::resolve(Some("https://samples.example.org")).expect("valid base resolves");
assert_eq!(resolved.join(""), "https://samples.example.org");
}
#[test]
fn an_empty_configured_base_is_rejected_rather_than_falling_back() {
assert_eq!(
DocsSampleBaseUrl::resolve(Some(" ")).expect_err("an empty base cannot form a URL"),
InvalidSampleBaseUrl::Empty {
key: SAMPLE_BASE_URL_CONFIG_KEY
}
);
}
#[test]
fn a_base_with_whitespace_is_rejected() {
let error =
DocsSampleBaseUrl::resolve(Some("https://samples.example.org/my docs")).expect_err("whitespace is invalid");
assert!(
error.to_string().contains("whitespace"),
"error must name the defect: {error}"
);
}
#[test]
fn a_scheme_less_base_is_rejected_because_a_reader_cannot_paste_it() {
let error = DocsSampleBaseUrl::resolve(Some("samples.example.org")).expect_err("a relative base is invalid");
assert!(
error.to_string().contains("absolute"),
"error must name the defect: {error}"
);
}
#[test]
fn a_fixture_level_declaration_is_rejected_against_the_fixture_key_not_the_config_key() {
let error = DocsSampleBaseUrl::resolve_at(Some("samples.example.org"), DOCS_SAMPLE_URL_FIXTURE_KEY)
.expect_err("a relative fixture-level address is invalid");
let message = error.to_string();
assert!(
message.contains(DOCS_SAMPLE_URL_FIXTURE_KEY),
"the rejection must name the fixture key: {message}"
);
assert!(
!message.contains(SAMPLE_BASE_URL_CONFIG_KEY),
"the rejection must not send the author to an alef.toml key they never wrote: {message}"
);
}
#[test]
fn a_non_http_scheme_is_accepted() {
let resolved = DocsSampleBaseUrl::resolve(Some("s3://sample-bucket")).expect("any scheme is a valid base");
assert_eq!(resolved.join("/report.pdf"), "s3://sample-bucket/report.pdf");
}
}