use std::ops::Deref;
use iri_string::types::{UriReferenceStr, UriReferenceString};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LinkTarget<TargetUriRef = UriReferenceString>(pub TargetUriRef);
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
pub enum InvalidEncodedLinkTarget {
#[error("Link target Uri ref must be enclosed in angle brackets")]
NotEnclosedInAngleBrackets,
#[error("Link target doesn't represent a valid uri reference")]
InvalidUriRef,
}
impl<TargetUriRef> Deref for LinkTarget<TargetUriRef> {
type Target = TargetUriRef;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<TargetUriRef> LinkTarget<TargetUriRef>
where
TargetUriRef: AsRef<UriReferenceStr>,
{
#[inline]
pub(crate) fn push_encoded_str(&self, buffer: &mut String) {
buffer.push('<');
buffer.push_str(self.0.as_ref().as_str());
buffer.push('>');
}
#[inline]
pub fn str_encode(&self) -> String {
let mut encoded = String::new();
self.push_encoded_str(&mut encoded);
encoded
}
}
impl<TargetUriRef> LinkTarget<TargetUriRef>
where
TargetUriRef: for<'a> TryFrom<&'a str>,
{
pub fn decode(encoded_str: &str) -> Result<Self, InvalidEncodedLinkTarget> {
if !encoded_str.starts_with('<') || !encoded_str.ends_with('>') {
return Err(InvalidEncodedLinkTarget::NotEnclosedInAngleBrackets);
}
let target_str = &encoded_str[1..encoded_str.len() - 1];
Ok(Self(
target_str
.try_into()
.map_err(|_| InvalidEncodedLinkTarget::InvalidUriRef)?,
))
}
}
#[cfg(test)]
mod tests {
use claims::*;
use iri_string::types::UriReferenceString;
use rstest::rstest;
use super::*;
#[rstest]
#[case("<>", "")]
#[case("</>", "/")]
#[case(
"<http://example.com/TheBook/chapter2>",
"http://example.com/TheBook/chapter2"
)]
#[case("<http://example.net/foo>", "http://example.net/foo")]
#[case("</terms>", "/terms")]
#[case("</TheBook/chapter2>", "/TheBook/chapter2")]
#[case("<http://example.org/>", "http://example.org/")]
#[case("<abc>", "abc")]
fn valid_link_target_will_be_decoded_correctly(
#[case] encoded_link_target_str: &str,
#[case] expected_uri_ref_str: &str,
) {
let link_target = assert_ok!(LinkTarget::<UriReferenceString>::decode(
encoded_link_target_str
));
assert_eq!(
link_target.as_str(),
expected_uri_ref_str,
"uri ref is parsed incorrectly"
);
}
#[rstest]
#[case("a/")]
#[case("http://example.org/a")]
fn unenclosed_link_target_will_be_rejected(#[case] encoded_link_target_str: &str) {
assert_err_eq!(
LinkTarget::<UriReferenceString>::decode(encoded_link_target_str),
InvalidEncodedLinkTarget::NotEnclosedInAngleBrackets
);
}
#[rstest]
#[case("<a b>")]
fn invalid_ref_link_target_will_be_rejected(#[case] encoded_link_target_str: &str) {
assert_matches!(
assert_err!(LinkTarget::<UriReferenceString>::decode(
encoded_link_target_str
)),
InvalidEncodedLinkTarget::InvalidUriRef
);
}
}