use self::errors::*;
use crate::DmntkError;
use std::convert::TryFrom;
use uriparse::{RelativeReference, URIReference, URI};
#[derive(Debug, Clone)]
pub struct HRef {
namespace: Option<String>,
id: String,
}
impl HRef {
pub fn namespace(&self) -> Option<&String> {
self.namespace.as_ref()
}
pub fn id(&self) -> &str {
&self.id
}
}
impl TryFrom<&str> for HRef {
type Error = DmntkError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if let Ok(reference) = RelativeReference::try_from(value) {
if reference.has_query() {
return Err(err_invalid_reference(value));
}
let id = reference.fragment().ok_or_else(|| err_invalid_reference_no_fragment(value))?.to_string();
let path = reference.path().to_string().trim().trim_end_matches('/').to_string();
let namespace = if path.is_empty() { None } else { Some(path) };
return Ok(Self { namespace, id });
}
if let Ok(uri_reference) = URIReference::try_from(value) {
if let Ok(uri) = URI::try_from(uri_reference) {
if uri.has_query() {
return Err(err_invalid_reference(value));
}
let id = uri.fragment().ok_or_else(|| err_invalid_reference_no_fragment(value))?.to_string();
let path = uri.into_base_uri().to_string().trim().trim_end_matches('/').to_string();
let namespace = if path.is_empty() { None } else { Some(path) };
return Ok(Self { namespace, id });
}
}
Err(err_invalid_reference(value))
}
}
mod errors {
use crate::{DmntkError, ToErrorMessage};
#[derive(ToErrorMessage)]
struct HRefError(String);
pub fn err_invalid_reference(s: &str) -> DmntkError {
HRefError(format!("invalid reference: '{s}'")).into()
}
pub fn err_invalid_reference_no_fragment(s: &str) -> DmntkError {
HRefError(format!("no fragment in reference: '{s}'")).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_relative_references() {
assert_eq!(r#"Err(DmntkError("<HRefError> no fragment in reference: ''"))"#, format!("{:?}", HRef::try_from("")));
assert_eq!(
r#"Err(DmntkError("<HRefError> no fragment in reference: 'documents'"))"#,
format!("{:?}", HRef::try_from("documents"))
);
assert_eq!(
r#"Ok(HRef { namespace: Some("documents"), id: "_b51ac78b-fd76-42fc-a12d-aad7150c9278" })"#,
format!("{:?}", HRef::try_from("documents#_b51ac78b-fd76-42fc-a12d-aad7150c9278"))
);
assert_eq!(
r#"Ok(HRef { namespace: None, id: "_b51ac78b-fd76-42fc-a12d-aad7150c9278" })"#,
format!("{:?}", HRef::try_from("#_b51ac78b-fd76-42fc-a12d-aad7150c9278"))
);
assert_eq!(
r#"Err(DmntkError("<HRefError> invalid reference: 'documents?name=Introduction#_b51ac78b-fd76-42fc-a12d-aad7150c9278'"))"#,
format!("{:?}", HRef::try_from("documents?name=Introduction#_b51ac78b-fd76-42fc-a12d-aad7150c9278"))
);
}
#[test]
fn test_absolute_references() {
assert_eq!(r#"Err(DmntkError("<HRefError> no fragment in reference: ''"))"#, format!("{:?}", HRef::try_from("")));
assert_eq!(
r#"Err(DmntkError("<HRefError> invalid reference: ' '"))"#,
format!("{:?}", HRef::try_from(" "))
);
assert_eq!(
r#"Ok(HRef { namespace: Some("https://dmntk.io/documents"), id: "_b51ac78b-fd76-42fc-a12d-aad7150c9278" })"#,
format!("{:?}", HRef::try_from("https://dmntk.io/documents#_b51ac78b-fd76-42fc-a12d-aad7150c9278"))
);
assert_eq!(
r#"Err(DmntkError("<HRefError> no fragment in reference: 'https://dmntk.io/documents'"))"#,
format!("{:?}", HRef::try_from("https://dmntk.io/documents"))
);
assert_eq!(
r#"Err(DmntkError("<HRefError> invalid reference: 'https::\\/dmntk.io/documents#id'"))"#,
format!("{:?}", HRef::try_from("https::\\/dmntk.io/documents#id"))
);
}
}