no clue what the current status is

This commit is contained in:
2026-08-14 23:08:42 +02:00
parent b0efc215ba
commit bee5e0f11d
82 changed files with 3778 additions and 3777 deletions

0
crates/guardian_commands/Cargo.toml Normal file → Executable file
View File

902
crates/guardian_commands/src/call.rs Normal file → Executable file
View File

@@ -1,451 +1,451 @@
use std::fmt::Display;
use nom::branch::alt;
use nom::bytes::complete::{tag, tag_no_case};
use nom::character::complete::{alpha1, alphanumeric1, char, multispace0};
use nom::combinator::{map, opt, recognize};
use nom::error::ErrorKind;
use nom::sequence::tuple;
use nom::{Err as NomError, IResult};
#[derive(Debug)]
pub enum Awacs {
Overlord,
Magic,
Wizard,
Focus,
Darkstar,
}
#[derive(Debug)]
pub enum Receiver {
Awacs(Awacs),
Atc(String), //airfield name
}
impl Display for Receiver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Receiver::Awacs(Awacs::Overlord) => write!(f, "Overlord"),
Receiver::Awacs(Awacs::Magic) => write!(f, "Magic"),
Receiver::Awacs(Awacs::Wizard) => write!(f, "Wizard"),
Receiver::Awacs(Awacs::Focus) => write!(f, "Focus"),
Receiver::Awacs(Awacs::Darkstar) => write!(f, "Darkstar"),
Receiver::Atc(airfield) => write!(f, "{}", airfield.to_owned()),
}
}
}
// impl ToString for Receiver {
// fn to_string(&self) -> String {
// match self {
// Receiver::Awacs(Awacs::Overlord) => "Overlord".to_string(),
// Receiver::Awacs(Awacs::Darkstar) => "Darkstar".to_string(),
// Receiver::Atc(airfield) => airfield.to_owned(),
// }
// }
// }
fn parse_receiver(input: &str) -> IResult<&str, Receiver> {
alt((
map(tag_no_case("overlord"), |_| {
Receiver::Awacs(Awacs::Overlord)
}),
map(tag_no_case("magic"), |_| Receiver::Awacs(Awacs::Magic)),
map(tag_no_case("wizard"), |_| Receiver::Awacs(Awacs::Wizard)),
map(tag_no_case("focus"), |_| Receiver::Awacs(Awacs::Focus)),
map(tag_no_case("darkstar"), |_| {
Receiver::Awacs(Awacs::Darkstar)
}),
map(alphanumeric1, |e: &str| Receiver::Atc(e.to_owned())), // anything else = airfield? airfields _can_ contain spaces though.. soo w... hmm... parsing that is a bitch
))(input)
}
#[derive(Debug)]
pub enum Squadron {
Enfield,
Springfield,
Uzi,
Colt,
Dodge,
Ford,
Chevy,
Pontiac,
Viper,
Venom,
Lobo,
Cowboy,
Python,
Rattler,
Panther,
Wolf,
Weasel,
Wild,
Ninja,
Jedi,
Hawg,
Boar,
Pig,
Tusk,
ArmyAir,
Apache,
Crow,
Chaos,
Sioux,
Gatling,
Gunslinger,
Hammerhead,
Bootleg,
Palehorse,
Carnivore,
Saber,
Hornet,
Squid,
Ragin,
Roman,
Sting,
Jury,
Joker,
Ram,
Hawk,
Devil,
Check,
Snake,
Custom(String),
}
impl ToString for Squadron {
fn to_string(&self) -> String {
match self {
Squadron::Enfield => "Enfield".to_string(),
Squadron::Springfield => "Springfield".to_string(),
Squadron::Uzi => "Uzi".to_string(),
Squadron::Colt => "Colt".to_string(),
Squadron::Dodge => "Dodge".to_string(),
Squadron::Ford => "Ford".to_string(),
Squadron::Chevy => "Chevy".to_string(),
Squadron::Hornet => "Hornet".to_string(),
Squadron::Pontiac => "Pontiac".to_string(),
Squadron::Viper => "Viper".to_string(),
Squadron::Venom => "Venom".to_string(),
Squadron::Lobo => "Lobo".to_string(),
Squadron::Cowboy => "Cowboy".to_string(),
Squadron::Python => "Python".to_string(),
Squadron::Rattler => "Rattler".to_string(),
Squadron::Panther => "Panther".to_string(),
Squadron::Wolf => "Wolf".to_string(),
Squadron::Weasel => "Weasel".to_string(),
Squadron::Wild => "Wild".to_string(),
Squadron::Ninja => "Ninja".to_string(),
Squadron::Jedi => "Jedi".to_string(),
Squadron::Hawg => "Hawg".to_string(),
Squadron::Boar => "Boar".to_string(),
Squadron::Pig => "Pig".to_string(),
Squadron::Tusk => "Tusk".to_string(),
Squadron::ArmyAir => "ArmyAir".to_string(),
Squadron::Apache => "Apache".to_string(),
Squadron::Crow => "Crow".to_string(),
Squadron::Chaos => "Chaos".to_string(),
Squadron::Sioux => "Sioux".to_string(),
Squadron::Gatling => "Gatling".to_string(),
Squadron::Gunslinger => "Gunslinger".to_string(),
Squadron::Hammerhead => "Hammerhead".to_string(),
Squadron::Bootleg => "Bootleg".to_string(),
Squadron::Palehorse => "Palehorse".to_string(),
Squadron::Carnivore => "Carnivore".to_string(),
Squadron::Saber => "Saber".to_string(),
Squadron::Squid => "Squid".to_string(),
Squadron::Ragin => "Ragin".to_string(),
Squadron::Roman => "Roman".to_string(),
Squadron::Sting => "Sting".to_string(),
Squadron::Jury => "Jury".to_string(),
Squadron::Joker => "Joker".to_string(),
Squadron::Ram => "Ram".to_string(),
Squadron::Hawk => "Hawk".to_string(),
Squadron::Devil => "Devil".to_string(),
Squadron::Check => "Check".to_string(),
Squadron::Snake => "Snake".to_string(),
Squadron::Custom(c) => c.to_string(),
}
}
}
impl std::str::FromStr for Squadron {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Enfield" => Ok(Squadron::Enfield),
"Springfield" => Ok(Squadron::Springfield),
"Uzi" => Ok(Squadron::Uzi),
"Colt" => Ok(Squadron::Colt),
"Dodge" => Ok(Squadron::Dodge),
"Ford" => Ok(Squadron::Ford),
"Chevy" => Ok(Squadron::Chevy),
"Hornet" => Ok(Squadron::Hornet),
"Pontiac" => Ok(Squadron::Pontiac),
"Viper" => Ok(Squadron::Viper),
"Venom" => Ok(Squadron::Venom),
"Lobo" => Ok(Squadron::Lobo),
"Cowboy" => Ok(Squadron::Cowboy),
"Python" => Ok(Squadron::Python),
"Rattler" => Ok(Squadron::Rattler),
"Panther" => Ok(Squadron::Panther),
"Wolf" => Ok(Squadron::Wolf),
"Weasel" => Ok(Squadron::Weasel),
"Wild" => Ok(Squadron::Wild),
"Ninja" => Ok(Squadron::Ninja),
"Jedi" => Ok(Squadron::Jedi),
"Hawg" => Ok(Squadron::Hawg),
"Boar" => Ok(Squadron::Boar),
"Pig" => Ok(Squadron::Pig),
"Tusk" => Ok(Squadron::Tusk),
"ArmyAir" => Ok(Squadron::ArmyAir),
"Apache" => Ok(Squadron::Apache),
"Crow" => Ok(Squadron::Crow),
"Chaos" => Ok(Squadron::Chaos),
"Sioux" => Ok(Squadron::Sioux),
"Gatling" => Ok(Squadron::Gatling),
"Gunslinger" => Ok(Squadron::Gunslinger),
"Hammerhead" => Ok(Squadron::Hammerhead),
"Bootleg" => Ok(Squadron::Bootleg),
"Palehorse" => Ok(Squadron::Palehorse),
"Carnivore" => Ok(Squadron::Carnivore),
"Saber" => Ok(Squadron::Saber),
"Squid" => Ok(Squadron::Squid),
"Ragin" => Ok(Squadron::Ragin),
"Roman" => Ok(Squadron::Roman),
"Sting" => Ok(Squadron::Sting),
"Jury" => Ok(Squadron::Jury),
"Joker" => Ok(Squadron::Joker),
"Ram" => Ok(Squadron::Ram),
"Hawk" => Ok(Squadron::Hawk),
"Devil" => Ok(Squadron::Devil),
"Check" => Ok(Squadron::Check),
"Snake" => Ok(Squadron::Snake),
c => Ok(Squadron::Custom(c.to_string())),
}
}
}
#[derive(Debug)]
pub struct Element {
pub squadron: Squadron,
pub group: u8,
pub unit: u8,
}
impl Display for Element {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {} {}",
self.squadron.to_string(),
self.group,
self.unit
)
}
}
fn add_spaces_around_numbers(input: &str) -> String {
let mut result = String::new();
let mut chars = input.chars().peekable();
while let Some(c) = chars.next() {
result.push(c);
if let Some(next_char) = chars.peek() {
if next_char.is_numeric() {
result.push(' ');
}
}
}
result
}
impl std::str::FromStr for Element {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let d = add_spaces_around_numbers(s);
let mut d = d.split(' ');
let squadron: Squadron = d.next().unwrap().parse()?;
let group: u8 = d.next().unwrap().parse()?;
let unit: u8 = d.next().unwrap().parse()?;
Ok(Element {
squadron,
group,
unit,
})
}
}
fn parse_element(input: &str) -> IResult<&str, Element> {
let mut parser = tuple((
alpha1, // Squadron name
opt(multispace0), // Optional whitespace
opt(tuple((
// Optional group
opt(multispace0),
parse_group_unit,
opt(multispace0),
))),
opt(char('-')),
opt(tuple((
// Optional unit
opt(multispace0),
parse_group_unit,
opt(multispace0),
))),
));
let (remainder, (squadron_str, _, group_str, _, unit_str)) = parser(input)?;
let squadron = match squadron_str.to_lowercase().as_str() {
"enfield" => Squadron::Enfield,
"springfield" => Squadron::Springfield,
"uzi" => Squadron::Uzi,
"colt" => Squadron::Colt,
"dodge" => Squadron::Dodge,
"ford" => Squadron::Ford,
"chevy" => Squadron::Chevy,
"hornet" => Squadron::Hornet,
"pontiac" => Squadron::Pontiac,
"viper" => Squadron::Viper,
"venom" => Squadron::Venom,
"lobo" => Squadron::Lobo,
"cowboy" => Squadron::Cowboy,
"python" => Squadron::Python,
"rattler" => Squadron::Rattler,
"panther" => Squadron::Panther,
"wolf" => Squadron::Wolf,
"weasel" => Squadron::Weasel,
"wild" => Squadron::Wild,
"ninja" => Squadron::Ninja,
"jedi" => Squadron::Jedi,
"hawg" => Squadron::Hawg,
"boar" => Squadron::Boar,
"pig" => Squadron::Pig,
"tusk" => Squadron::Tusk,
"armyair" => Squadron::ArmyAir,
"apache" => Squadron::Apache,
"crow" => Squadron::Crow,
"chaos" => Squadron::Chaos,
"sioux" => Squadron::Sioux,
"gatling" => Squadron::Gatling,
"gunslinger" => Squadron::Gunslinger,
"hammerhead" => Squadron::Hammerhead,
"bootleg" => Squadron::Bootleg,
"palehorse" => Squadron::Palehorse,
"carnivore" => Squadron::Carnivore,
"saber" => Squadron::Saber,
"squid" => Squadron::Squid,
"ragin" => Squadron::Ragin,
"roman" => Squadron::Roman,
"sting" => Squadron::Sting,
"jury" => Squadron::Jury,
"joker" => Squadron::Joker,
"ram" => Squadron::Ram,
"hawk" => Squadron::Hawk,
"devil" => Squadron::Devil,
"check" => Squadron::Check,
"snake" => Squadron::Snake,
c => Squadron::Custom(c.to_string()),
};
// _ => {
// return Err(NomError::Error(nom::error::Error {
// input: squadron_str,
// code: ErrorKind::Alpha,
// }))
// }
let group = group_str.unwrap_or((None, 0, None)).1;
let unit = unit_str.unwrap_or((None, 0, None)).1;
Ok((
remainder,
Element {
squadron,
group,
unit,
},
))
}
fn parse_group_unit(input: &str) -> IResult<&str, u8> {
let parse_digit = map(
recognize(alt((
char('0'),
char('1'),
char('2'),
char('3'),
char('4'),
char('5'),
char('6'),
char('7'),
char('8'),
char('9'),
))),
|s: &str| s.parse::<u8>().unwrap(),
);
let parse_word = alt((
map(tag("one"), |_| 1),
map(tag("two"), |_| 2),
map(tag("three"), |_| 3),
map(tag("four"), |_| 4),
map(tag("five"), |_| 5),
map(tag("six"), |_| 6),
map(tag("seven"), |_| 7),
map(tag("eight"), |_| 8),
map(tag("nine"), |_| 9),
));
alt((parse_digit, parse_word))(input)
}
// #[derive(Debug)]
// pub enum Unit {
// NauticalMiles,
// Kilometers,
// }
// #[derive(Debug)]
// pub enum Call {
// RadioCheck(Awacs, Element),
// Shopping(Awacs, Element),
// LocateAirfield(Awacs, Element, String),
// LocateFriendly(Awacs, Element, Element),
// Tripwire(Awacs, Element, Option<i32>, Unit),
// }
#[derive(Debug)]
pub struct Call {
pub receiver: Receiver,
pub element: Element,
pub command: String,
}
pub fn parse_call(input: &str) -> IResult<&str, Call> {
let mut parser = tuple((parse_receiver, multispace0, parse_element));
let (command, (receiver, _, element)) = parser(input)?;
if element.group == 0 || element.unit == 0 {
return Err(NomError::Error(nom::error::Error {
input: command,
code: ErrorKind::Digit,
}));
}
Ok((
"",
Call {
receiver,
element,
command: command.to_string(),
},
))
}
use std::fmt::Display;
use nom::branch::alt;
use nom::bytes::complete::{tag, tag_no_case};
use nom::character::complete::{alpha1, alphanumeric1, char, multispace0};
use nom::combinator::{map, opt, recognize};
use nom::error::ErrorKind;
use nom::sequence::tuple;
use nom::{Err as NomError, IResult};
#[derive(Debug)]
pub enum Awacs {
Overlord,
Magic,
Wizard,
Focus,
Darkstar,
}
#[derive(Debug)]
pub enum Receiver {
Awacs(Awacs),
Atc(String), //airfield name
}
impl Display for Receiver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Receiver::Awacs(Awacs::Overlord) => write!(f, "Overlord"),
Receiver::Awacs(Awacs::Magic) => write!(f, "Magic"),
Receiver::Awacs(Awacs::Wizard) => write!(f, "Wizard"),
Receiver::Awacs(Awacs::Focus) => write!(f, "Focus"),
Receiver::Awacs(Awacs::Darkstar) => write!(f, "Darkstar"),
Receiver::Atc(airfield) => write!(f, "{}", airfield.to_owned()),
}
}
}
// impl ToString for Receiver {
// fn to_string(&self) -> String {
// match self {
// Receiver::Awacs(Awacs::Overlord) => "Overlord".to_string(),
// Receiver::Awacs(Awacs::Darkstar) => "Darkstar".to_string(),
// Receiver::Atc(airfield) => airfield.to_owned(),
// }
// }
// }
fn parse_receiver(input: &str) -> IResult<&str, Receiver> {
alt((
map(tag_no_case("overlord"), |_| {
Receiver::Awacs(Awacs::Overlord)
}),
map(tag_no_case("magic"), |_| Receiver::Awacs(Awacs::Magic)),
map(tag_no_case("wizard"), |_| Receiver::Awacs(Awacs::Wizard)),
map(tag_no_case("focus"), |_| Receiver::Awacs(Awacs::Focus)),
map(tag_no_case("darkstar"), |_| {
Receiver::Awacs(Awacs::Darkstar)
}),
map(alphanumeric1, |e: &str| Receiver::Atc(e.to_owned())), // anything else = airfield? airfields _can_ contain spaces though.. soo w... hmm... parsing that is a bitch
))(input)
}
#[derive(Debug)]
pub enum Squadron {
Enfield,
Springfield,
Uzi,
Colt,
Dodge,
Ford,
Chevy,
Pontiac,
Viper,
Venom,
Lobo,
Cowboy,
Python,
Rattler,
Panther,
Wolf,
Weasel,
Wild,
Ninja,
Jedi,
Hawg,
Boar,
Pig,
Tusk,
ArmyAir,
Apache,
Crow,
Chaos,
Sioux,
Gatling,
Gunslinger,
Hammerhead,
Bootleg,
Palehorse,
Carnivore,
Saber,
Hornet,
Squid,
Ragin,
Roman,
Sting,
Jury,
Joker,
Ram,
Hawk,
Devil,
Check,
Snake,
Custom(String),
}
impl ToString for Squadron {
fn to_string(&self) -> String {
match self {
Squadron::Enfield => "Enfield".to_string(),
Squadron::Springfield => "Springfield".to_string(),
Squadron::Uzi => "Uzi".to_string(),
Squadron::Colt => "Colt".to_string(),
Squadron::Dodge => "Dodge".to_string(),
Squadron::Ford => "Ford".to_string(),
Squadron::Chevy => "Chevy".to_string(),
Squadron::Hornet => "Hornet".to_string(),
Squadron::Pontiac => "Pontiac".to_string(),
Squadron::Viper => "Viper".to_string(),
Squadron::Venom => "Venom".to_string(),
Squadron::Lobo => "Lobo".to_string(),
Squadron::Cowboy => "Cowboy".to_string(),
Squadron::Python => "Python".to_string(),
Squadron::Rattler => "Rattler".to_string(),
Squadron::Panther => "Panther".to_string(),
Squadron::Wolf => "Wolf".to_string(),
Squadron::Weasel => "Weasel".to_string(),
Squadron::Wild => "Wild".to_string(),
Squadron::Ninja => "Ninja".to_string(),
Squadron::Jedi => "Jedi".to_string(),
Squadron::Hawg => "Hawg".to_string(),
Squadron::Boar => "Boar".to_string(),
Squadron::Pig => "Pig".to_string(),
Squadron::Tusk => "Tusk".to_string(),
Squadron::ArmyAir => "ArmyAir".to_string(),
Squadron::Apache => "Apache".to_string(),
Squadron::Crow => "Crow".to_string(),
Squadron::Chaos => "Chaos".to_string(),
Squadron::Sioux => "Sioux".to_string(),
Squadron::Gatling => "Gatling".to_string(),
Squadron::Gunslinger => "Gunslinger".to_string(),
Squadron::Hammerhead => "Hammerhead".to_string(),
Squadron::Bootleg => "Bootleg".to_string(),
Squadron::Palehorse => "Palehorse".to_string(),
Squadron::Carnivore => "Carnivore".to_string(),
Squadron::Saber => "Saber".to_string(),
Squadron::Squid => "Squid".to_string(),
Squadron::Ragin => "Ragin".to_string(),
Squadron::Roman => "Roman".to_string(),
Squadron::Sting => "Sting".to_string(),
Squadron::Jury => "Jury".to_string(),
Squadron::Joker => "Joker".to_string(),
Squadron::Ram => "Ram".to_string(),
Squadron::Hawk => "Hawk".to_string(),
Squadron::Devil => "Devil".to_string(),
Squadron::Check => "Check".to_string(),
Squadron::Snake => "Snake".to_string(),
Squadron::Custom(c) => c.to_string(),
}
}
}
impl std::str::FromStr for Squadron {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Enfield" => Ok(Squadron::Enfield),
"Springfield" => Ok(Squadron::Springfield),
"Uzi" => Ok(Squadron::Uzi),
"Colt" => Ok(Squadron::Colt),
"Dodge" => Ok(Squadron::Dodge),
"Ford" => Ok(Squadron::Ford),
"Chevy" => Ok(Squadron::Chevy),
"Hornet" => Ok(Squadron::Hornet),
"Pontiac" => Ok(Squadron::Pontiac),
"Viper" => Ok(Squadron::Viper),
"Venom" => Ok(Squadron::Venom),
"Lobo" => Ok(Squadron::Lobo),
"Cowboy" => Ok(Squadron::Cowboy),
"Python" => Ok(Squadron::Python),
"Rattler" => Ok(Squadron::Rattler),
"Panther" => Ok(Squadron::Panther),
"Wolf" => Ok(Squadron::Wolf),
"Weasel" => Ok(Squadron::Weasel),
"Wild" => Ok(Squadron::Wild),
"Ninja" => Ok(Squadron::Ninja),
"Jedi" => Ok(Squadron::Jedi),
"Hawg" => Ok(Squadron::Hawg),
"Boar" => Ok(Squadron::Boar),
"Pig" => Ok(Squadron::Pig),
"Tusk" => Ok(Squadron::Tusk),
"ArmyAir" => Ok(Squadron::ArmyAir),
"Apache" => Ok(Squadron::Apache),
"Crow" => Ok(Squadron::Crow),
"Chaos" => Ok(Squadron::Chaos),
"Sioux" => Ok(Squadron::Sioux),
"Gatling" => Ok(Squadron::Gatling),
"Gunslinger" => Ok(Squadron::Gunslinger),
"Hammerhead" => Ok(Squadron::Hammerhead),
"Bootleg" => Ok(Squadron::Bootleg),
"Palehorse" => Ok(Squadron::Palehorse),
"Carnivore" => Ok(Squadron::Carnivore),
"Saber" => Ok(Squadron::Saber),
"Squid" => Ok(Squadron::Squid),
"Ragin" => Ok(Squadron::Ragin),
"Roman" => Ok(Squadron::Roman),
"Sting" => Ok(Squadron::Sting),
"Jury" => Ok(Squadron::Jury),
"Joker" => Ok(Squadron::Joker),
"Ram" => Ok(Squadron::Ram),
"Hawk" => Ok(Squadron::Hawk),
"Devil" => Ok(Squadron::Devil),
"Check" => Ok(Squadron::Check),
"Snake" => Ok(Squadron::Snake),
c => Ok(Squadron::Custom(c.to_string())),
}
}
}
#[derive(Debug)]
pub struct Element {
pub squadron: Squadron,
pub group: u8,
pub unit: u8,
}
impl Display for Element {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {} {}",
self.squadron.to_string(),
self.group,
self.unit
)
}
}
fn add_spaces_around_numbers(input: &str) -> String {
let mut result = String::new();
let mut chars = input.chars().peekable();
while let Some(c) = chars.next() {
result.push(c);
if let Some(next_char) = chars.peek() {
if next_char.is_numeric() {
result.push(' ');
}
}
}
result
}
impl std::str::FromStr for Element {
type Err = Box<dyn std::error::Error + Send + Sync>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let d = add_spaces_around_numbers(s);
let mut d = d.split(' ');
let squadron: Squadron = d.next().unwrap().parse()?;
let group: u8 = d.next().unwrap().parse()?;
let unit: u8 = d.next().unwrap().parse()?;
Ok(Element {
squadron,
group,
unit,
})
}
}
fn parse_element(input: &str) -> IResult<&str, Element> {
let mut parser = tuple((
alpha1, // Squadron name
opt(multispace0), // Optional whitespace
opt(tuple((
// Optional group
opt(multispace0),
parse_group_unit,
opt(multispace0),
))),
opt(char('-')),
opt(tuple((
// Optional unit
opt(multispace0),
parse_group_unit,
opt(multispace0),
))),
));
let (remainder, (squadron_str, _, group_str, _, unit_str)) = parser(input)?;
let squadron = match squadron_str.to_lowercase().as_str() {
"enfield" => Squadron::Enfield,
"springfield" => Squadron::Springfield,
"uzi" => Squadron::Uzi,
"colt" => Squadron::Colt,
"dodge" => Squadron::Dodge,
"ford" => Squadron::Ford,
"chevy" => Squadron::Chevy,
"hornet" => Squadron::Hornet,
"pontiac" => Squadron::Pontiac,
"viper" => Squadron::Viper,
"venom" => Squadron::Venom,
"lobo" => Squadron::Lobo,
"cowboy" => Squadron::Cowboy,
"python" => Squadron::Python,
"rattler" => Squadron::Rattler,
"panther" => Squadron::Panther,
"wolf" => Squadron::Wolf,
"weasel" => Squadron::Weasel,
"wild" => Squadron::Wild,
"ninja" => Squadron::Ninja,
"jedi" => Squadron::Jedi,
"hawg" => Squadron::Hawg,
"boar" => Squadron::Boar,
"pig" => Squadron::Pig,
"tusk" => Squadron::Tusk,
"armyair" => Squadron::ArmyAir,
"apache" => Squadron::Apache,
"crow" => Squadron::Crow,
"chaos" => Squadron::Chaos,
"sioux" => Squadron::Sioux,
"gatling" => Squadron::Gatling,
"gunslinger" => Squadron::Gunslinger,
"hammerhead" => Squadron::Hammerhead,
"bootleg" => Squadron::Bootleg,
"palehorse" => Squadron::Palehorse,
"carnivore" => Squadron::Carnivore,
"saber" => Squadron::Saber,
"squid" => Squadron::Squid,
"ragin" => Squadron::Ragin,
"roman" => Squadron::Roman,
"sting" => Squadron::Sting,
"jury" => Squadron::Jury,
"joker" => Squadron::Joker,
"ram" => Squadron::Ram,
"hawk" => Squadron::Hawk,
"devil" => Squadron::Devil,
"check" => Squadron::Check,
"snake" => Squadron::Snake,
c => Squadron::Custom(c.to_string()),
};
// _ => {
// return Err(NomError::Error(nom::error::Error {
// input: squadron_str,
// code: ErrorKind::Alpha,
// }))
// }
let group = group_str.unwrap_or((None, 0, None)).1;
let unit = unit_str.unwrap_or((None, 0, None)).1;
Ok((
remainder,
Element {
squadron,
group,
unit,
},
))
}
fn parse_group_unit(input: &str) -> IResult<&str, u8> {
let parse_digit = map(
recognize(alt((
char('0'),
char('1'),
char('2'),
char('3'),
char('4'),
char('5'),
char('6'),
char('7'),
char('8'),
char('9'),
))),
|s: &str| s.parse::<u8>().unwrap(),
);
let parse_word = alt((
map(tag("one"), |_| 1),
map(tag("two"), |_| 2),
map(tag("three"), |_| 3),
map(tag("four"), |_| 4),
map(tag("five"), |_| 5),
map(tag("six"), |_| 6),
map(tag("seven"), |_| 7),
map(tag("eight"), |_| 8),
map(tag("nine"), |_| 9),
));
alt((parse_digit, parse_word))(input)
}
// #[derive(Debug)]
// pub enum Unit {
// NauticalMiles,
// Kilometers,
// }
// #[derive(Debug)]
// pub enum Call {
// RadioCheck(Awacs, Element),
// Shopping(Awacs, Element),
// LocateAirfield(Awacs, Element, String),
// LocateFriendly(Awacs, Element, Element),
// Tripwire(Awacs, Element, Option<i32>, Unit),
// }
#[derive(Debug)]
pub struct Call {
pub receiver: Receiver,
pub element: Element,
pub command: String,
}
pub fn parse_call(input: &str) -> IResult<&str, Call> {
let mut parser = tuple((parse_receiver, multispace0, parse_element));
let (command, (receiver, _, element)) = parser(input)?;
if element.group == 0 || element.unit == 0 {
return Err(NomError::Error(nom::error::Error {
input: command,
code: ErrorKind::Digit,
}));
}
Ok((
"",
Call {
receiver,
element,
command: command.to_string(),
},
))
}

