mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 23:44:40 +00:00
random polish
This commit is contained in:
parent
fe2136ce11
commit
1526a21276
7 changed files with 296 additions and 145 deletions
|
@ -12,16 +12,20 @@ use crate::{
|
|||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use super::{list::ListTag, Tag};
|
||||
use super::{list::ListTag, NbtTag};
|
||||
|
||||
/// A list of named tags. The order of the tags is preserved.
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct CompoundTag<'a> {
|
||||
values: Vec<(&'a Mutf8Str, Tag<'a>)>,
|
||||
pub struct NbtCompound<'a> {
|
||||
values: Vec<(&'a Mutf8Str, NbtTag<'a>)>,
|
||||
}
|
||||
|
||||
impl<'a> CompoundTag<'a> {
|
||||
pub fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
|
||||
impl<'a> NbtCompound<'a> {
|
||||
pub fn read(data: &mut Cursor<&'a [u8]>) -> Result<Self, Error> {
|
||||
Self::read_with_depth(data, 0)
|
||||
}
|
||||
|
||||
pub fn read_with_depth(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
|
@ -36,38 +40,39 @@ impl<'a> CompoundTag<'a> {
|
|||
match tag_type {
|
||||
BYTE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
SHORT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
INT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
LONG_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
FLOAT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
DOUBLE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
BYTE_ARRAY_ID => {
|
||||
values.push((tag_name, Tag::ByteArray(read_with_u32_length(data, 1)?)))
|
||||
values.push((tag_name, NbtTag::ByteArray(read_with_u32_length(data, 1)?)))
|
||||
}
|
||||
STRING_ID => values.push((tag_name, Tag::String(read_string(data)?))),
|
||||
LIST_ID => values.push((tag_name, Tag::List(ListTag::new(data, depth + 1)?))),
|
||||
COMPOUND_ID => {
|
||||
values.push((tag_name, Tag::Compound(CompoundTag::new(data, depth + 1)?)))
|
||||
}
|
||||
INT_ARRAY_ID => values.push((tag_name, Tag::IntArray(read_int_array(data)?))),
|
||||
LONG_ARRAY_ID => values.push((tag_name, Tag::LongArray(read_long_array(data)?))),
|
||||
STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?))),
|
||||
LIST_ID => values.push((tag_name, NbtTag::List(ListTag::read(data, depth + 1)?))),
|
||||
COMPOUND_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?),
|
||||
)),
|
||||
INT_ARRAY_ID => values.push((tag_name, NbtTag::IntArray(read_int_array(data)?))),
|
||||
LONG_ARRAY_ID => values.push((tag_name, NbtTag::LongArray(read_long_array(data)?))),
|
||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
||||
}
|
||||
}
|
||||
|
@ -85,46 +90,46 @@ impl<'a> CompoundTag<'a> {
|
|||
unchecked_write_string(data, name);
|
||||
}
|
||||
match tag {
|
||||
Tag::Byte(byte) => unsafe {
|
||||
NbtTag::Byte(byte) => unsafe {
|
||||
unchecked_push(data, *byte as u8);
|
||||
},
|
||||
Tag::Short(short) => unsafe {
|
||||
NbtTag::Short(short) => unsafe {
|
||||
unchecked_extend(data, &short.to_be_bytes());
|
||||
},
|
||||
Tag::Int(int) => unsafe {
|
||||
NbtTag::Int(int) => unsafe {
|
||||
unchecked_extend(data, &int.to_be_bytes());
|
||||
},
|
||||
Tag::Long(long) => {
|
||||
NbtTag::Long(long) => {
|
||||
data.extend_from_slice(&long.to_be_bytes());
|
||||
}
|
||||
Tag::Float(float) => unsafe {
|
||||
NbtTag::Float(float) => unsafe {
|
||||
unchecked_extend(data, &float.to_be_bytes());
|
||||
},
|
||||
Tag::Double(double) => {
|
||||
NbtTag::Double(double) => {
|
||||
data.extend_from_slice(&double.to_be_bytes());
|
||||
}
|
||||
Tag::ByteArray(byte_array) => {
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
Tag::String(string) => {
|
||||
NbtTag::String(string) => {
|
||||
write_string(data, string);
|
||||
}
|
||||
Tag::List(list) => {
|
||||
NbtTag::List(list) => {
|
||||
list.write(data);
|
||||
}
|
||||
Tag::Compound(compound) => {
|
||||
NbtTag::Compound(compound) => {
|
||||
compound.write(data);
|
||||
}
|
||||
Tag::IntArray(int_array) => {
|
||||
NbtTag::IntArray(int_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&int_array.as_big_endian());
|
||||
}
|
||||
Tag::LongArray(long_array) => {
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
|
@ -136,7 +141,7 @@ impl<'a> CompoundTag<'a> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, name: &str) -> Option<&Tag<'a>> {
|
||||
pub fn get(&self, name: &str) -> Option<&NbtTag<'a>> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, value) in &self.values {
|
||||
|
@ -186,7 +191,7 @@ impl<'a> CompoundTag<'a> {
|
|||
pub fn list(&self, name: &str) -> Option<&ListTag<'a>> {
|
||||
self.get(name).and_then(|tag| tag.list())
|
||||
}
|
||||
pub fn compound(&self, name: &str) -> Option<&CompoundTag<'a>> {
|
||||
pub fn compound(&self, name: &str) -> Option<&NbtCompound<'a>> {
|
||||
self.get(name).and_then(|tag| tag.compound())
|
||||
}
|
||||
pub fn int_array(&self, name: &str) -> Option<Vec<i32>> {
|
||||
|
@ -196,7 +201,7 @@ impl<'a> CompoundTag<'a> {
|
|||
self.get(name).and_then(|tag| tag.long_array())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &Tag<'a>)> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag<'a>)> {
|
||||
self.values.iter().map(|(k, v)| (*k, v))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use super::{read_u32, CompoundTag, MAX_DEPTH};
|
||||
use super::{read_u32, NbtCompound, MAX_DEPTH};
|
||||
|
||||
/// A list of NBT tags of a single type.
|
||||
#[repr(u8)]
|
||||
|
@ -30,12 +30,12 @@ pub enum ListTag<'a> {
|
|||
ByteArray(Vec<&'a [u8]>) = BYTE_ARRAY_ID,
|
||||
String(Vec<&'a Mutf8Str>) = STRING_ID,
|
||||
List(Vec<ListTag<'a>>) = LIST_ID,
|
||||
Compound(Vec<CompoundTag<'a>>) = COMPOUND_ID,
|
||||
Compound(Vec<NbtCompound<'a>>) = COMPOUND_ID,
|
||||
IntArray(Vec<RawList<'a, i32>>) = INT_ARRAY_ID,
|
||||
LongArray(Vec<RawList<'a, i64>>) = LONG_ARRAY_ID,
|
||||
}
|
||||
impl<'a> ListTag<'a> {
|
||||
pub fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
|
||||
pub fn read(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ impl<'a> ListTag<'a> {
|
|||
// arbitrary number to prevent big allocations
|
||||
let mut lists = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
lists.push(ListTag::new(data, depth + 1)?)
|
||||
lists.push(ListTag::read(data, depth + 1)?)
|
||||
}
|
||||
lists
|
||||
}),
|
||||
|
@ -83,7 +83,7 @@ impl<'a> ListTag<'a> {
|
|||
// arbitrary number to prevent big allocations
|
||||
let mut compounds = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
compounds.push(CompoundTag::new(data, depth + 1)?)
|
||||
compounds.push(NbtCompound::read_with_depth(data, depth + 1)?)
|
||||
}
|
||||
compounds
|
||||
}),
|
||||
|
@ -247,7 +247,7 @@ impl<'a> ListTag<'a> {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compounds(&self) -> Option<&[CompoundTag]> {
|
||||
pub fn compounds(&self) -> Option<&[NbtCompound]> {
|
||||
match self {
|
||||
ListTag::Compound(compounds) => Some(compounds),
|
||||
_ => None,
|
||||
|
|
|
@ -17,13 +17,13 @@ use crate::{
|
|||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
pub use self::{compound::CompoundTag, list::ListTag};
|
||||
pub use self::{compound::NbtCompound, list::ListTag};
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BaseNbt<'a> {
|
||||
name: &'a Mutf8Str,
|
||||
tag: CompoundTag<'a>,
|
||||
tag: NbtCompound<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -43,7 +43,7 @@ impl<'a> Nbt<'a> {
|
|||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let name = read_string(data)?;
|
||||
let tag = CompoundTag::new(data, 0)?;
|
||||
let tag = NbtCompound::read_with_depth(data, 0)?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt { name, tag }))
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ impl<'a> BaseNbt<'a> {
|
|||
}
|
||||
}
|
||||
impl<'a> Deref for BaseNbt<'a> {
|
||||
type Target = CompoundTag<'a>;
|
||||
type Target = NbtCompound<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
|
@ -102,7 +102,7 @@ impl<'a> BaseNbt<'a> {
|
|||
/// A single NBT tag.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Tag<'a> {
|
||||
pub enum NbtTag<'a> {
|
||||
Byte(i8) = BYTE_ID,
|
||||
Short(i16) = SHORT_ID,
|
||||
Int(i32) = INT_ID,
|
||||
|
@ -112,11 +112,11 @@ pub enum Tag<'a> {
|
|||
ByteArray(&'a [u8]) = BYTE_ARRAY_ID,
|
||||
String(&'a Mutf8Str) = STRING_ID,
|
||||
List(ListTag<'a>) = LIST_ID,
|
||||
Compound(CompoundTag<'a>) = COMPOUND_ID,
|
||||
Compound(NbtCompound<'a>) = COMPOUND_ID,
|
||||
IntArray(RawList<'a, i32>) = INT_ARRAY_ID,
|
||||
LongArray(RawList<'a, i64>) = LONG_ARRAY_ID,
|
||||
}
|
||||
impl<'a> Tag<'a> {
|
||||
impl<'a> NbtTag<'a> {
|
||||
/// Get the numerical ID of the tag type.
|
||||
#[inline]
|
||||
pub fn id(&self) -> u8 {
|
||||
|
@ -129,73 +129,73 @@ impl<'a> Tag<'a> {
|
|||
|
||||
pub fn byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
Tag::Byte(byte) => Some(*byte),
|
||||
NbtTag::Byte(byte) => Some(*byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn short(&self) -> Option<i16> {
|
||||
match self {
|
||||
Tag::Short(short) => Some(*short),
|
||||
NbtTag::Short(short) => Some(*short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int(&self) -> Option<i32> {
|
||||
match self {
|
||||
Tag::Int(int) => Some(*int),
|
||||
NbtTag::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long(&self) -> Option<i64> {
|
||||
match self {
|
||||
Tag::Long(long) => Some(*long),
|
||||
NbtTag::Long(long) => Some(*long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn float(&self) -> Option<f32> {
|
||||
match self {
|
||||
Tag::Float(float) => Some(*float),
|
||||
NbtTag::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn double(&self) -> Option<f64> {
|
||||
match self {
|
||||
Tag::Double(double) => Some(*double),
|
||||
NbtTag::Double(double) => Some(*double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_array(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
Tag::ByteArray(byte_array) => Some(byte_array),
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn string(&self) -> Option<&Mutf8Str> {
|
||||
match self {
|
||||
Tag::String(string) => Some(string),
|
||||
NbtTag::String(string) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn list(&self) -> Option<&ListTag<'a>> {
|
||||
match self {
|
||||
Tag::List(list) => Some(list),
|
||||
NbtTag::List(list) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound(&self) -> Option<&CompoundTag<'a>> {
|
||||
pub fn compound(&self) -> Option<&NbtCompound<'a>> {
|
||||
match self {
|
||||
Tag::Compound(compound) => Some(compound),
|
||||
NbtTag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_array(&self) -> Option<Vec<i32>> {
|
||||
match self {
|
||||
Tag::IntArray(int_array) => Some(int_array.to_vec()),
|
||||
NbtTag::IntArray(int_array) => Some(int_array.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_array(&self) -> Option<Vec<i64>> {
|
||||
match self {
|
||||
Tag::LongArray(long_array) => Some(long_array.to_vec()),
|
||||
NbtTag::LongArray(long_array) => Some(long_array.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
19
src/mutf8.rs
19
src/mutf8.rs
|
@ -15,7 +15,7 @@ pub struct Mutf8Str {
|
|||
/// An owned M-UTF8 string.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Default)]
|
||||
pub struct Mutf8String {
|
||||
vec: Vec<u8>,
|
||||
pub(crate) vec: Vec<u8>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -168,6 +168,16 @@ impl Mutf8String {
|
|||
pub fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<u8>) -> Mutf8String {
|
||||
Self { vec }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_string(s: String) -> Mutf8String {
|
||||
Self::from_vec(mutf8::encode(&s).into_owned())
|
||||
}
|
||||
}
|
||||
impl Deref for Mutf8String {
|
||||
type Target = Mutf8Str;
|
||||
|
@ -178,7 +188,12 @@ impl Deref for Mutf8String {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: make Mutf8 correct
|
||||
impl From<String> for Mutf8String {
|
||||
#[inline]
|
||||
fn from(s: String) -> Self {
|
||||
Self::from_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
@ -13,16 +13,24 @@ use crate::{
|
|||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use super::{list::ListTag, Tag};
|
||||
use super::{list::ListTag, NbtTag};
|
||||
|
||||
/// A list of named tags. The order of the tags is preserved.
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct CompoundTag {
|
||||
values: Vec<(Mutf8String, Tag)>,
|
||||
pub struct NbtCompound {
|
||||
pub(crate) values: Vec<(Mutf8String, NbtTag)>,
|
||||
}
|
||||
|
||||
impl CompoundTag {
|
||||
pub fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
impl NbtCompound {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
|
||||
Self::read_with_depth(data, 0)
|
||||
}
|
||||
|
||||
pub fn read_with_depth(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
|
@ -37,42 +45,43 @@ impl CompoundTag {
|
|||
match tag_type {
|
||||
BYTE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
SHORT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
INT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
LONG_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
FLOAT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
DOUBLE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
NbtTag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
BYTE_ARRAY_ID => values.push((
|
||||
tag_name,
|
||||
Tag::ByteArray(read_with_u32_length(data, 1)?.to_owned()),
|
||||
NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned()),
|
||||
)),
|
||||
STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?.to_owned()))),
|
||||
LIST_ID => values.push((tag_name, NbtTag::List(ListTag::read(data, depth + 1)?))),
|
||||
COMPOUND_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?),
|
||||
)),
|
||||
STRING_ID => values.push((tag_name, Tag::String(read_string(data)?.to_owned()))),
|
||||
LIST_ID => values.push((tag_name, Tag::List(ListTag::new(data, depth + 1)?))),
|
||||
COMPOUND_ID => {
|
||||
values.push((tag_name, Tag::Compound(CompoundTag::new(data, depth + 1)?)))
|
||||
}
|
||||
INT_ARRAY_ID => {
|
||||
values.push((tag_name, Tag::IntArray(read_int_array(data)?.to_vec())))
|
||||
values.push((tag_name, NbtTag::IntArray(read_int_array(data)?.to_vec())))
|
||||
}
|
||||
LONG_ARRAY_ID => {
|
||||
values.push((tag_name, Tag::LongArray(read_long_array(data)?.to_vec())))
|
||||
values.push((tag_name, NbtTag::LongArray(read_long_array(data)?.to_vec())))
|
||||
}
|
||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
||||
}
|
||||
|
@ -91,46 +100,46 @@ impl CompoundTag {
|
|||
unchecked_write_string(data, name);
|
||||
}
|
||||
match tag {
|
||||
Tag::Byte(byte) => unsafe {
|
||||
NbtTag::Byte(byte) => unsafe {
|
||||
unchecked_push(data, *byte as u8);
|
||||
},
|
||||
Tag::Short(short) => unsafe {
|
||||
NbtTag::Short(short) => unsafe {
|
||||
unchecked_extend(data, &short.to_be_bytes());
|
||||
},
|
||||
Tag::Int(int) => unsafe {
|
||||
NbtTag::Int(int) => unsafe {
|
||||
unchecked_extend(data, &int.to_be_bytes());
|
||||
},
|
||||
Tag::Long(long) => {
|
||||
NbtTag::Long(long) => {
|
||||
data.extend_from_slice(&long.to_be_bytes());
|
||||
}
|
||||
Tag::Float(float) => unsafe {
|
||||
NbtTag::Float(float) => unsafe {
|
||||
unchecked_extend(data, &float.to_be_bytes());
|
||||
},
|
||||
Tag::Double(double) => {
|
||||
NbtTag::Double(double) => {
|
||||
data.extend_from_slice(&double.to_be_bytes());
|
||||
}
|
||||
Tag::ByteArray(byte_array) => {
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
Tag::String(string) => {
|
||||
NbtTag::String(string) => {
|
||||
write_string(data, string);
|
||||
}
|
||||
Tag::List(list) => {
|
||||
NbtTag::List(list) => {
|
||||
list.write(data);
|
||||
}
|
||||
Tag::Compound(compound) => {
|
||||
NbtTag::Compound(compound) => {
|
||||
compound.write(data);
|
||||
}
|
||||
Tag::IntArray(int_array) => {
|
||||
NbtTag::IntArray(int_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
|
||||
}
|
||||
Tag::LongArray(long_array) => {
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
|
@ -142,7 +151,7 @@ impl CompoundTag {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, name: &str) -> Option<&Tag> {
|
||||
pub fn get(&self, name: &str) -> Option<&NbtTag> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, value) in &self.values {
|
||||
|
@ -154,7 +163,7 @@ impl CompoundTag {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut Tag> {
|
||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut NbtTag> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, value) in &mut self.values {
|
||||
|
@ -231,10 +240,10 @@ impl CompoundTag {
|
|||
pub fn list_mut(&mut self, name: &str) -> Option<&mut ListTag> {
|
||||
self.get_mut(name).and_then(|tag| tag.list_mut())
|
||||
}
|
||||
pub fn compound(&self, name: &str) -> Option<&CompoundTag> {
|
||||
pub fn compound(&self, name: &str) -> Option<&NbtCompound> {
|
||||
self.get(name).and_then(|tag| tag.compound())
|
||||
}
|
||||
pub fn compound_mut(&mut self, name: &str) -> Option<&mut CompoundTag> {
|
||||
pub fn compound_mut(&mut self, name: &str) -> Option<&mut NbtCompound> {
|
||||
self.get_mut(name).and_then(|tag| tag.compound_mut())
|
||||
}
|
||||
pub fn int_array(&self, name: &str) -> Option<&[i32]> {
|
||||
|
@ -250,10 +259,10 @@ impl CompoundTag {
|
|||
self.get_mut(name).and_then(|tag| tag.long_array_mut())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &Tag)> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag)> {
|
||||
self.values.iter().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Mutf8Str, &mut Tag)> {
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Mutf8Str, &mut NbtTag)> {
|
||||
self.values.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
|
@ -268,22 +277,23 @@ impl CompoundTag {
|
|||
pub fn keys_mut(&mut self) -> impl Iterator<Item = &mut Mutf8String> {
|
||||
self.values.iter_mut().map(|(k, _)| k)
|
||||
}
|
||||
pub fn values(&self) -> impl Iterator<Item = &Tag> {
|
||||
pub fn values(&self) -> impl Iterator<Item = &NbtTag> {
|
||||
self.values.iter().map(|(_, v)| v)
|
||||
}
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Tag> {
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut NbtTag> {
|
||||
self.values.iter_mut().map(|(_, v)| v)
|
||||
}
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, Tag)> {
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
self.values.into_iter()
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
pub fn insert(&mut self, name: Mutf8String, tag: Tag) {
|
||||
pub fn insert(&mut self, name: impl Into<Mutf8String>, tag: NbtTag) {
|
||||
let name = name.into();
|
||||
self.values.push((name, tag));
|
||||
}
|
||||
pub fn remove(&mut self, name: &str) -> Option<Tag> {
|
||||
pub fn remove(&mut self, name: &str) -> Option<NbtTag> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for i in 0..self.values.len() {
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
Error,
|
||||
};
|
||||
|
||||
use super::{compound::CompoundTag, read_u32, MAX_DEPTH};
|
||||
use super::{compound::NbtCompound, read_u32, MAX_DEPTH};
|
||||
|
||||
/// A list of NBT tags of a single type.
|
||||
#[repr(u8)]
|
||||
|
@ -32,12 +32,12 @@ pub enum ListTag {
|
|||
ByteArray(Vec<Vec<u8>>) = BYTE_ARRAY_ID,
|
||||
String(Vec<Mutf8String>) = STRING_ID,
|
||||
List(Vec<ListTag>) = LIST_ID,
|
||||
Compound(Vec<CompoundTag>) = COMPOUND_ID,
|
||||
Compound(Vec<NbtCompound>) = COMPOUND_ID,
|
||||
IntArray(Vec<Vec<i32>>) = INT_ARRAY_ID,
|
||||
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
|
||||
}
|
||||
impl ListTag {
|
||||
pub fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
pub fn read(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ impl ListTag {
|
|||
// arbitrary number to prevent big allocations
|
||||
let mut lists = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
lists.push(ListTag::new(data, depth + 1)?)
|
||||
lists.push(ListTag::read(data, depth + 1)?)
|
||||
}
|
||||
lists
|
||||
}),
|
||||
|
@ -85,7 +85,7 @@ impl ListTag {
|
|||
// arbitrary number to prevent big allocations
|
||||
let mut compounds = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
compounds.push(CompoundTag::new(data, depth + 1)?)
|
||||
compounds.push(NbtCompound::read_with_depth(data, depth + 1)?)
|
||||
}
|
||||
compounds
|
||||
}),
|
||||
|
@ -249,7 +249,7 @@ impl ListTag {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compounds(&self) -> Option<&[CompoundTag]> {
|
||||
pub fn compounds(&self) -> Option<&[NbtCompound]> {
|
||||
match self {
|
||||
ListTag::Compound(compounds) => Some(compounds),
|
||||
_ => None,
|
||||
|
|
191
src/owned/mod.rs
191
src/owned/mod.rs
|
@ -17,13 +17,13 @@ use crate::{
|
|||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
pub use self::{compound::CompoundTag, list::ListTag};
|
||||
pub use self::{compound::NbtCompound, list::ListTag};
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct BaseNbt {
|
||||
name: Mutf8String,
|
||||
tag: CompoundTag,
|
||||
tag: NbtCompound,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -33,7 +33,7 @@ pub enum Nbt {
|
|||
}
|
||||
|
||||
impl Nbt {
|
||||
pub fn new(name: Mutf8String, tag: CompoundTag) -> Self {
|
||||
pub fn new(name: Mutf8String, tag: NbtCompound) -> Self {
|
||||
Self::Some(BaseNbt { name, tag })
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ impl Nbt {
|
|||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let name = read_string(data)?.to_owned();
|
||||
let tag = CompoundTag::new(data, 0)?;
|
||||
let tag = NbtCompound::read_with_depth(data, 0)?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt { name, tag }))
|
||||
}
|
||||
|
@ -78,10 +78,44 @@ impl Nbt {
|
|||
pub fn is_none(&self) -> bool {
|
||||
!self.is_some()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag)> {
|
||||
const EMPTY: &'static NbtCompound = &NbtCompound { values: Vec::new() };
|
||||
|
||||
if let Nbt::Some(nbt) = self {
|
||||
nbt.iter()
|
||||
} else {
|
||||
EMPTY.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
const EMPTY: NbtCompound = NbtCompound { values: Vec::new() };
|
||||
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.tag.into_iter(),
|
||||
Nbt::None => EMPTY.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Deref for Nbt {
|
||||
type Target = BaseNbt;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
const EMPTY: &'static BaseNbt = &BaseNbt {
|
||||
name: Mutf8String { vec: Vec::new() },
|
||||
tag: NbtCompound { values: Vec::new() },
|
||||
};
|
||||
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt,
|
||||
Nbt::None => EMPTY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseNbt {
|
||||
pub fn new(name: Mutf8String, tag: CompoundTag) -> Self {
|
||||
pub fn new(name: Mutf8String, tag: NbtCompound) -> Self {
|
||||
Self { name, tag }
|
||||
}
|
||||
|
||||
|
@ -96,9 +130,13 @@ impl BaseNbt {
|
|||
write_string(data, &self.name);
|
||||
self.tag.write(data);
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
self.tag.into_iter()
|
||||
}
|
||||
}
|
||||
impl Deref for BaseNbt {
|
||||
type Target = CompoundTag;
|
||||
type Target = NbtCompound;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
|
@ -108,7 +146,7 @@ impl Deref for BaseNbt {
|
|||
/// A single NBT tag.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Tag {
|
||||
pub enum NbtTag {
|
||||
Byte(i8) = BYTE_ID,
|
||||
Short(i16) = SHORT_ID,
|
||||
Int(i32) = INT_ID,
|
||||
|
@ -118,11 +156,11 @@ pub enum Tag {
|
|||
ByteArray(Vec<u8>) = BYTE_ARRAY_ID,
|
||||
String(Mutf8String) = STRING_ID,
|
||||
List(ListTag) = LIST_ID,
|
||||
Compound(CompoundTag) = COMPOUND_ID,
|
||||
Compound(NbtCompound) = COMPOUND_ID,
|
||||
IntArray(Vec<i32>) = INT_ARRAY_ID,
|
||||
LongArray(Vec<i64>) = LONG_ARRAY_ID,
|
||||
}
|
||||
impl Tag {
|
||||
impl NbtTag {
|
||||
/// Get the numerical ID of the tag type.
|
||||
#[inline]
|
||||
pub fn id(&self) -> u8 {
|
||||
|
@ -135,145 +173,228 @@ impl Tag {
|
|||
|
||||
pub fn byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
Tag::Byte(byte) => Some(*byte),
|
||||
NbtTag::Byte(byte) => Some(*byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_mut(&mut self) -> Option<&mut i8> {
|
||||
match self {
|
||||
Tag::Byte(byte) => Some(byte),
|
||||
NbtTag::Byte(byte) => Some(byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_byte(self) -> Option<i8> {
|
||||
match self {
|
||||
NbtTag::Byte(byte) => Some(byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short(&self) -> Option<i16> {
|
||||
match self {
|
||||
Tag::Short(short) => Some(*short),
|
||||
NbtTag::Short(short) => Some(*short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn short_mut(&mut self) -> Option<&mut i16> {
|
||||
match self {
|
||||
Tag::Short(short) => Some(short),
|
||||
NbtTag::Short(short) => Some(short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_short(self) -> Option<i16> {
|
||||
match self {
|
||||
NbtTag::Short(short) => Some(short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int(&self) -> Option<i32> {
|
||||
match self {
|
||||
Tag::Int(int) => Some(*int),
|
||||
NbtTag::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_mut(&mut self) -> Option<&mut i32> {
|
||||
match self {
|
||||
Tag::Int(int) => Some(int),
|
||||
NbtTag::Int(int) => Some(int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_int(self) -> Option<i32> {
|
||||
match self {
|
||||
NbtTag::Int(int) => Some(int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn long(&self) -> Option<i64> {
|
||||
match self {
|
||||
Tag::Long(long) => Some(*long),
|
||||
NbtTag::Long(long) => Some(*long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_mut(&mut self) -> Option<&mut i64> {
|
||||
match self {
|
||||
Tag::Long(long) => Some(long),
|
||||
NbtTag::Long(long) => Some(long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_long(self) -> Option<i64> {
|
||||
match self {
|
||||
NbtTag::Long(long) => Some(long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float(&self) -> Option<f32> {
|
||||
match self {
|
||||
Tag::Float(float) => Some(*float),
|
||||
NbtTag::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn float_mut(&mut self) -> Option<&mut f32> {
|
||||
match self {
|
||||
Tag::Float(float) => Some(float),
|
||||
NbtTag::Float(float) => Some(float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_float(self) -> Option<f32> {
|
||||
match self {
|
||||
NbtTag::Float(float) => Some(float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn double(&self) -> Option<f64> {
|
||||
match self {
|
||||
Tag::Double(double) => Some(*double),
|
||||
NbtTag::Double(double) => Some(*double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn double_mut(&mut self) -> Option<&mut f64> {
|
||||
match self {
|
||||
Tag::Double(double) => Some(double),
|
||||
NbtTag::Double(double) => Some(double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_double(self) -> Option<f64> {
|
||||
match self {
|
||||
NbtTag::Double(double) => Some(double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn byte_array(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
Tag::ByteArray(byte_array) => Some(byte_array),
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_array_mut(&mut self) -> Option<&mut Vec<u8>> {
|
||||
match self {
|
||||
Tag::ByteArray(byte_array) => Some(byte_array),
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_byte_array(self) -> Option<Vec<u8>> {
|
||||
match self {
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(&self) -> Option<&Mutf8Str> {
|
||||
match self {
|
||||
Tag::String(string) => Some(string),
|
||||
NbtTag::String(string) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn string_mut(&mut self) -> Option<&mut Mutf8String> {
|
||||
match self {
|
||||
Tag::String(string) => Some(string),
|
||||
NbtTag::String(string) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_string(self) -> Option<Mutf8String> {
|
||||
match self {
|
||||
NbtTag::String(string) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Option<&ListTag> {
|
||||
match self {
|
||||
Tag::List(list) => Some(list),
|
||||
NbtTag::List(list) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn list_mut(&mut self) -> Option<&mut ListTag> {
|
||||
match self {
|
||||
Tag::List(list) => Some(list),
|
||||
NbtTag::List(list) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound(&self) -> Option<&CompoundTag> {
|
||||
pub fn into_list(self) -> Option<ListTag> {
|
||||
match self {
|
||||
Tag::Compound(compound) => Some(compound),
|
||||
NbtTag::List(list) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound_mut(&mut self) -> Option<&mut CompoundTag> {
|
||||
|
||||
pub fn compound(&self) -> Option<&NbtCompound> {
|
||||
match self {
|
||||
Tag::Compound(compound) => Some(compound),
|
||||
NbtTag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound_mut(&mut self) -> Option<&mut NbtCompound> {
|
||||
match self {
|
||||
NbtTag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_compound(self) -> Option<NbtCompound> {
|
||||
match self {
|
||||
NbtTag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn int_array(&self) -> Option<&[i32]> {
|
||||
match self {
|
||||
Tag::IntArray(int_array) => Some(int_array),
|
||||
NbtTag::IntArray(int_array) => Some(int_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_array_mut(&mut self) -> Option<&mut Vec<i32>> {
|
||||
match self {
|
||||
Tag::IntArray(int_array) => Some(int_array),
|
||||
NbtTag::IntArray(int_array) => Some(int_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_int_array(self) -> Option<Vec<i32>> {
|
||||
match self {
|
||||
NbtTag::IntArray(int_array) => Some(int_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn long_array(&self) -> Option<&[i64]> {
|
||||
match self {
|
||||
Tag::LongArray(long_array) => Some(long_array),
|
||||
NbtTag::LongArray(long_array) => Some(long_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_array_mut(&mut self) -> Option<&mut Vec<i64>> {
|
||||
match self {
|
||||
Tag::LongArray(long_array) => Some(long_array),
|
||||
NbtTag::LongArray(long_array) => Some(long_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn into_long_array(self) -> Option<Vec<i64>> {
|
||||
match self {
|
||||
NbtTag::LongArray(long_array) => Some(long_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue