#![doc = include_str!("../README.md")]
#![allow(unknown_lints)]
#![allow(clippy::result_large_err)]
use std::{
borrow::Cow,
collections::{btree_map::IntoIter, BTreeMap},
ops::{Deref, DerefMut},
};
pub mod error;
#[cfg(feature = "serde")]
mod serde;
pub mod text;
pub type Key<'text> = Cow<'text, str>;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Vdf<'text> {
pub key: Key<'text>,
pub value: Value<'text>,
}
impl<'text> From<PartialVdf<'text>> for Vdf<'text> {
fn from(partial: PartialVdf<'text>) -> Self {
Self {
key: partial.key,
value: partial.value,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PartialVdf<'text> {
pub key: Key<'text>,
pub value: Value<'text>,
pub bases: Vec<Cow<'text, str>>,
}
impl<'text> Vdf<'text> {
pub fn new(key: Key<'text>, value: Value<'text>) -> Self {
Self { key, value }
}
}
type ObjInner<'text> = BTreeMap<Key<'text>, Vec<Value<'text>>>;
type ObjInnerPair<'text> = (Key<'text>, Vec<Value<'text>>);
#[derive(Debug, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Obj<'text>(pub ObjInner<'text>);
impl<'text> Obj<'text> {
pub fn new() -> Self {
Self::default()
}
pub fn into_inner(self) -> ObjInner<'text> {
self.0
}
pub fn into_vdfs(self) -> IntoVdfs<'text> {
IntoVdfs::new(self)
}
}
impl<'text> FromIterator<ObjInnerPair<'text>> for Obj<'text> {
fn from_iter<T: IntoIterator<Item = ObjInnerPair<'text>>>(iter: T) -> Self {
let mut inner = BTreeMap::new();
for (key, values) in iter {
inner.insert(key, values);
}
Self(inner)
}
}
impl<'text> Deref for Obj<'text> {
type Target = ObjInner<'text>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Obj<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub struct IntoVdfs<'text> {
current_entry: Option<ObjInnerPair<'text>>,
it: IntoIter<Key<'text>, Vec<Value<'text>>>,
}
impl<'text> IntoVdfs<'text> {
fn new(obj: Obj<'text>) -> Self {
Self {
current_entry: None,
it: obj.into_inner().into_iter(),
}
}
}
impl<'text> Iterator for IntoVdfs<'text> {
type Item = Vdf<'text>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.current_entry.take() {
Some((key, mut values)) if !values.is_empty() => {
let value = values.pop().expect("values isn't empty");
self.current_entry = Some((key.clone(), values));
return Some(Vdf::new(key, value));
}
_ => {
let (key, values) = self.it.next()?;
self.current_entry = Some((key, values.into_iter().rev().collect()));
}
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Value<'text> {
Str(Cow<'text, str>),
Obj(Obj<'text>),
}
impl<'text> Value<'text> {
pub fn is_str(&self) -> bool {
self.get_str().is_some()
}
pub fn is_obj(&self) -> bool {
self.get_obj().is_some()
}
pub fn get_str(&self) -> Option<&str> {
if let Self::Str(s) = self {
Some(s)
} else {
None
}
}
pub fn get_obj(&self) -> Option<&Obj<'_>> {
if let Self::Obj(obj) = self {
Some(obj)
} else {
None
}
}
pub fn get_mut_str(&mut self) -> Option<&mut Cow<'text, str>> {
if let Self::Str(s) = self {
Some(s)
} else {
None
}
}
pub fn get_mut_obj(&mut self) -> Option<&mut Obj<'text>> {
if let Self::Obj(obj) = self {
Some(obj)
} else {
None
}
}
pub fn unwrap_str(self) -> Cow<'text, str> {
self.expect_str("Called `unwrap_str` on a `Value::Obj` variant")
}
pub fn unwrap_obj(self) -> Obj<'text> {
self.expect_obj("Called `unwrap_obj` on a `Value::Str` variant")
}
pub fn expect_str(self, msg: &str) -> Cow<'text, str> {
if let Self::Str(s) = self {
s
} else {
panic!("{}", msg)
}
}
pub fn expect_obj(self, msg: &str) -> Obj<'text> {
if let Self::Obj(obj) = self {
obj
} else {
panic!("{}", msg)
}
}
}