dsntk_common/
href.rs

1//! # URI reference
2//!
3//! This [HRef] struct utilizes an **href** attribute whose value must be a valid URI reference
4//! [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986), where the path components
5//! may be absolute or relative, the reference has no query component,
6//! and the fragment consists of the value of the **id** of the referenced DMN element.
7
8use self::errors::*;
9use crate::DsntkError;
10use uriparse::URIReference;
11
12/// URI reference used for utilizing `href` attribute.
13#[derive(Debug, Clone)]
14pub struct HRef {
15  /// Namespace built from URI's path components.
16  namespace: Option<String>,
17  /// DMN element's identifier built from URI's fragment.
18  id: String,
19}
20
21impl HRef {
22  /// Returns the optional namespace.
23  pub fn namespace(&self) -> Option<&String> {
24    self.namespace.as_ref()
25  }
26
27  /// Returns the identifier.
28  pub fn id(&self) -> &str {
29    &self.id
30  }
31}
32
33impl TryFrom<&str> for HRef {
34  type Error = DsntkError;
35
36  /// Converts [HRef] from string.
37  fn try_from(s: &str) -> Result<Self, Self::Error> {
38    match URIReference::try_from(s) {
39      Ok(mut uri_reference) => {
40        uri_reference.normalize();
41        let (scheme, authority, path, query, fragment) = uri_reference.into_parts();
42        if query.is_some() {
43          return Err(err_query_not_allowed(s));
44        }
45        if fragment.is_none() {
46          return Err(err_fragment_is_missing(s));
47        }
48        let id = fragment.unwrap().to_string();
49        let base_uri = URIReference::builder()
50          .with_scheme(scheme)
51          .with_authority(authority)
52          .with_path(path)
53          .build()
54          .unwrap() // this unwrap is ok: scheme, authority and path are valid
55          .to_string();
56        let namespace = if base_uri.is_empty() { None } else { Some(base_uri) };
57        Ok(Self { namespace, id })
58      }
59      Err(reason) => Err(err_invalid_reference(s, reason.to_string())),
60    }
61  }
62}
63
64mod errors {
65  use crate::{DsntkError, ToErrorMessage};
66
67  /// Errors reported by [HRef](crate::href::HRef).
68  #[derive(ToErrorMessage)]
69  struct HRefError(String);
70
71  /// Creates an error indicating an invalid reference.
72  pub fn err_invalid_reference(s: &str, reason: String) -> DsntkError {
73    HRefError(format!("invalid reference '{s}', reason: {reason}")).into()
74  }
75
76  /// Creates an error indicating the missing fragment.
77  pub fn err_fragment_is_missing(s: &str) -> DsntkError {
78    HRefError(format!("fragment is missing in reference: '{s}'")).into()
79  }
80
81  /// Creates an error indicating that query is not allowed.
82  pub fn err_query_not_allowed(s: &str) -> DsntkError {
83    HRefError(format!("query is not allowed in reference: '{s}'")).into()
84  }
85}