nmd_core/resource/
resource_reference.rsuse getset::{Getters, Setters};
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{resource::remote_resource::RemoteResource, utility::file_utility};
static OF_INTERNAL_RESOURCE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(.*)?#(.*)").unwrap());
const VALUE_SEPARATOR: &str = "-";
const SPACE_REPLACER: char = '-';
#[derive(Error, Debug)]
pub enum ResourceReferenceError {
#[error("invalid URL reference")]
InvalidUrlReference,
#[error("invalid URL reference")]
InvalidAssetReference,
#[error("invalid URL reference")]
InvalidInternalReference,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ResourceReferenceType {
Url,
Asset,
Internal
}
#[derive(Debug, Clone, Getters, Setters, Serialize, Deserialize)]
pub struct ResourceReference {
#[getset(get = "pub", set = "pub")]
value: String,
ref_type: ResourceReferenceType
}
impl ResourceReference {
pub fn new(value: &str, ref_type: ResourceReferenceType) -> Self {
Self {
value: String::from(value),
ref_type
}
}
pub fn of_url(raw: &str) -> Result<Self, ResourceReferenceError> {
if !RemoteResource::is_valid_remote_resource(raw) {
return Err(ResourceReferenceError::InvalidUrlReference)
}
Ok(Self::new(raw, ResourceReferenceType::Url))
}
pub fn of_asset(raw: &str) -> Result<Self, ResourceReferenceError> {
if !file_utility::is_file_path(raw) {
return Err(ResourceReferenceError::InvalidAssetReference)
}
Ok(Self::new(raw, ResourceReferenceType::Asset))
}
pub fn of_internal(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
let raw = raw.to_lowercase();
let caps = OF_INTERNAL_RESOURCE_REGEX.captures(&raw);
if caps.is_none() {
return Err(ResourceReferenceError::InvalidInternalReference)
}
let caps = caps.unwrap();
let document_name = caps.get(1);
let value = caps.get(2);
if value.is_none() {
return Err(ResourceReferenceError::InvalidInternalReference)
}
let value = value.unwrap().as_str();
if let Some(document_name) = document_name {
let document_name = document_name.as_str().trim();
if !document_name.is_empty() {
return Ok(Self::new(&format!("{}{}{}", document_name, VALUE_SEPARATOR, value), ResourceReferenceType::Internal))
}
}
Ok(Self::new(&format!("{}{}{}", document_name_if_missed.unwrap().to_string(), VALUE_SEPARATOR, value), ResourceReferenceType::Internal))
}
pub fn of_internal_from_without_sharp(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
let raw_with_sharp = format!("#{}", raw);
Self::of_internal(&raw_with_sharp, document_name_if_missed)
}
pub fn of(raw: &str, document_name_if_missed: Option<&impl ToString>) -> Result<Self, ResourceReferenceError> {
if RemoteResource::is_valid_remote_resource(raw) {
return Self::of_url(raw)
}
if raw.contains("#") {
return Self::of_internal(raw, document_name_if_missed)
} else { return Self::of_asset(raw)
}
}
pub fn build(&self) -> String {
match self.ref_type {
ResourceReferenceType::Url | ResourceReferenceType::Asset => String::from(&self.value),
ResourceReferenceType::Internal => format!("#{}", Self::parse_str(&self.value))
}
}
pub fn build_without_internal_sharp(&self) -> String {
match self.ref_type {
ResourceReferenceType::Url | ResourceReferenceType::Asset => String::from(&self.value),
ResourceReferenceType::Internal => format!("{}", Self::parse_str(&self.value))
}
}
fn parse_str(s: &str) -> String {
s.chars().map(|c| {
if c.is_alphanumeric() {
return c;
}
if c == ' ' {
return SPACE_REPLACER;
}
'-'
}).collect()
}
}