#![deny(missing_docs)]
use std::error::Error;
use std::fmt::{Display, Error as FmtError, Formatter};
use anyhow::{Context, Error as AnyError};
use serde::{Deserialize, Deserializer};
use thiserror::Error;
#[cfg(test)]
mod tests;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LoadError {
#[error("Could not parse format.version: {0}")]
Format(#[source] AnyError),
}
pub type VersionTuple = (u32, u32);
impl From<&Version> for VersionTuple {
#[inline]
fn from(ver: &Version) -> Self {
(ver.major, ver.minor)
}
}
#[derive(Debug, Deserialize, PartialEq, Eq)]
pub struct Version {
major: u32,
minor: u32,
}
impl Version {
#[must_use]
#[inline]
pub const fn new(major: u32, minor: u32) -> Self {
Self { major, minor }
}
#[allow(clippy::must_use_candidate)]
#[inline]
pub const fn major(&self) -> u32 {
self.major
}
#[allow(clippy::must_use_candidate)]
#[inline]
pub const fn minor(&self) -> u32 {
self.minor
}
#[allow(clippy::must_use_candidate)]
#[inline]
pub const fn as_version_tuple(&self) -> VersionTuple {
(self.major, self.minor)
}
}
impl Display for Version {
#[inline]
fn fmt(&self, writer: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(writer, "{}.{}", self.major, self.minor)
}
}
#[derive(Debug, Deserialize)]
pub struct Format {
version: Version,
}
impl Format {
#[allow(clippy::must_use_candidate)]
#[inline]
pub const fn version(&self) -> &Version {
&self.version
}
}
#[derive(Debug, Deserialize)]
pub struct FormatOnlyTop {
format: Format,
}
#[inline]
pub fn get_format_from_str<'contents, 'data, E, F>(
contents: &'contents str,
from_str_fn: F,
) -> Result<Format, LoadError>
where
'contents: 'data,
E: Error + Send + Sync + 'static,
F: Fn(&'data str) -> Result<FormatOnlyTop, E>,
{
Ok(from_str_fn(contents)
.context("Could not parse the input data")
.map_err(LoadError::Format)?
.format)
}
#[inline]
pub fn get_version_from_str<'contents, 'data, E, F>(
contents: &'contents str,
from_str_fn: F,
) -> Result<Version, LoadError>
where
'contents: 'data,
E: Error + Send + Sync + 'static,
F: Fn(&'data str) -> Result<FormatOnlyTop, E>,
{
Ok(get_format_from_str(contents, from_str_fn)?.version)
}
#[inline]
pub fn get_format_from_value<V: Deserializer<'static> + 'static>(
value: V,
) -> Result<Format, LoadError>
where
V::Error: Send + Sync,
{
let ftop: FormatOnlyTop = FormatOnlyTop::deserialize(value)
.context("Could not extract the top-level format object")
.map_err(LoadError::Format)?;
Ok(ftop.format)
}
#[inline]
pub fn get_version_from_value<V: Deserializer<'static> + 'static>(
value: V,
) -> Result<Version, LoadError>
where
V::Error: Send + Sync,
{
Ok(get_format_from_value(value)?.version)
}