mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Rewrite ident module (again) (#111)
Ident is now a wrapper around any string type `S`.
This commit is contained in:
parent
afe390836c
commit
7d0c254874
|
@ -16,7 +16,6 @@ aes = "0.7.5"
|
|||
anyhow = "1.0.65"
|
||||
approx = "0.5.1"
|
||||
arrayvec = "0.7.2"
|
||||
ascii = "1.1.0"
|
||||
async-trait = "0.1.57"
|
||||
base64 = "0.13.0"
|
||||
bitfield-struct = "0.1.7"
|
||||
|
@ -36,12 +35,12 @@ rsa = "0.6.1"
|
|||
rsa-der = "0.3.0"
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
valence_nbt = "0.2.0"
|
||||
sha1 = "0.10.5"
|
||||
sha2 = "0.10.6"
|
||||
thiserror = "1.0.35"
|
||||
url = { version = "2.2.2", features = ["serde"] }
|
||||
uuid = { version = "1.1.2", features = ["serde"] }
|
||||
valence_nbt = "0.2.0"
|
||||
vek = "0.15.8"
|
||||
|
||||
[dependencies.tokio]
|
||||
|
|
12
src/biome.rs
12
src/biome.rs
|
@ -26,7 +26,7 @@ pub struct BiomeId(pub(crate) u16);
|
|||
pub struct Biome {
|
||||
/// The unique name for this biome. The name can be
|
||||
/// seen in the F3 debug menu.
|
||||
pub name: Ident<'static>,
|
||||
pub name: Ident<String>,
|
||||
pub precipitation: BiomePrecipitation,
|
||||
pub sky_color: u32,
|
||||
pub water_fog_color: u32,
|
||||
|
@ -36,7 +36,7 @@ pub struct Biome {
|
|||
pub grass_color: Option<u32>,
|
||||
pub grass_color_modifier: BiomeGrassColorModifier,
|
||||
pub music: Option<BiomeMusic>,
|
||||
pub ambient_sound: Option<Ident<'static>>,
|
||||
pub ambient_sound: Option<Ident<String>>,
|
||||
pub additions_sound: Option<BiomeAdditionsSound>,
|
||||
pub mood_sound: Option<BiomeMoodSound>,
|
||||
pub particle: Option<BiomeParticle>,
|
||||
|
@ -202,20 +202,20 @@ pub enum BiomeGrassColorModifier {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct BiomeMusic {
|
||||
pub replace_current_music: bool,
|
||||
pub sound: Ident<'static>,
|
||||
pub sound: Ident<String>,
|
||||
pub min_delay: i32,
|
||||
pub max_delay: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BiomeAdditionsSound {
|
||||
pub sound: Ident<'static>,
|
||||
pub sound: Ident<String>,
|
||||
pub tick_chance: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BiomeMoodSound {
|
||||
pub sound: Ident<'static>,
|
||||
pub sound: Ident<String>,
|
||||
pub tick_delay: i32,
|
||||
pub offset: f64,
|
||||
pub block_search_extent: i32,
|
||||
|
@ -224,5 +224,5 @@ pub struct BiomeMoodSound {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct BiomeParticle {
|
||||
pub probability: f32,
|
||||
pub kind: Ident<'static>,
|
||||
pub kind: Ident<String>,
|
||||
}
|
||||
|
|
|
@ -505,7 +505,7 @@ impl<C: Config> Client<C> {
|
|||
/// Plays a sound to the client at a given position.
|
||||
pub fn play_sound(
|
||||
&mut self,
|
||||
name: Ident<'static>,
|
||||
name: Ident<String>,
|
||||
category: SoundCategory,
|
||||
pos: Vec3<f64>,
|
||||
volume: f32,
|
||||
|
|
|
@ -17,11 +17,11 @@ use crate::{ident, LIBRARY_NAMESPACE};
|
|||
pub struct DimensionId(pub(crate) u16);
|
||||
|
||||
impl DimensionId {
|
||||
pub(crate) fn dimension_type_name(self) -> Ident<'static> {
|
||||
pub(crate) fn dimension_type_name(self) -> Ident<String> {
|
||||
ident!("{LIBRARY_NAMESPACE}:dimension_type_{}", self.0)
|
||||
}
|
||||
|
||||
pub(crate) fn dimension_name(self) -> Ident<'static> {
|
||||
pub(crate) fn dimension_name(self) -> Ident<String> {
|
||||
ident!("{LIBRARY_NAMESPACE}:dimension_{}", self.0)
|
||||
}
|
||||
}
|
||||
|
|
404
src/ident.rs
404
src/ident.rs
|
@ -1,20 +1,23 @@
|
|||
//! Resource identifiers.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Write;
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, hash};
|
||||
|
||||
use ascii::{AsAsciiStr, AsciiChar, AsciiStr, IntoAsciiString};
|
||||
use hash::Hash;
|
||||
use serde::de::Visitor;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||
use thiserror::Error;
|
||||
use serde::de::Error as _;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::nbt;
|
||||
use crate::protocol::{Decode, Encode};
|
||||
|
||||
/// A wrapper around a string type `S` which guarantees the wrapped string is a
|
||||
/// valid resource identifier.
|
||||
///
|
||||
/// A resource identifier is a string divided into a "namespace" part and a
|
||||
/// "path" part. For instance `minecraft:apple` and `valence:frobnicator` are
|
||||
/// both valid identifiers.
|
||||
|
@ -25,270 +28,267 @@ use crate::protocol::{Decode, Encode};
|
|||
///
|
||||
/// A string must match the regex `^([a-z0-9_-]+:)?[a-z0-9_\/.-]+$` to be a
|
||||
/// valid identifier.
|
||||
#[derive(Clone, Eq)]
|
||||
pub struct Ident<'a> {
|
||||
string: Cow<'a, AsciiStr>,
|
||||
/// The index of the first character of the path part in the string.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Ident<S> {
|
||||
string: S,
|
||||
path_start: usize,
|
||||
}
|
||||
|
||||
/// The error type created when an [`Ident`] cannot be parsed from a
|
||||
/// string. Contains the offending string.
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[error("invalid resource identifier \"{0}\"")]
|
||||
pub struct IdentParseError<'a>(pub Cow<'a, str>);
|
||||
|
||||
impl<'a> Ident<'a> {
|
||||
/// Parses a new identifier from a string.
|
||||
///
|
||||
/// An error is returned containing the input string if it is not a valid
|
||||
/// resource identifier.
|
||||
pub fn new(string: impl Into<Cow<'a, str>>) -> Result<Ident<'a>, IdentParseError<'a>> {
|
||||
#![allow(bindings_with_variant_name)]
|
||||
|
||||
let cow = match string.into() {
|
||||
Cow::Borrowed(s) => {
|
||||
Cow::Borrowed(s.as_ascii_str().map_err(|_| IdentParseError(s.into()))?)
|
||||
}
|
||||
Cow::Owned(s) => Cow::Owned(
|
||||
s.into_ascii_string()
|
||||
.map_err(|e| IdentParseError(e.into_source().into()))?,
|
||||
),
|
||||
};
|
||||
|
||||
let str = cow.as_ref();
|
||||
|
||||
let check_namespace = |s: &AsciiStr| {
|
||||
impl<S: Borrow<str>> Ident<S> {
|
||||
pub fn new(string: S) -> Result<Self, IdentError<S>> {
|
||||
let check_namespace = |s: &str| {
|
||||
!s.is_empty()
|
||||
&& s.chars()
|
||||
.all(|c| matches!(c.as_char(), 'a'..='z' | '0'..='9' | '_' | '-'))
|
||||
.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '-'))
|
||||
};
|
||||
let check_path = |s: &AsciiStr| {
|
||||
let check_path = |s: &str| {
|
||||
!s.is_empty()
|
||||
&& s.chars()
|
||||
.all(|c| matches!(c.as_char(), 'a'..='z' | '0'..='9' | '_' | '/' | '.' | '-'))
|
||||
.all(|c| matches!(c, 'a'..='z' | '0'..='9' | '_' | '/' | '.' | '-'))
|
||||
};
|
||||
|
||||
match str.chars().position(|c| c == AsciiChar::Colon) {
|
||||
Some(colon_idx)
|
||||
if check_namespace(&str[..colon_idx]) && check_path(&str[colon_idx + 1..]) =>
|
||||
{
|
||||
Ok(Self {
|
||||
string: cow,
|
||||
path_start: colon_idx + 1,
|
||||
})
|
||||
let str = string.borrow();
|
||||
|
||||
match str.split_once(':') {
|
||||
Some((namespace, path)) if check_namespace(namespace) && check_path(path) => {
|
||||
let path_start = namespace.len() + 1;
|
||||
Ok(Self { string, path_start })
|
||||
}
|
||||
None if check_path(str) => Ok(Self {
|
||||
string: cow,
|
||||
string,
|
||||
path_start: 0,
|
||||
}),
|
||||
_ => Err(IdentParseError(ascii_cow_to_str_cow(cow))),
|
||||
_ => Err(IdentError(string)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the namespace part of this resource identifier.
|
||||
///
|
||||
/// If this identifier was constructed from a string without a namespace,
|
||||
/// then "minecraft" is returned.
|
||||
/// If the underlying string does not contain a namespace followed by a
|
||||
/// ':' character, `"minecraft"` is returned.
|
||||
pub fn namespace(&self) -> &str {
|
||||
if self.path_start == 0 {
|
||||
"minecraft"
|
||||
} else {
|
||||
self.string[..self.path_start - 1].as_str()
|
||||
&self.string.borrow()[..self.path_start - 1]
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path part of this resource identifier.
|
||||
pub fn path(&self) -> &str {
|
||||
self.string[self.path_start..].as_str()
|
||||
&self.string.borrow()[self.path_start..]
|
||||
}
|
||||
|
||||
/// Returns the underlying string as a `str`.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.string.as_str()
|
||||
self.string.borrow()
|
||||
}
|
||||
|
||||
/// Borrows the underlying string and returns it as an `Ident`. This
|
||||
/// operation is infallible and no checks need to be performed.
|
||||
pub fn as_str_ident(&self) -> Ident<&str> {
|
||||
Ident {
|
||||
string: self.string.borrow(),
|
||||
path_start: self.path_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the underlying string to its owned representation and returns
|
||||
/// it as an `Ident`. This operation is infallible and no checks need to be
|
||||
/// performed.
|
||||
pub fn to_owned_ident(&self) -> Ident<S::Owned>
|
||||
where
|
||||
S: ToOwned,
|
||||
S::Owned: Borrow<str>,
|
||||
{
|
||||
Ident {
|
||||
string: self.string.to_owned(),
|
||||
path_start: self.path_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the identifier and returns the underlying string.
|
||||
pub fn into_inner(self) -> Cow<'a, str> {
|
||||
ascii_cow_to_str_cow(self.string)
|
||||
pub fn into_inner(self) -> S {
|
||||
self.string
|
||||
}
|
||||
}
|
||||
|
||||
/// Used as the argument to `#[serde(deserialize_with = "...")]` when you
|
||||
/// don't want to borrow data from the `'de` lifetime.
|
||||
pub fn deserialize_to_owned<'de, D>(deserializer: D) -> Result<Ident<'static>, D::Error>
|
||||
/// The error type created when an [`Ident`] cannot be parsed from a
|
||||
/// string. Contains the offending string.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct IdentError<S>(pub S);
|
||||
|
||||
impl<S> fmt::Debug for IdentError<S>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
S: Borrow<str>,
|
||||
{
|
||||
Ident::new(String::deserialize(deserializer)?).map_err(de::Error::custom)
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_tuple("IdentError").field(&self.0.borrow()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn ascii_cow_to_str_cow(cow: Cow<AsciiStr>) -> Cow<str> {
|
||||
match cow {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(s.as_str()),
|
||||
Cow::Owned(s) => Cow::Owned(s.into()),
|
||||
impl<S> fmt::Display for IdentError<S>
|
||||
where
|
||||
S: Borrow<str>,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "invalid resource identifier \"{}\"", self.0.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Ident<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Ident").field(&self.as_str()).finish()
|
||||
}
|
||||
}
|
||||
impl<S> Error for IdentError<S> where S: Borrow<str> {}
|
||||
|
||||
impl<'a> FromStr for Ident<'a> {
|
||||
type Err = IdentParseError<'a>;
|
||||
impl FromStr for Ident<String> {
|
||||
type Err = IdentError<String>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ident::new(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<'a>> for String {
|
||||
fn from(id: Ident) -> Self {
|
||||
id.string.into_owned().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<'a>> for Cow<'a, str> {
|
||||
fn from(id: Ident<'a>) -> Self {
|
||||
id.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<str> for Ident<'a> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> PartialEq<Ident<'b>> for Ident<'a> {
|
||||
fn eq(&self, other: &Ident<'b>) -> bool {
|
||||
(self.namespace(), self.path()) == (other.namespace(), other.path())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> PartialOrd<Ident<'b>> for Ident<'a> {
|
||||
fn partial_cmp(&self, other: &Ident<'b>) -> Option<Ordering> {
|
||||
(self.namespace(), self.path()).partial_cmp(&(other.namespace(), other.path()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Hash for Ident<'a> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.namespace().hash(state);
|
||||
self.path().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Ident<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.namespace(), self.path())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<String> for Ident<'a> {
|
||||
type Error = IdentParseError<'a>;
|
||||
impl TryFrom<String> for Ident<String> {
|
||||
type Error = IdentError<String>;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
Ident::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Ident<'a> {
|
||||
type Error = IdentParseError<'a>;
|
||||
|
||||
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||
Ident::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ident<'a>> for nbt::Value {
|
||||
fn from(id: Ident<'a>) -> Self {
|
||||
String::from(id).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for Ident<'a> {
|
||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||
self.as_str().encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decode for Ident<'a> {
|
||||
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
|
||||
Ok(Ident::new(String::decode(r)?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Serialize for Ident<'a> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.as_str().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// This uses borrowed data from the `'de` lifetime. If you just want owned
|
||||
/// data, see [`Ident::deserialize_to_owned`].
|
||||
impl<'de> Deserialize<'de> for Ident<'de> {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
deserializer.deserialize_string(IdentVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for IdentVisitor {
|
||||
type Value = Ident<'de>;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "a valid Minecraft resource identifier")
|
||||
}
|
||||
|
||||
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
|
||||
Ident::from_str(s).map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
||||
impl<S> From<Ident<S>> for String
|
||||
where
|
||||
E: de::Error,
|
||||
S: Into<String> + Borrow<str>,
|
||||
{
|
||||
Ident::new(v).map_err(E::custom)
|
||||
fn from(id: Ident<S>) -> Self {
|
||||
if id.path_start == 0 {
|
||||
format!("minecraft:{}", id.string.borrow())
|
||||
} else {
|
||||
id.string.into()
|
||||
}
|
||||
|
||||
fn visit_string<E: de::Error>(self, s: String) -> Result<Self::Value, E> {
|
||||
Ident::new(s).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience macro for constructing an [`Ident`] from a format string.
|
||||
impl<S: Borrow<str>> fmt::Display for Ident<S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.namespace(), self.path())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> PartialEq<Ident<T>> for Ident<S>
|
||||
where
|
||||
S: Borrow<str>,
|
||||
T: Borrow<str>,
|
||||
{
|
||||
fn eq(&self, other: &Ident<T>) -> bool {
|
||||
self.namespace() == other.namespace() && self.path() == other.path()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Eq for Ident<S> where S: Borrow<str> {}
|
||||
|
||||
impl<S, T> PartialOrd<Ident<T>> for Ident<S>
|
||||
where
|
||||
S: Borrow<str>,
|
||||
T: Borrow<str>,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Ident<T>) -> Option<Ordering> {
|
||||
(self.namespace(), self.path()).partial_cmp(&(other.namespace(), other.path()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Ord for Ident<S>
|
||||
where
|
||||
S: Borrow<str>,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(self.namespace(), self.path()).cmp(&(other.namespace(), other.path()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Hash for Ident<S>
|
||||
where
|
||||
S: Borrow<str>,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.namespace(), self.path()).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Serialize for Ident<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.string.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Ident<T>
|
||||
where
|
||||
T: Deserialize<'de> + Borrow<str>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ident::new(T::deserialize(deserializer)?).map_err(D::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Encode> Encode for Ident<S> {
|
||||
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
||||
self.string.encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Decode for Ident<S>
|
||||
where
|
||||
S: Decode + Borrow<str> + Send + Sync + 'static,
|
||||
{
|
||||
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
|
||||
Ok(Ident::new(S::decode(r)?)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<Ident<S>> for nbt::Value
|
||||
where
|
||||
S: Into<nbt::Value>,
|
||||
{
|
||||
fn from(id: Ident<S>) -> Self {
|
||||
id.string.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience macro for constructing an [`Ident<String>`] from a format
|
||||
/// string.
|
||||
///
|
||||
/// The arguments to this macro are forwarded to [`std::format_args`].
|
||||
/// The arguments to this macro are forwarded to [`std::format`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The macro will cause a panic if the formatted string is not a valid
|
||||
/// The macro will cause a panic if the formatted string is not a valid resource
|
||||
/// identifier. See [`Ident`] for more information.
|
||||
///
|
||||
/// [`Ident<String>`]: [Ident]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use valence::ident;
|
||||
///
|
||||
/// let namespace = "my_namespace";
|
||||
/// let path = ident!("{namespace}:my_path");
|
||||
/// let path = "my_path";
|
||||
///
|
||||
/// assert_eq!(path.namespace(), "my_namespace");
|
||||
/// assert_eq!(path.path(), "my_path");
|
||||
/// let id = ident!("{namespace}:{path}");
|
||||
///
|
||||
/// assert_eq!(id.namespace(), "my_namespace");
|
||||
/// assert_eq!(id.path(), "my_path");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! ident {
|
||||
($($arg:tt)*) => {{
|
||||
let errmsg = "invalid resource identifier in `ident` macro";
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
(|args: ::std::fmt::Arguments| match args.as_str() {
|
||||
Some(s) => $crate::ident::Ident::new(s).expect(errmsg),
|
||||
None => $crate::ident::Ident::new(args.to_string()).expect(errmsg),
|
||||
})(format_args!($($arg)*))
|
||||
$crate::ident::Ident::new(::std::format!($($arg)*)).unwrap()
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -346,26 +346,4 @@ mod tests {
|
|||
|
||||
assert_eq!(h1.finish(), h2.finish());
|
||||
}
|
||||
|
||||
fn check_borrowed(id: Ident) {
|
||||
if let Cow::Owned(_) = id.into_inner() {
|
||||
panic!("not borrowed!");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn literal_is_borrowed() {
|
||||
check_borrowed(ident!("akjghsjkhebf"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visit_borrowed_str_borrows() {
|
||||
let data = String::from("valence:frobnicator");
|
||||
|
||||
check_borrowed(
|
||||
IdentVisitor
|
||||
.visit_borrowed_str::<de::value::Error>(data.as_ref())
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ pub trait Decode: Sized {
|
|||
}
|
||||
|
||||
/// The maximum number of bytes in a single packet.
|
||||
pub const MAX_PACKET_SIZE: i32 = 2097151;
|
||||
pub const MAX_PACKET_SIZE: i32 = 2097152;
|
||||
|
||||
impl Encode for () {
|
||||
fn encode(&self, _w: &mut impl Write) -> anyhow::Result<()> {
|
||||
|
|
|
@ -281,7 +281,7 @@ pub mod play {
|
|||
|
||||
def_struct! {
|
||||
PluginMessageC2s {
|
||||
channel: Ident<'static>,
|
||||
channel: Ident<String>,
|
||||
data: RawBytes,
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ pub mod play {
|
|||
def_struct! {
|
||||
PlaceRecipe {
|
||||
window_id: i8,
|
||||
recipe: Ident<'static>,
|
||||
recipe: Ident<String>,
|
||||
make_all: bool,
|
||||
}
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ pub mod play {
|
|||
|
||||
def_struct! {
|
||||
SetSeenRecipe {
|
||||
recipe_id: Ident<'static>,
|
||||
recipe_id: Ident<String>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -542,7 +542,7 @@ pub mod play {
|
|||
|
||||
def_enum! {
|
||||
SeenAdvancements: VarInt {
|
||||
OpenedTab: Ident<'static> = 0,
|
||||
OpenedTab: Ident<String> = 0,
|
||||
ClosedScreen = 1,
|
||||
}
|
||||
}
|
||||
|
@ -610,9 +610,9 @@ pub mod play {
|
|||
def_struct! {
|
||||
ProgramJigsawBlock {
|
||||
location: BlockPos,
|
||||
name: Ident<'static>,
|
||||
target: Ident<'static>,
|
||||
pool: Ident<'static>,
|
||||
name: Ident<String>,
|
||||
target: Ident<String>,
|
||||
pool: Ident<String>,
|
||||
final_state: String,
|
||||
joint_type: String,
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ pub mod login {
|
|||
def_struct! {
|
||||
LoginPluginRequest {
|
||||
message_id: VarInt,
|
||||
channel: Ident<'static>,
|
||||
channel: Ident<String>,
|
||||
data: RawBytes,
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ pub mod play {
|
|||
|
||||
def_struct! {
|
||||
CustomSoundEffect {
|
||||
name: Ident<'static>,
|
||||
name: Ident<String>,
|
||||
category: SoundCategory,
|
||||
position: Vec3<i32>,
|
||||
volume: f32,
|
||||
|
@ -388,13 +388,13 @@ pub mod play {
|
|||
is_hardcore: bool,
|
||||
gamemode: GameMode,
|
||||
previous_gamemode: GameMode,
|
||||
dimension_names: Vec<Ident<'static>>,
|
||||
dimension_names: Vec<Ident<String>>,
|
||||
/// Contains information about dimensions, biomes, and chats.
|
||||
registry_codec: Compound,
|
||||
/// The name of the dimension type being spawned into.
|
||||
dimension_type_name: Ident<'static>,
|
||||
dimension_type_name: Ident<String>,
|
||||
/// The name of the dimension being spawned into.
|
||||
dimension_name: Ident<'static>,
|
||||
dimension_name: Ident<String>,
|
||||
/// Hash of the world's seed used for client biome noise.
|
||||
hashed_seed: i64,
|
||||
/// No longer used by the client.
|
||||
|
@ -409,7 +409,7 @@ pub mod play {
|
|||
/// If this is a superflat world.
|
||||
/// Superflat worlds have different void fog and horizon levels.
|
||||
is_flat: bool,
|
||||
last_death_location: Option<(Ident<'static>, BlockPos)>,
|
||||
last_death_location: Option<(Ident<String>, BlockPos)>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,15 +529,15 @@ pub mod play {
|
|||
|
||||
def_struct! {
|
||||
Respawn {
|
||||
dimension_type_name: Ident<'static>,
|
||||
dimension_name: Ident<'static>,
|
||||
dimension_type_name: Ident<String>,
|
||||
dimension_name: Ident<String>,
|
||||
hashed_seed: u64,
|
||||
game_mode: GameMode,
|
||||
previous_game_mode: GameMode,
|
||||
is_debug: bool,
|
||||
is_flat: bool,
|
||||
copy_metadata: bool,
|
||||
last_death_location: Option<(Ident<'static>, BlockPos)>,
|
||||
last_death_location: Option<(Ident<String>, BlockPos)>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -708,7 +708,7 @@ pub mod play {
|
|||
|
||||
def_struct! {
|
||||
EntityAttributesProperty {
|
||||
key: Ident<'static>,
|
||||
key: Ident<String>,
|
||||
value: f64,
|
||||
modifiers: Vec<EntityAttributesModifiers>
|
||||
}
|
||||
|
|
|
@ -378,15 +378,14 @@ enum ClickEvent {
|
|||
enum HoverEvent {
|
||||
ShowText(Box<Text>),
|
||||
ShowItem {
|
||||
#[serde(deserialize_with = "Ident::deserialize_to_owned")]
|
||||
id: Ident<'static>,
|
||||
id: Ident<String>,
|
||||
count: Option<i32>,
|
||||
// TODO: tag
|
||||
},
|
||||
ShowEntity {
|
||||
name: Box<Text>,
|
||||
#[serde(rename = "type", deserialize_with = "Ident::deserialize_to_owned")]
|
||||
kind: Ident<'static>,
|
||||
#[serde(rename = "type")]
|
||||
kind: Ident<String>,
|
||||
// TODO: id (hyphenated entity UUID as a string)
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue