#![doc = include_str!("../README.md")]
#![allow(unknown_lints)]
#![allow(clippy::result_large_err)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::{
borrow::Cow,
collections::{btree_map::IntoIter, BTreeMap},
fmt,
ops::{Deref, DerefMut},
};
pub mod error;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
mod serde;
pub mod text;
pub use pest;
pub fn parse<'text>(text: &'text str) -> error::Result<PartialVdf<'text>> {
Parser::new().parse(text)
}
#[derive(Clone, Debug, Default)]
pub struct Parser {
literal_special_chars: bool,
}
impl Parser {
pub const fn new() -> Self {
Self {
literal_special_chars: false,
}
}
pub const fn literal_special_chars(mut self, yes: bool) -> Self {
self.literal_special_chars = yes;
self
}
pub fn parse<'text>(&self, vdf: &'text str) -> error::Result<PartialVdf<'text>> {
if self.literal_special_chars {
#[expect(deprecated)] text::parse::raw_parse(vdf)
} else {
#[expect(deprecated)] text::parse::escaped_parse(vdf)
}
}
}
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> Vdf<'text> {
pub fn new(key: Key<'text>, value: Value<'text>) -> Self {
Self { key, value }
}
}
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>>,
}
type ObjInner<'text> = BTreeMap<Key<'text>, Vec<Value<'text>>>;
type ObjInnerPair<'text> = (Key<'text>, Vec<Value<'text>>);
#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Obj<'text>(pub ObjInner<'text>);
impl fmt::Debug for Obj<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut map_f = f.debug_map();
for (k, v) in &self.0 {
map_f.key(k);
match v.as_slice() {
[single] => _ = map_f.value(single),
_ => _ = map_f.value(v),
}
}
map_f.finish()
}
}
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, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Value<'text> {
Str(Cow<'text, str>),
Obj(Obj<'text>),
}
impl fmt::Debug for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Str(s) => {
f.write_str("Str(")?;
fmt::Debug::fmt(s, f)?;
f.write_str(")")
}
Self::Obj(o) => {
f.write_str("Obj(")?;
fmt::Debug::fmt(o, f)?;
f.write_str(")")
}
}
}
}
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)
}
}
}