I'm writing a little library for parsing OVPN config files. OVPN config files have this format
command arg1 arg2
othercommand arg1 arg2
There's a fixed set of commands, some of them have optional arguments. I want to represent the parsed commands as an enum. So the above might end up being represented like this:
enum ConfigDirective{
Command{arg1: String},
OtherCommand{arg1: String, optinal_arg1: Option<String>},
}
fn parse_line(command: String, args: Vec<String>) -> ConfigDirective {
match command {
"command" => ConfigDirective::Command{arg1: args[0]},
"other_command" => ConfigDirective:OtherCommand{arg1: args[0], optional_arg1: args.get(1),
}
}
I like this structure but there are a lot of possible commands (somewhere in the region of 280). So I want to write a macro to generate most of the boilerplate. Ideally I would write something like the following:
define_config_directive!{
{command => "command1", rust_name => CommandOne, args => [arg1], optional_args => []},
{command => "other_command", rust_name => OtherCommand, args => [arg1], optional_args => [optional_arg1]},
}
The closest I've been able to get so far is this:
macro_rules! define_config_directives {
($({
rust_name => $rust_name:ident,
required => [$($required:ident),*],
optional => [$($optional:ident),*]
}),*) => {
#[derive(PartialEq, Eq, Debug)]
pub enum ConfigDirective {
$($rust_name{
$($required: String),*,
$($optional: Option<String>),*,
}),*
}
};
}
So I have a few problems:
- I don't know how to implement the
parse_line
function in this macro, I need to iterate over each required argument in order writing some code to pull the corresponding argument out of the line and the same for optional arguments - I don't know how to handle situations where there are no arguments at all, ideally that would be a simple enum variant without fields.
Does anyone know if there's a way to solve this on stable rust? Or should I just generate the code using a python script?
build.rs
file. When usingcargo build
, cargo will first compile and executebuild.rs
(if present) before compiling the rest of your crate, so you can easily usebuild.rs
to generate Rust code without involving any 3rd party tooling/makefile. – Matthieu M.