vmf_vmt/
lib.rs

1use nom::{
2    branch::alt, bytes::complete::is_not, character::complete::{multispace0, none_of, one_of}, combinator::recognize, error::ParseError, multi::{many0, many1}, sequence::{delimited, separated_pair}, IResult
3};
4
5#[derive(PartialEq,Debug)]
6pub enum Valve {
7    Class {
8        class_name: String,
9        data: Vec<Valve>,
10    },
11    PropertyValue(String, String),
12}
13
14pub type VmfVmt = Vec<Valve>;
15
16pub fn build_vmf_vmt(data: VmfVmt) -> String {
17    data.into_iter().map(|class| build_valve(0,class)).collect::<Vec<String>>().concat()
18}
19
20pub fn build_valve(indent: usize, data: Valve) -> String {
21    match data {
22        Valve::Class { class_name, data } => {
23            let mut out = String::new();
24            for _ in 0..indent {out.push('\t')};
25            out.push_str(&class_name);
26            out.push_str("\n");
27            for _ in 0..indent {out.push('\t')};
28            out.push_str("{\n");
29            for subvalue in data {
30                out.push_str(&build_valve(indent+1,subvalue));
31            }
32            for _ in 0..indent {out.push('\t')};
33            out.push_str("}\n");
34            out
35        },
36        Valve::PropertyValue(property, value) => {
37            let mut out = String::new();
38            for _ in 0..indent {out.push('\t')};
39            out.push_str("\"");
40            out.push_str(&property);
41            out.push_str("\" \"");
42            out.push_str(&value);
43            out.push_str("\"\n");
44            out
45        },
46    }
47}
48
49pub fn parse_vmf_vmt(input: &str) -> IResult<&str,VmfVmt> {
50    many1(ws(parse_class))(input)
51}
52
53fn parse_valve(input: &str) -> IResult<&str, Valve> {
54    alt((parse_class, parse_property_value))(input)
55}
56
57fn parse_class(input: &str) -> IResult<&str, Valve> {
58    let (input, class_name) = parse_unquoted(input)?;
59    let (input, data) = delimited(
60        ws(one_of("{")),
61        many0(ws(parse_valve)),
62        ws(one_of("}")),
63    )(input)?;
64    Ok((input, Valve::Class { class_name, data }))
65}
66
67fn parse_property_value(input: &str) -> IResult<&str, Valve> {
68    let (input, (property, value)) =
69        separated_pair(parse_string, multispace0, parse_string)(input)?;
70    Ok((input, Valve::PropertyValue(property, value)))
71}
72
73fn parse_string(input: &str) -> IResult<&str, String> {
74    alt((parse_unquoted, parse_quoted))(input)
75}
76
77fn parse_unquoted(input: &str) -> IResult<&str, String> {
78    let (input, string) = is_not(" \t\r\n\"{{}}")(input)?;
79    Ok((input, string.to_string()))
80}
81
82fn parse_quoted(input: &str) -> IResult<&str, String> {
83    let (input, string) = delimited(
84        recognize(one_of("\"")),
85        recognize(many0(none_of("\""))),
86        recognize(one_of("\"")),
87    )(input)?;
88    Ok((input, string.to_string()))
89}
90
91fn ws<'a, F: 'a, O, E: ParseError<&'a str>>(
92    inner: F,
93) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
94where
95    F: Fn(&'a str) -> IResult<&'a str, O, E>,
96{
97    delimited(multispace0, inner, multispace0)
98}