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}