Skip to content

Commit

Permalink
done identifier; remove temp default features
Browse files Browse the repository at this point in the history
  • Loading branch information
JieningYu committed Jan 30, 2024
1 parent 9eea50b commit 14299d2
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 2 deletions.
1 change: 0 additions & 1 deletion util/identifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ serde = { version = "1.0", optional = true }
rimecraft-edcode = { path = "../edcode", optional = true }

[features]
default = ["serde", "edcode", "vanilla"]
serde = ["dep:serde"]
edcode = ["dep:rimecraft-edcode"]
vanilla = []
239 changes: 239 additions & 0 deletions util/identifier/src/vanilla.rs
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")));
}
}
1 change: 0 additions & 1 deletion util/registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ serde = { version = "1.0", optional = true }
rimecraft-edcode = { path = "../edcode", optional = true }

[features]
default = ["serde", "edcode"]
serde = ["dep:serde"]
edcode = ["dep:rimecraft-edcode"]

0 comments on commit 14299d2

Please sign in to comment.