1098
crates/guardian_commands/src/console.rs Normal file → Executable file

File diff suppressed because it is too large Load Diff

0
crates/guardian_commands/src/lib.rs Normal file → Executable file
View File

388
crates/guardian_commands/src/parser.rs Normal file → Executable file
View File

@@ -1,194 +1,194 @@
use nom::{
bytes::complete::tag,
error::{Error, ErrorKind, ParseError},
sequence::delimited,
IResult,
};
// from https://github.com/getreu/parse-hyperlinks/blob/5af034d14aa72ffb9e705da13bf557a564b1bebf/parse-hyperlinks/src/lib.rs#L41
fn take_until_unbalanced(
opening_bracket: char,
closing_bracket: char,
) -> impl Fn(&str) -> IResult<&str, &str> {
move |i: &str| {
let mut index = 0;
let mut bracket_counter = 0;
while let Some(n) = &i[index..].find(&[opening_bracket, closing_bracket, '\\'][..]) {
index += n;
let mut it = i[index..].chars();
match it.next() {
Some('\\') => {
// Skip the escape char `\`.
index += '\\'.len_utf8();
// Skip also the following char.
if let Some(c) = it.next() {
index += c.len_utf8();
}
}
Some(c) if c == opening_bracket => {
bracket_counter += 1;
index += opening_bracket.len_utf8();
}
Some(c) if c == closing_bracket => {
// Closing bracket.
bracket_counter -= 1;
index += closing_bracket.len_utf8();
}
// Can not happen.
_ => unreachable!(),
};
// We found the unmatched closing bracket.
if bracket_counter == -1 {
// We do not consume it.
index -= closing_bracket.len_utf8();
return Ok((&i[index..], &i[0..index]));
};
}
if bracket_counter == 0 {
Ok(("", i))
} else {
Err(nom::Err::Error(Error::from_error_kind(
i,
ErrorKind::TakeUntil,
)))
}
}
}
fn split(input: &str) -> IResult<&str, &str> {
let mut parser = delimited(tag("["), take_until_unbalanced('[', ']'), tag("]"));
parser(input)
}
fn inner_parse(input: &str, depth: usize) -> Vec<String> {
let mut output = vec![];
let start_idx = input.find('[').unwrap_or(input.len());
if start_idx > 0 && depth == 0 {
output.push(input[..start_idx].to_string());
}
if start_idx == input.len() {
return output;
}
let input = &input[start_idx..];
let splitted = split(input).unwrap();
let variants: Vec<String> = splitted
.1
.split('|')
.map(|v| v.to_string())
.collect::<Vec<_>>();
let mut temp = vec![];
let mut temp_str = String::new();
for v in &variants {
if v.contains('[') || v.contains(']') {
temp_str.push_str(v);
temp_str.push('|');
} else {
let len = temp_str.len();
if len > 0 {
temp.push(temp_str[..len - 1].to_string());
temp.push(v.to_string());
}
temp_str.clear();
}
}
let variants = if temp.is_empty() { variants } else { temp };
// check if variants need expanding
let mut temp = vec![];
for v in variants {
if v.contains('[') {
let index = v.find('[').unwrap_or(0);
let prepend = &v[..index];
let remainder = inner_parse(&v, depth + 1);
for r in &remainder {
let l = format!("{}{}", prepend, r);
temp.push(l);
}
} else {
temp.push(v);
}
}
let remainder = splitted.0;
let backup = output.clone();
output.clear();
for variant in temp {
if remainder.contains('[') {
let index = remainder.find('[').unwrap_or(0);
let prepend = &remainder[..index];
let r2 = inner_parse(remainder, depth + 1);
if !backup.is_empty() {
for o in &backup {
for r in &r2 {
let l = format!("{}{}{}{}", o, variant, prepend, r);
output.push(l);
}
}
} else {
for r in &r2 {
let l = format!("{}{}{}", variant, prepend, r);
output.push(l);
}
}
} else if !backup.is_empty() {
for o in &backup {
output.push(format!("{}{}{}", o, variant, remainder));
}
} else {
output.push(format!("{}{}", variant, remainder));
}
}
output
}
pub fn parse(input: &str) -> Vec<String> {
inner_parse(input, 0)
}
#[test]
fn parse_test() {
// This works
assert_eq!(
parse("[Hello|Goodbye], World!"),
vec!["Hello, World!", "Goodbye, World!"]
);
assert_eq!(
parse("[Hello|Goodbye], [World|Moon]!"),
vec![
"Hello, World!",
"Hello, Moon!",
"Goodbye, World!",
"Goodbye, Moon!",
]
);
assert_eq!(
parse("[[Heading|Bearing] [to|for]|Where is] that"),
vec![
"Heading to that",
"Heading for that",
"Bearing to that",
"Bearing for that",
"Where is that",
]
);
assert_eq!(
parse("set [warning|tripwire]"),
vec!["set warning", "set tripwire"]
);
}
use nom::{
bytes::complete::tag,
error::{Error, ErrorKind, ParseError},
sequence::delimited,
IResult,
};
// from https://github.com/getreu/parse-hyperlinks/blob/5af034d14aa72ffb9e705da13bf557a564b1bebf/parse-hyperlinks/src/lib.rs#L41
fn take_until_unbalanced(
opening_bracket: char,
closing_bracket: char,
) -> impl Fn(&str) -> IResult<&str, &str> {
move |i: &str| {
let mut index = 0;
let mut bracket_counter = 0;
while let Some(n) = &i[index..].find(&[opening_bracket, closing_bracket, '\\'][..]) {
index += n;
let mut it = i[index..].chars();
match it.next() {
Some('\\') => {
// Skip the escape char `\`.
index += '\\'.len_utf8();
// Skip also the following char.
if let Some(c) = it.next() {
index += c.len_utf8();
}
}
Some(c) if c == opening_bracket => {
bracket_counter += 1;
index += opening_bracket.len_utf8();
}
Some(c) if c == closing_bracket => {
// Closing bracket.
bracket_counter -= 1;
index += closing_bracket.len_utf8();
}
// Can not happen.
_ => unreachable!(),
};
// We found the unmatched closing bracket.
if bracket_counter == -1 {
// We do not consume it.
index -= closing_bracket.len_utf8();
return Ok((&i[index..], &i[0..index]));
};
}
if bracket_counter == 0 {
Ok(("", i))
} else {
Err(nom::Err::Error(Error::from_error_kind(
i,
ErrorKind::TakeUntil,
)))
}
}
}
fn split(input: &str) -> IResult<&str, &str> {
let mut parser = delimited(tag("["), take_until_unbalanced('[', ']'), tag("]"));
parser(input)
}
fn inner_parse(input: &str, depth: usize) -> Vec<String> {
let mut output = vec![];
let start_idx = input.find('[').unwrap_or(input.len());
if start_idx > 0 && depth == 0 {
output.push(input[..start_idx].to_string());
}
if start_idx == input.len() {
return output;
}
let input = &input[start_idx..];
let splitted = split(input).unwrap();
let variants: Vec<String> = splitted
.1
.split('|')
.map(|v| v.to_string())
.collect::<Vec<_>>();
let mut temp = vec![];
let mut temp_str = String::new();
for v in &variants {
if v.contains('[') || v.contains(']') {
temp_str.push_str(v);
temp_str.push('|');
} else {
let len = temp_str.len();
if len > 0 {
temp.push(temp_str[..len - 1].to_string());
temp.push(v.to_string());
}
temp_str.clear();
}
}
let variants = if temp.is_empty() { variants } else { temp };
// check if variants need expanding
let mut temp = vec![];
for v in variants {
if v.contains('[') {
let index = v.find('[').unwrap_or(0);
let prepend = &v[..index];
let remainder = inner_parse(&v, depth + 1);
for r in &remainder {
let l = format!("{}{}", prepend, r);
temp.push(l);
}
} else {
temp.push(v);
}
}
let remainder = splitted.0;
let backup = output.clone();
output.clear();
for variant in temp {
if remainder.contains('[') {
let index = remainder.find('[').unwrap_or(0);
let prepend = &remainder[..index];
let r2 = inner_parse(remainder, depth + 1);
if !backup.is_empty() {
for o in &backup {
for r in &r2 {
let l = format!("{}{}{}{}", o, variant, prepend, r);
output.push(l);
}
}
} else {
for r in &r2 {
let l = format!("{}{}{}", variant, prepend, r);
output.push(l);
}
}
} else if !backup.is_empty() {
for o in &backup {
output.push(format!("{}{}{}", o, variant, remainder));
}
} else {
output.push(format!("{}{}", variant, remainder));
}
}
output
}
pub fn parse(input: &str) -> Vec<String> {
inner_parse(input, 0)
}
#[test]
fn parse_test() {
// This works
assert_eq!(
parse("[Hello|Goodbye], World!"),
vec!["Hello, World!", "Goodbye, World!"]
);
assert_eq!(
parse("[Hello|Goodbye], [World|Moon]!"),
vec![
"Hello, World!",
"Hello, Moon!",
"Goodbye, World!",
"Goodbye, Moon!",
]
);
assert_eq!(
parse("[[Heading|Bearing] [to|for]|Where is] that"),
vec![
"Heading to that",
"Heading for that",
"Bearing to that",
"Bearing for that",
"Where is that",
]
);
assert_eq!(
parse("set [warning|tripwire]"),
vec!["set warning", "set tripwire"]
);
}