-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
done identifier; remove temp default features
- Loading branch information
Showing
3 changed files
with
239 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,240 @@ | ||
//! Vanilla implementation of namespace and path. | ||
use std::{hash::Hash, str::FromStr, sync::Arc}; | ||
|
||
use crate::Separate; | ||
|
||
pub const MINECRAFT: Namespace = Namespace(ArcCowStr::Ref("minecraft")); | ||
|
||
/// Namespace of an vanilla `Identifier`. | ||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct Namespace(ArcCowStr<'static>); | ||
|
||
impl Namespace { | ||
/// Creates a new `Namespace` from the given value. | ||
#[inline] | ||
pub fn new(value: impl Into<Arc<str>>) -> Self { | ||
let value = value.into(); | ||
validate_namespace(&value).unwrap(); | ||
Self(ArcCowStr::Arc(value)) | ||
} | ||
|
||
/// Creates a new `Namespace` from the given value | ||
/// at compile time. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The given namespace shoule be all [a-z0-9_.-] character. | ||
pub const unsafe fn const_new(value: &'static str) -> Self { | ||
Self(ArcCowStr::Ref(value)) | ||
} | ||
|
||
/// Returns the inner value of the `Namespace`. | ||
#[inline] | ||
pub fn as_str(&self) -> &str { | ||
self.0.as_ref() | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Namespace { | ||
#[inline] | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
self.0.fmt(f) | ||
} | ||
} | ||
|
||
impl FromStr for Namespace { | ||
type Err = Error; | ||
|
||
#[inline] | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
validate_namespace(s)?; | ||
Ok(Self(ArcCowStr::Arc(s.into()))) | ||
} | ||
} | ||
|
||
impl Separate for Namespace { | ||
const SEPARATOR: char = ':'; | ||
} | ||
|
||
impl Default for Namespace { | ||
#[inline] | ||
fn default() -> Self { | ||
MINECRAFT.clone() | ||
} | ||
} | ||
|
||
/// Path of an vanilla `Identifier`. | ||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub struct Path(ArcCowStr<'static>); | ||
|
||
impl Path { | ||
/// Creates a new `Path` from the given value. | ||
#[inline] | ||
pub fn new(value: impl Into<Arc<str>>) -> Self { | ||
let value = value.into(); | ||
validate_path(&value).unwrap(); | ||
Self(ArcCowStr::Arc(value)) | ||
} | ||
|
||
/// Creates a new `Path` from the given value | ||
/// at compile time. | ||
/// | ||
/// # Safety | ||
/// | ||
/// The given path shoule be all [a-z0-9/_.-] character. | ||
#[inline] | ||
pub const unsafe fn new_unchecked(value: &'static str) -> Self { | ||
Self(ArcCowStr::Ref(value)) | ||
} | ||
|
||
/// Returns the inner value of the `Path`. | ||
#[inline] | ||
pub fn as_str(&self) -> &str { | ||
self.0.as_ref() | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Path { | ||
#[inline] | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
self.0.fmt(f) | ||
} | ||
} | ||
|
||
impl FromStr for Path { | ||
type Err = Error; | ||
|
||
#[inline] | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
validate_path(s)?; | ||
Ok(Self(ArcCowStr::Arc(s.into()))) | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
enum ArcCowStr<'a> { | ||
Arc(Arc<str>), | ||
Ref(&'a str), | ||
} | ||
|
||
impl<'a> std::fmt::Display for ArcCowStr<'a> { | ||
#[inline] | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ArcCowStr::Arc(s) => s.fmt(f), | ||
ArcCowStr::Ref(s) => s.fmt(f), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Hash for ArcCowStr<'a> { | ||
#[inline] | ||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | ||
match self { | ||
ArcCowStr::Arc(s) => (**s).hash(state), | ||
ArcCowStr::Ref(s) => (**s).hash(state), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> PartialEq for ArcCowStr<'a> { | ||
#[inline] | ||
fn eq(&self, other: &Self) -> bool { | ||
match (self, other) { | ||
(ArcCowStr::Arc(s), ArcCowStr::Arc(o)) => s == o, | ||
(ArcCowStr::Ref(s), ArcCowStr::Ref(o)) => s == o, | ||
(ArcCowStr::Arc(s), ArcCowStr::Ref(o)) => **s == **o, | ||
(ArcCowStr::Ref(s), ArcCowStr::Arc(o)) => **s == **o, | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Eq for ArcCowStr<'a> {} | ||
|
||
impl<'a> PartialOrd for ArcCowStr<'a> { | ||
#[inline] | ||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl<'a> Ord for ArcCowStr<'a> { | ||
fn cmp(&self, other: &Self) -> std::cmp::Ordering { | ||
match (self, other) { | ||
(ArcCowStr::Arc(s), ArcCowStr::Arc(o)) => s.cmp(o), | ||
(ArcCowStr::Ref(s), ArcCowStr::Ref(o)) => s.cmp(o), | ||
(ArcCowStr::Arc(s), ArcCowStr::Ref(o)) => (**s).cmp(*o), | ||
(ArcCowStr::Ref(s), ArcCowStr::Arc(o)) => (**s).cmp(&**o), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> AsRef<str> for ArcCowStr<'a> { | ||
#[inline] | ||
fn as_ref(&self) -> &str { | ||
match self { | ||
ArcCowStr::Arc(s) => s.as_ref(), | ||
ArcCowStr::Ref(s) => s, | ||
} | ||
} | ||
} | ||
|
||
fn validate_namespace(value: &str) -> Result<(), Error> { | ||
for c in value.chars() { | ||
if !matches!(c, '_' | '-' | 'a'..='z' | '0'..='9' | '.') { | ||
return Err(Error::InvalidNamespace(value.to_owned())); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn validate_path(value: &str) -> Result<(), Error> { | ||
for c in value.chars() { | ||
if !matches!(c, '_' | '-' | '/' | 'a'..='z' | '0'..='9' | '.') { | ||
return Err(Error::InvalidPath(value.to_owned())); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Error type for `Namespace` and `Path`. | ||
#[derive(Debug)] | ||
pub enum Error { | ||
InvalidNamespace(String), | ||
InvalidPath(String), | ||
} | ||
|
||
impl std::fmt::Display for Error { | ||
#[inline] | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Error::InvalidNamespace(s) => write!(f, "invalid namespace: {}", s), | ||
Error::InvalidPath(s) => write!(f, "invalid path: {}", s), | ||
} | ||
} | ||
} | ||
|
||
/// Identifier of vanilla. | ||
pub type Identifier = crate::Identifier<Namespace, Path>; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::vanilla::{Namespace, Path}; | ||
|
||
use super::Identifier; | ||
|
||
#[test] | ||
fn display() { | ||
let id = Identifier::new(Namespace::new("foo"), Path::new("bar")); | ||
assert_eq!(id.to_string(), "foo:bar"); | ||
} | ||
|
||
#[test] | ||
fn parse() { | ||
let id: Identifier = "foo:bar".parse().unwrap(); | ||
assert_eq!(id, Identifier::new(Namespace::new("foo"), Path::new("bar"))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters