wip(tesla_api_coverage): broken, but wip parse teslatte endpoints
This commit is contained in:
parent
3f7df753b1
commit
85ca49b49d
|
@ -16,4 +16,6 @@ nom = "7.1.3"
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
serde = { version = "1.0.189", features = ["derive"] }
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
heck = "0.5.0-rc.1"
|
heck = "0.5.0-rc.1"
|
||||||
|
glob = "0.3.1"
|
||||||
|
syn = { version = "2.0.38" , features = ["full", "extra-traits"] }
|
|
@ -1,4 +1,6 @@
|
||||||
mod fleet;
|
mod fleet;
|
||||||
|
mod nom_help;
|
||||||
|
mod teslatte;
|
||||||
mod timdorr;
|
mod timdorr;
|
||||||
mod vehicle_command;
|
mod vehicle_command;
|
||||||
|
|
||||||
|
@ -35,21 +37,26 @@ async fn main() {
|
||||||
cache_fetch(VEHICLE_COMMAND_URL, VEHICLE_COMMAND_FILE, args.cached)
|
cache_fetch(VEHICLE_COMMAND_URL, VEHICLE_COMMAND_FILE, args.cached)
|
||||||
);
|
);
|
||||||
|
|
||||||
let timdorr_endpoints = timdorr::parse(&timdorr);
|
// let timdorr_endpoints = timdorr::parse(&timdorr);
|
||||||
let fleet_endpoints = fleet::parse(&fleet_html);
|
// let fleet_endpoints = fleet::parse(&fleet_html);
|
||||||
let vehicle_command_endpoints = vehicle_command::parse(&command_golang);
|
// let vehicle_command_endpoints = vehicle_command::parse(&command_golang);
|
||||||
|
|
||||||
// Make hashsets from all the keys and see what's different between timdorr and fleet
|
let mut teslatte_project_path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
|
||||||
let timdorr_keys: std::collections::HashSet<&String> = timdorr_endpoints.keys().collect();
|
teslatte_project_path.push("..");
|
||||||
let fleet_keys: std::collections::HashSet<&String> = fleet_endpoints.keys().collect();
|
|
||||||
|
|
||||||
let timdorr_only = timdorr_keys.difference(&fleet_keys);
|
let teslatte_endpoints = teslatte::parse(&teslatte_project_path);
|
||||||
let fleet_only = fleet_keys.difference(&timdorr_keys);
|
|
||||||
let both = timdorr_keys.intersection(&fleet_keys);
|
|
||||||
|
|
||||||
info!("Timdorr only: {:?}", timdorr_only);
|
// // Make hashsets from all the keys and see what's different between timdorr and fleet
|
||||||
info!("Fleet only: {:?}", fleet_only);
|
// let timdorr_keys: std::collections::HashSet<&String> = timdorr_endpoints.keys().collect();
|
||||||
info!("Both: {:?}", both);
|
// let fleet_keys: std::collections::HashSet<&String> = fleet_endpoints.keys().collect();
|
||||||
|
//
|
||||||
|
// let timdorr_only = timdorr_keys.difference(&fleet_keys);
|
||||||
|
// let fleet_only = fleet_keys.difference(&timdorr_keys);
|
||||||
|
// let both = timdorr_keys.intersection(&fleet_keys);
|
||||||
|
//
|
||||||
|
// info!("Timdorr only: {:?}", timdorr_only);
|
||||||
|
// info!("Fleet only: {:?}", fleet_only);
|
||||||
|
// info!("Both: {:?}", both);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cache_fetch(url: &str, filename: &str, cache: bool) -> String {
|
async fn cache_fetch(url: &str, filename: &str, cache: bool) -> String {
|
||||||
|
|
39
tesla_api_coverage/src/nom_help.rs
Normal file
39
tesla_api_coverage/src/nom_help.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::{tag, take_until};
|
||||||
|
use nom::character::complete::{char, line_ending, space1};
|
||||||
|
use nom::multi::many0;
|
||||||
|
use nom::IResult;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
/// Ignore the quotes and return the inner string.
|
||||||
|
/// e.g. "unlock"
|
||||||
|
pub fn quoted_string(s: &str) -> IResult<&str, &str> {
|
||||||
|
short_trace("quoted string", s);
|
||||||
|
let (s, _) = char('"')(s)?;
|
||||||
|
let (s, string) = take_until("\"")(s)?;
|
||||||
|
let (s, _) = char('"')(s)?;
|
||||||
|
Ok((s, string))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ignore_whitespace(s: &str) -> IResult<&str, ()> {
|
||||||
|
short_trace("ignore whitespace", s);
|
||||||
|
let (s, ws) = many0(alt((tag("\t"), space1, line_ending)))(s)?;
|
||||||
|
short_trace("ignore whitespace afterwards", s);
|
||||||
|
Ok((s, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn until_eol(s: &str) -> IResult<&str, &str> {
|
||||||
|
short_trace("eol", s);
|
||||||
|
let (s, line) = take_until("\n")(s)?;
|
||||||
|
let (s, _) = line_ending(s)?;
|
||||||
|
short_trace("eol afterwards", s);
|
||||||
|
Ok((s, line))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn short_trace(prefix: &str, s: &str) {
|
||||||
|
let mut max_len_left = 20;
|
||||||
|
if s.len() < max_len_left {
|
||||||
|
max_len_left = s.len();
|
||||||
|
}
|
||||||
|
trace!("{}: {:?}...", prefix, &s[0..max_len_left])
|
||||||
|
}
|
243
tesla_api_coverage/src/teslatte.rs
Normal file
243
tesla_api_coverage/src/teslatte.rs
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
//! Parse the whole teslatte project and find any get*! post*! macros.
|
||||||
|
|
||||||
|
use crate::nom_help::{ignore_whitespace, quoted_string, short_trace};
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::{tag, take_till1, take_until1, take_while1};
|
||||||
|
use nom::character::complete::alpha1;
|
||||||
|
use nom::character::is_alphabetic;
|
||||||
|
use nom::combinator::opt;
|
||||||
|
use nom::{IResult, Needed};
|
||||||
|
use reqwest::Method;
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use tracing::{debug, info, trace};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TeslatteEndpoint {
|
||||||
|
pub method: Method,
|
||||||
|
pub endpoint: String,
|
||||||
|
pub uri: String,
|
||||||
|
// pub args: Vec<String>,
|
||||||
|
// pub post_struct: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(path: &Path) -> anyhow::Result<Vec<TeslatteEndpoint>> {
|
||||||
|
// glob all .rs files from path
|
||||||
|
|
||||||
|
let mut path = PathBuf::from(path);
|
||||||
|
path.push("src");
|
||||||
|
path.push("**/*.rs");
|
||||||
|
|
||||||
|
debug!("Globbing {path:?}");
|
||||||
|
|
||||||
|
let pattern = path.to_str().unwrap();
|
||||||
|
for file in glob::glob(pattern).unwrap() {
|
||||||
|
let path = file?;
|
||||||
|
|
||||||
|
if !path.ends_with("src/vehicles.rs") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_file(&path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(todo!())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Examples
|
||||||
|
///
|
||||||
|
/// impl VehicleApi for OwnerApi {
|
||||||
|
/// get!(vehicles, Vec<Vehicle>, "/vehicles");
|
||||||
|
/// get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", VehicleId);
|
||||||
|
/// post_arg_empty!(wake_up, "/vehicles/{}/command/wake_up", VehicleId);
|
||||||
|
///
|
||||||
|
/// Another one:
|
||||||
|
///
|
||||||
|
/// impl OwnerApi {
|
||||||
|
/// pub_get_arg!(powerwall_status, PowerwallStatus, "/powerwalls/{}/status", PowerwallId);
|
||||||
|
/// pub_get_args!(powerwall_energy_history, PowerwallEnergyHistory, "/powerwalls/{}/energyhistory", PowerwallEnergyHistoryValues);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
fn parse_file(path: &PathBuf) -> anyhow::Result<()> {
|
||||||
|
info!("Parsing file: {path:?}");
|
||||||
|
let content = read_to_string(path)?;
|
||||||
|
|
||||||
|
let mut endpoints = vec![];
|
||||||
|
let mut inside_owner_api = false;
|
||||||
|
|
||||||
|
for line in content.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
trace!(?line);
|
||||||
|
|
||||||
|
let owner_api_start = is_owner_api_start(line);
|
||||||
|
if owner_api_start {
|
||||||
|
if inside_owner_api {
|
||||||
|
panic!("Nested OwnerApi")
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Found OwnerApi");
|
||||||
|
inside_owner_api = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line == "}" && inside_owner_api {
|
||||||
|
trace!("End OwnerApi");
|
||||||
|
inside_owner_api = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !inside_owner_api {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Looking at line: {line:?}");
|
||||||
|
let (_, maybe_endpoint) = opt(alt((get, get_arg)))(line).unwrap();
|
||||||
|
if let Some(endpoint) = maybe_endpoint {
|
||||||
|
endpoints.push(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(endpoints);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_owner_api_start(line: &str) -> bool {
|
||||||
|
line.ends_with("OwnerApi {")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn common_macro_with_comma<'a>(expected_tag: &str, s: &'a str) -> IResult<&'a str, &'a str> {
|
||||||
|
// short_trace("common macro", s);
|
||||||
|
// let (s, _) = ignore_whitespace(s)?;
|
||||||
|
// let (s, _) = tag(expected_tag)(s)?;
|
||||||
|
// let (s, _) = tag("(")(s)?;
|
||||||
|
// let (s, fn_name) = function_name(s)?;
|
||||||
|
// let (s, ()) = comma(s)?;
|
||||||
|
//
|
||||||
|
// Ok((s, fn_name))
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn macro_fn_name_then_comma(expected_tag: &str) -> impl Fn(&str) -> IResult<&str, &str> + '_ {
|
||||||
|
return move |s: &str| -> IResult<&str, &str> {
|
||||||
|
short_trace("common macro", s);
|
||||||
|
let (s, _) = ignore_whitespace(s)?;
|
||||||
|
let (s, _) = tag(expected_tag)(s)?;
|
||||||
|
let (s, _) = tag("(")(s)?;
|
||||||
|
let (s, fn_name) = function_name(s)?;
|
||||||
|
let (s, ()) = comma(s)?;
|
||||||
|
|
||||||
|
Ok((s, fn_name))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get!(vehicles, Vec<Vehicle>, "/vehicles");
|
||||||
|
/// pub_get!(vehicles, Vec<Vehicle>, "/vehicles");
|
||||||
|
fn get(s: &str) -> IResult<&str, TeslatteEndpoint> {
|
||||||
|
let (s, fn_name) = alt((
|
||||||
|
macro_fn_name_then_comma("get!"),
|
||||||
|
macro_fn_name_then_comma("pub_get!"),
|
||||||
|
))(s)?;
|
||||||
|
|
||||||
|
let (s, response_type) = struct_name(s)?;
|
||||||
|
let (s, ()) = comma(s)?;
|
||||||
|
let (s, uri) = quoted_string(s)?;
|
||||||
|
let (s, _) = end_args(s)?;
|
||||||
|
|
||||||
|
let endpoint = TeslatteEndpoint {
|
||||||
|
method: Method::GET,
|
||||||
|
endpoint: fn_name.to_string(),
|
||||||
|
uri: uri.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((s, endpoint))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", VehicleId);
|
||||||
|
fn get_arg(s: &str) -> IResult<&str, TeslatteEndpoint> {
|
||||||
|
let (s, fn_name) = alt((
|
||||||
|
macro_fn_name_then_comma("get_arg!"),
|
||||||
|
macro_fn_name_then_comma("pub_get_arg!"),
|
||||||
|
))(s)?;
|
||||||
|
let (s, response_type) = struct_name(s)?;
|
||||||
|
let (s, ()) = comma(s)?;
|
||||||
|
let (s, uri) = quoted_string(s)?;
|
||||||
|
let (s, ()) = comma(s)?;
|
||||||
|
let (s, arg_type) = struct_name(s)?;
|
||||||
|
let (s, _) = end_args(s)?;
|
||||||
|
|
||||||
|
let endpoint = TeslatteEndpoint {
|
||||||
|
method: Method::GET,
|
||||||
|
endpoint: fn_name.to_string(),
|
||||||
|
uri: uri.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((s, endpoint))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn function_name(s: &str) -> IResult<&str, &str> {
|
||||||
|
take_while1(is_function_chars)(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn struct_name(s: &str) -> IResult<&str, &str> {
|
||||||
|
let (s, name) = take_while1(is_type)(s)?;
|
||||||
|
|
||||||
|
Ok((s, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_function_chars(c: char) -> bool {
|
||||||
|
is_lower_alpha(c) || c == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_lower_alpha(c: char) -> bool {
|
||||||
|
c.is_ascii_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_alpha(c: char) -> bool {
|
||||||
|
c.is_ascii_alphabetic()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_type(c: char) -> bool {
|
||||||
|
c.is_ascii_alphabetic() || c == '<' || c == '>'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn comma(s: &str) -> IResult<&str, ()> {
|
||||||
|
let (s, _) = tag(",")(s)?;
|
||||||
|
let (s, _) = ignore_whitespace(s)?;
|
||||||
|
|
||||||
|
Ok((s, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_args(s: &str) -> IResult<&str, ()> {
|
||||||
|
let (s, _) = tag(");")(s)?;
|
||||||
|
Ok((s, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get() {
|
||||||
|
let s = r#"get!(vehicles, Vec<Vehicle>, "/vehicles");"#;
|
||||||
|
let (_, endpoint) = get(s).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_get() {
|
||||||
|
let s = r#"pub_get!(vehicles, Vec<Vehicle>, "/vehicles");"#;
|
||||||
|
let (_, endpoint) = get(s).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_arg() {
|
||||||
|
let s = r#"get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", VehicleId);"#;
|
||||||
|
let (_, endpoint) = get_arg(s).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pub_get_arg() {
|
||||||
|
let s =
|
||||||
|
r#"pub_get_arg!(vehicle_data, VehicleData, "/vehicles/{}/vehicle_data", VehicleId);"#;
|
||||||
|
let (_, endpoint) = get_arg(s).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,18 +4,18 @@ use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
pub struct Endpoint {
|
pub struct TimdorrEndpoint {
|
||||||
#[serde(rename = "TYPE")]
|
#[serde(rename = "TYPE")]
|
||||||
method: String,
|
method: String,
|
||||||
uri: String,
|
uri: String,
|
||||||
auth: bool,
|
auth: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(json_str: &str) -> HashMap<String, Endpoint> {
|
pub fn parse(json_str: &str) -> HashMap<String, TimdorrEndpoint> {
|
||||||
let map: HashMap<String, Endpoint> = serde_json::from_str(json_str).unwrap();
|
let map: HashMap<String, TimdorrEndpoint> = serde_json::from_str(json_str).unwrap();
|
||||||
|
|
||||||
// rename all the keys to kebab-case
|
// rename all the keys to kebab-case
|
||||||
map.into_iter()
|
map.into_iter()
|
||||||
.map(|(k, v)| (k.to_kebab_case(), v))
|
.map(|(k, v)| (k.to_kebab_case(), v))
|
||||||
.collect::<HashMap<String, Endpoint>>()
|
.collect::<HashMap<String, TimdorrEndpoint>>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
use crate::nom_help;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::{tag, take_until, take_while};
|
use nom::bytes::complete::{tag, take_until};
|
||||||
use nom::character::complete::{char, line_ending, space0, space1, tab};
|
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::multi::{many0, many1};
|
use nom::multi::many1;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
pub fn parse(s: &str) -> HashMap<String, VehicleCommandEndpoint> {
|
pub fn parse(s: &str) -> HashMap<String, VehicleCommandEndpoint> {
|
||||||
// Seek all the way to: var commands = map[string]*Command{\n
|
// Seek all the way to: var commands = map[string]*Command{\n
|
||||||
|
@ -21,12 +21,12 @@ pub fn parse(s: &str) -> HashMap<String, VehicleCommandEndpoint> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seek_to_map(s: &str) -> IResult<&str, ()> {
|
pub fn seek_to_map(s: &str) -> IResult<&str, ()> {
|
||||||
short_trace("seek to map", s);
|
nom_help::short_trace("seek to map", s);
|
||||||
let tag_str = "var commands = map[string]*Command{\n";
|
let tag_str = "var commands = map[string]*Command{\n";
|
||||||
// There's gotta be a nom function to these two lines.
|
// There's gotta be a nom function to these two lines.
|
||||||
let (s, _) = take_until(tag_str)(s)?;
|
let (s, _) = take_until(tag_str)(s)?;
|
||||||
let (s, _) = tag(tag_str)(s)?;
|
let (s, _) = tag(tag_str)(s)?;
|
||||||
short_trace("seek to map done", s);
|
nom_help::short_trace("seek to map done", s);
|
||||||
Ok((s, ()))
|
Ok((s, ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,40 +52,40 @@ fn map_entry(s: &str) -> IResult<&str, VehicleCommandEndpoint> {
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
short_trace("--- map entry ---", s);
|
nom_help::short_trace("--- map entry ---", s);
|
||||||
|
|
||||||
// endpoint
|
// endpoint
|
||||||
short_trace("endpoint", s);
|
nom_help::short_trace("endpoint", s);
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, endpoint) = quoted_string(s)?;
|
let (s, endpoint) = nom_help::quoted_string(s)?;
|
||||||
let (s, _) = until_eol(s)?;
|
let (s, _) = nom_help::until_eol(s)?;
|
||||||
|
|
||||||
// help
|
// help
|
||||||
short_trace("help", s);
|
nom_help::short_trace("help", s);
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("help:")(s)?;
|
let (s, _) = tag("help:")(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, help) = quoted_string(s)?;
|
let (s, help) = nom_help::quoted_string(s)?;
|
||||||
let (s, _) = tag(",")(s)?;
|
let (s, _) = tag(",")(s)?;
|
||||||
|
|
||||||
// requiresAuth
|
// requiresAuth
|
||||||
short_trace("requiresAuth", s);
|
nom_help::short_trace("requiresAuth", s);
|
||||||
let (s, requires_auth) = bool_field_or_false(s, "requiresAuth:")?;
|
let (s, requires_auth) = bool_field_or_false(s, "requiresAuth:")?;
|
||||||
|
|
||||||
// requiresFleetAPI
|
// requiresFleetAPI
|
||||||
short_trace("requiresFleetAPI", s);
|
nom_help::short_trace("requiresFleetAPI", s);
|
||||||
let (s, requires_fleet) = bool_field_or_false(s, "requiresFleetAPI:")?;
|
let (s, requires_fleet) = bool_field_or_false(s, "requiresFleetAPI:")?;
|
||||||
|
|
||||||
// Required args
|
// Required args
|
||||||
short_trace("required args", s);
|
nom_help::short_trace("required args", s);
|
||||||
let (s, required_args) = args(s, "args: []Argument{")?;
|
let (s, required_args) = args(s, "args: []Argument{")?;
|
||||||
|
|
||||||
// Optional args
|
// Optional args
|
||||||
short_trace("optional args", s);
|
nom_help::short_trace("optional args", s);
|
||||||
let (s, optional_args) = args(s, "optional: []Argument{")?;
|
let (s, optional_args) = args(s, "optional: []Argument{")?;
|
||||||
|
|
||||||
// Ignore the handler, as there's not really much data we can take out of it.
|
// Ignore the handler, as there's not really much data we can take out of it.
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = take_until("},")(s)?;
|
let (s, _) = take_until("},")(s)?;
|
||||||
let (s, _) = tag("},")(s)?;
|
let (s, _) = tag("},")(s)?;
|
||||||
|
|
||||||
|
@ -104,49 +104,24 @@ fn map_entry(s: &str) -> IResult<&str, VehicleCommandEndpoint> {
|
||||||
Ok((s, map_entry))
|
Ok((s, map_entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ignore the quotes and return the inner string.
|
|
||||||
/// e.g. "unlock"
|
|
||||||
fn quoted_string(s: &str) -> IResult<&str, &str> {
|
|
||||||
short_trace("quoted string", s);
|
|
||||||
let (s, _) = char('"')(s)?;
|
|
||||||
let (s, string) = take_until("\"")(s)?;
|
|
||||||
let (s, _) = char('"')(s)?;
|
|
||||||
Ok((s, string))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ignore_whitespace(s: &str) -> IResult<&str, ()> {
|
|
||||||
short_trace("ignore whitespace", s);
|
|
||||||
let (s, ws) = many0(alt((tag("\t"), space1, line_ending)))(s)?;
|
|
||||||
short_trace("ignore whitespace afterwards", s);
|
|
||||||
Ok((s, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn until_eol(s: &str) -> IResult<&str, &str> {
|
|
||||||
short_trace("eol", s);
|
|
||||||
let (s, line) = take_until("\n")(s)?;
|
|
||||||
let (s, _) = line_ending(s)?;
|
|
||||||
short_trace("eol afterwards", s);
|
|
||||||
Ok((s, line))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn str_to_bool(s: &str) -> IResult<&str, bool> {
|
fn str_to_bool(s: &str) -> IResult<&str, bool> {
|
||||||
short_trace("bool", s);
|
nom_help::short_trace("bool", s);
|
||||||
let (s, bool_str) = alt((tag("true"), tag("false")))(s)?;
|
let (s, bool_str) = alt((tag("true"), tag("false")))(s)?;
|
||||||
let bool = match bool_str {
|
let bool = match bool_str {
|
||||||
"true" => true,
|
"true" => true,
|
||||||
"false" => false,
|
"false" => false,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
short_trace("bool afterwards", s);
|
nom_help::short_trace("bool afterwards", s);
|
||||||
Ok((s, bool))
|
Ok((s, bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the field isn't there, assume false.
|
/// If the field isn't there, assume false.
|
||||||
fn bool_field<'a>(field_tag: &str) -> impl Fn(&'a str) -> IResult<&'a str, bool> + '_ {
|
fn bool_field<'a>(field_tag: &str) -> impl Fn(&'a str) -> IResult<&'a str, bool> + '_ {
|
||||||
return move |s: &str| -> IResult<&'a str, bool> {
|
return move |s: &str| -> IResult<&'a str, bool> {
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag(field_tag)(s)?;
|
let (s, _) = tag(field_tag)(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, value) = str_to_bool(s)?;
|
let (s, value) = str_to_bool(s)?;
|
||||||
let (s, _) = tag(",")(s)?;
|
let (s, _) = tag(",")(s)?;
|
||||||
|
|
||||||
|
@ -165,41 +140,41 @@ struct Arg {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args<'a>(s: &'a str, field_tag: &str) -> IResult<&'a str, Vec<Arg>> {
|
fn args<'a>(s: &'a str, field_tag: &str) -> IResult<&'a str, Vec<Arg>> {
|
||||||
short_trace("args", s);
|
nom_help::short_trace("args", s);
|
||||||
|
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, maybe_field) = opt(tag(field_tag))(s)?;
|
let (s, maybe_field) = opt(tag(field_tag))(s)?;
|
||||||
if maybe_field.is_none() {
|
if maybe_field.is_none() {
|
||||||
trace!("no arg record");
|
trace!("no arg record");
|
||||||
return Ok((s, vec![]));
|
return Ok((s, vec![]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, args) = many1(arg)(s)?;
|
let (s, args) = many1(arg)(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("},")(s)?;
|
let (s, _) = tag("},")(s)?;
|
||||||
short_trace("args afterwards", s);
|
nom_help::short_trace("args afterwards", s);
|
||||||
Ok((s, args))
|
Ok((s, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg(s: &str) -> IResult<&str, Arg> {
|
fn arg(s: &str) -> IResult<&str, Arg> {
|
||||||
short_trace("arg", s);
|
nom_help::short_trace("arg", s);
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("Argument{")(s)?;
|
let (s, _) = tag("Argument{")(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("name:")(s)?;
|
let (s, _) = tag("name:")(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, name) = quoted_string(s)?;
|
let (s, name) = nom_help::quoted_string(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag(",")(s)?;
|
let (s, _) = tag(",")(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("help:")(s)?;
|
let (s, _) = tag("help:")(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, help) = quoted_string(s)?;
|
let (s, help) = nom_help::quoted_string(s)?;
|
||||||
let (s, _) = opt(tag(","))(s)?;
|
let (s, _) = opt(tag(","))(s)?;
|
||||||
let (s, _) = ignore_whitespace(s)?;
|
let (s, _) = nom_help::ignore_whitespace(s)?;
|
||||||
let (s, _) = tag("},")(s)?;
|
let (s, _) = tag("},")(s)?;
|
||||||
short_trace("arg afterwards", s);
|
nom_help::short_trace("arg afterwards", s);
|
||||||
Ok((
|
Ok((
|
||||||
s,
|
s,
|
||||||
Arg {
|
Arg {
|
||||||
|
@ -208,11 +183,3 @@ fn arg(s: &str) -> IResult<&str, Arg> {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn short_trace(prefix: &str, s: &str) {
|
|
||||||
let mut max_len_left = 20;
|
|
||||||
if s.len() < max_len_left {
|
|
||||||
max_len_left = s.len();
|
|
||||||
}
|
|
||||||
trace!("{}: {:?}...", prefix, &s[0..max_len_left])
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue