use serde::{Deserialize, Serialize};
use crate::model::{Area, GeoPoint};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AirspaceKind {
Controlled(ControlledClass),
Restrictive(RestrictiveKind),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ControlledClass {
B,
C,
D,
E,
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum RestrictiveKind {
Prohibited,
Restricted,
Moa,
Alert,
Warning,
Danger,
Training,
Other(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AltitudeDatum {
Ground,
Msl,
Agl,
FlightLevel,
Unlimited,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AltitudeLimit {
pub value_ft: Option<f64>,
pub datum: AltitudeDatum,
}
impl AltitudeLimit {
pub fn new(value_ft: Option<f64>, datum: AltitudeDatum) -> Self {
Self { value_ft, datum }
}
pub fn ground() -> Self {
Self {
value_ft: None,
datum: AltitudeDatum::Ground,
}
}
pub fn unlimited() -> Self {
Self {
value_ft: None,
datum: AltitudeDatum::Unlimited,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Airspace {
pub kind: AirspaceKind,
pub designator: String,
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub center_ident: Option<String>,
pub lower: AltitudeLimit,
pub upper: AltitudeLimit,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bounds: Option<Area>,
}
impl Airspace {
pub fn new(kind: AirspaceKind, designator: impl Into<String>) -> Self {
Self {
kind,
designator: designator.into(),
name: None,
center_ident: None,
lower: AltitudeLimit::ground(),
upper: AltitudeLimit::unlimited(),
bounds: None,
}
}
#[must_use]
pub fn with_name(mut self, name: Option<String>) -> Self {
self.name = name;
self
}
#[must_use]
pub fn with_center_ident(mut self, center_ident: Option<String>) -> Self {
self.center_ident = center_ident;
self
}
#[must_use]
pub fn with_lower(mut self, lower: AltitudeLimit) -> Self {
self.lower = lower;
self
}
#[must_use]
pub fn with_upper(mut self, upper: AltitudeLimit) -> Self {
self.upper = upper;
self
}
#[must_use]
pub fn with_bounds(mut self, bounds: Option<Area>) -> Self {
self.bounds = bounds;
self
}
#[must_use]
pub fn bounds_may_contain(&self, point: GeoPoint) -> Option<bool> {
self.bounds.as_ref().and_then(|area| area.contains(point))
}
}
#[cfg(test)]
mod tests;