nmd_core/resource/
resource_reference.rs1use getset::{Getters, Setters};
2use once_cell::sync::Lazy;
3use regex::Regex;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use crate::{resource::remote_resource::RemoteResource, utility::file_utility};
8
9static OF_INTERNAL_RESOURCE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(.*)?#(.*)").unwrap());
10
11const VALUE_SEPARATOR: &str = "-";
12const SPACE_REPLACER: char = '-';
13
14
15#[derive(Error, Debug)]
16pub enum ResourceReferenceError {
17 #[error("invalid URL reference")]
18 InvalidUrlReference,
19
20 #[error("invalid URL reference")]
21 InvalidAssetReference,
22
23 #[error("invalid URL reference")]
24 InvalidInternalReference,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum ResourceReferenceType {
29 Url,
30 Asset,
31 Internal
32}
33
34
35#[derive(Debug, Clone, Getters, Setters, Serialize, Deserialize)]
45pub struct ResourceReference {
46
47 #[getset(get = "pub", set = "pub")]
48 value: String,
49
50 ref_type: ResourceReferenceType
51}
52
53impl ResourceReference {
54 pub fn new(value: &str, ref_type: ResourceReferenceType) -> Self {
55 Self {
56 value: String::from(value),
57 ref_type
58 }
59 }
60
61 pub fn of_url(raw: &str) -> Result<Self, ResourceReferenceError> {
62
63 if !RemoteResource::is_valid_remote_resource(raw) {
64 return Err(ResourceReferenceError::InvalidUrlReference)
65 }
66
67 Ok(Self::new(raw, ResourceReferenceType::Url))
68 }
69
70 pub fn of_asset(raw: &str) -> Result<Self, ResourceReferenceError> {
71 if !file_utility::is_file_path(raw) {
72 return Err(ResourceReferenceError::InvalidAssetReference)
73 }
74
75 Ok(Self::new(raw, ResourceReferenceType::Asset))
76 }
77
78 pub fn of_internal(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
84
85 let raw = raw.to_lowercase();
86
87 let caps = OF_INTERNAL_RESOURCE_REGEX.captures(&raw);
88
89 if caps.is_none() {
90 return Err(ResourceReferenceError::InvalidInternalReference)
91 }
92
93 let caps = caps.unwrap();
94
95 let document_name = caps.get(1);
96 let value = caps.get(2);
97
98 if value.is_none() {
99 return Err(ResourceReferenceError::InvalidInternalReference)
100 }
101 let value = value.unwrap().as_str();
102
103 if let Some(document_name) = document_name {
104
105 let document_name = document_name.as_str().trim();
106
107 if !document_name.is_empty() {
108
109 return Ok(Self::new(&format!("{}{}{}", document_name, VALUE_SEPARATOR, value), ResourceReferenceType::Internal))
110 }
111 }
112
113 Ok(Self::new(&format!("{}{}{}", document_name_if_missed.unwrap().to_string(), VALUE_SEPARATOR, value), ResourceReferenceType::Internal))
114
115 }
116
117 pub fn of_internal_from_without_sharp(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
118
119 let raw_with_sharp = format!("#{}", raw);
120
121 Self::of_internal(&raw_with_sharp, document_name_if_missed)
122 }
123
124 pub fn of(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
132
133 if RemoteResource::is_valid_remote_resource(raw) {
134 return Self::of_url(raw)
135 }
136
137 if raw.contains("#") {
138 return Self::of_internal(raw, document_name_if_missed)
139
140 } else { return Self::of_asset(raw)
142 }
143 }
144
145 pub fn build(&self) -> String {
146
147 match self.ref_type {
148 ResourceReferenceType::Url | ResourceReferenceType::Asset => String::from(&self.value),
149 ResourceReferenceType::Internal => format!("#{}", Self::parse_str(&self.value))
150 }
151 }
152
153 pub fn build_without_internal_sharp(&self) -> String {
154
155 match self.ref_type {
156 ResourceReferenceType::Url | ResourceReferenceType::Asset => String::from(&self.value),
157 ResourceReferenceType::Internal => format!("{}", Self::parse_str(&self.value))
158 }
159
160 }
161
162 fn parse_str(s: &str) -> String {
163
164 s.chars().map(|c| {
165
166 if c.is_alphanumeric() {
167 return c;
168 }
169
170 if c == ' ' {
171 return SPACE_REPLACER;
172 }
173
174 '-'
175 }).collect()
176 }
177}