use crate::{error::Error, metadata};
use serde::{de::Deserializer, Deserialize};
use serde_aux::field_attributes::deserialize_number_from_string;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Image {
pub source: String,
pub transparent_color: Option<String>,
pub width: u32,
pub height: u32,
}
impl<'de> Deserialize<'de> for Image {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct XMLImage {
pub source: String,
pub trans: Option<String>,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub width: u32,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub height: u32,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum ImageData {
XML {
image: Vec<XMLImage>,
},
JSON {
image: String,
imageheight: u32,
imagewidth: u32,
transparentcolor: Option<String>,
},
}
impl Into<Image> for ImageData {
fn into(self) -> Image {
match self {
ImageData::XML { mut image } => {
let image = image.remove(0);
Image {
source: image.source,
transparent_color: image.trans,
width: image.width,
height: image.height,
}
}
ImageData::JSON {
image,
imageheight,
imagewidth,
transparentcolor,
} => Image {
source: image,
transparent_color: transparentcolor,
width: imagewidth,
height: imageheight,
},
}
}
}
let data = ImageData::deserialize(deserializer)?;
Ok(data.into())
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct Frame {
#[serde(deserialize_with = "deserialize_number_from_string")]
pub tileid: u32,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub duration: u32,
}
fn deserialize_animation<'de, D>(deserializer: D) -> Result<Vec<Frame>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Animation {
#[serde(alias = "frame", default)]
frames: Vec<Frame>,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum Animations {
JSON(Vec<Frame>),
XML(Vec<Animation>),
}
match Animations::deserialize(deserializer)? {
Animations::XML(animations) => Ok(animations[0].frames.clone()),
Animations::JSON(frames) => Ok(frames),
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct Tile {
#[serde(deserialize_with = "deserialize_number_from_string")]
pub id: u32,
pub r#type: String,
#[serde(deserialize_with = "deserialize_animation", default)]
pub animation: Vec<Frame>,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct Tileset {
#[serde(flatten)]
pub metadata: Option<metadata::Metadata>,
pub name: String,
#[serde(
deserialize_with = "deserialize_number_from_string",
rename = "tilewidth"
)]
pub tile_width: u32,
#[serde(
deserialize_with = "deserialize_number_from_string",
rename = "tileheight"
)]
pub tile_height: u32,
#[serde(default, deserialize_with = "deserialize_number_from_string")]
pub spacing: u32,
#[serde(default, deserialize_with = "deserialize_number_from_string")]
pub margin: u32,
#[serde(
deserialize_with = "deserialize_number_from_string",
rename = "tilecount"
)]
pub tile_count: usize,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub columns: u32,
#[serde(rename = "backgroundcolor")]
pub background_color: Option<String>,
#[serde(flatten)]
pub image: Image,
#[serde(alias = "tile", default)]
pub tiles: Vec<Tile>,
}
impl Tileset {
pub fn from_json(s: &str) -> Result<Tileset, Error> {
serde_json::from_str(s).map_err(From::from)
}
pub fn from_json_data(buf: &[u8]) -> Result<Tileset, Error> {
let s = std::str::from_utf8(buf).map_err(Error::Utf8Error)?;
Tileset::from_json(s)
}
#[cfg(feature = "xml")]
pub fn from_xml(s: &str) -> Result<Tileset, Error> {
#[derive(Deserialize)]
struct Doc {
tileset: Vec<Tileset>,
}
let json = super::to_json::to_json(s).map_err(Error::Conversion)?;
let mut doc: Doc = serde_json::from_value(json).map_err(Error::Deserialization)?;
Ok(doc.tileset.remove(0))
}
#[cfg(feature = "xml")]
pub fn from_xml_data(buf: &[u8]) -> Result<Tileset, Error> {
let s = std::str::from_utf8(buf).map_err(Error::Utf8Error)?;
Tileset::from_xml(s)
}
}