use serde::{Deserialize, Serialize}; use std::{fmt::Display, path::Path, str::FromStr}; #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct AssetPath { #[serde(skip)] prefix: Option, path: String, } impl AssetPath { fn check_prefix(prefix: &str) -> String { if prefix.ends_with('/') { prefix.to_string() } else { format!("{}/", prefix) } } pub fn assume_prefix_free(&mut self) { assert!(self.prefix.is_none(), "Prefix already set!"); self.prefix = Some(String::new()); } pub fn set_prefix(&mut self, prefix: &str) { assert!(self.prefix.is_none(), "Prefix already set!"); self.prefix = Some(Self::check_prefix(prefix)); } pub fn has_prefix(&self) -> bool { match &self.prefix { Some(prefix) => !prefix.is_empty(), None => false, } } pub fn prefix(&self) -> Option<&str> { self.prefix.as_ref().map(|s| s.as_str()) } pub fn set_path(&mut self, path: impl ToString) { self.path = path.to_string(); } pub fn full_path(&self) -> String { assert!(self.prefix.is_some(), "Prefix must be set!"); format!("{}{}", self.prefix.clone().unwrap(), self.path) } pub fn path_without_prefix(&self) -> &str { &self.path } pub fn is_empty(&self) -> bool { self.path.is_empty() } pub fn exists(&self) -> bool { let s = self.full_path(); let path = Path::new(&s); path.exists() } } impl Display for AssetPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.path) } } impl FromStr for AssetPath { type Err = String; fn from_str(s: &str) -> Result { Ok(Self::from(s)) } } impl From<(&str, &str)> for AssetPath { fn from((prefix, path): (&str, &str)) -> Self { Self { prefix: Some(Self::check_prefix(prefix)), path: path.to_string(), } } } impl From<(String, &str)> for AssetPath { fn from((prefix, path): (String, &str)) -> Self { Self { prefix: Some(Self::check_prefix(&prefix)), path: path.to_string(), } } } impl From<(&str, String)> for AssetPath { fn from((prefix, path): (&str, String)) -> Self { Self { prefix: Some(Self::check_prefix(prefix)), path, } } } impl From<(String, String)> for AssetPath { fn from((prefix, path): (String, String)) -> Self { Self { prefix: Some(Self::check_prefix(&prefix)), path, } } } impl From<&str> for AssetPath { fn from(path: &str) -> Self { Self { prefix: None, path: path.to_string(), } } } impl From for AssetPath { fn from(path: String) -> Self { Self { prefix: None, path } } }