use std::ops::Deref;
use euroscope_sys::{EsHandle, PluginPtr};
use crate::{Context, SectorElementType, utils::cstr_lossy};
macro_rules! owned {
($owned:ident, $handle:ident, $free:ident) => {
#[doc = concat!("An owned [`", stringify!($handle), "`](crate::", stringify!($handle),
") (a heap copy). Frees on drop; derefs to the borrowed handle.")]
pub struct $owned {
inner: crate::$handle<'static>,
}
impl $owned {
#[allow(dead_code)]
pub(crate) fn from_owned(raw: EsHandle) -> Option<Self> {
(!raw.is_null()).then(|| Self {
inner: crate::$handle::from_raw(raw),
})
}
}
impl Deref for $owned {
type Target = crate::$handle<'static>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[expect(clippy::missing_trait_methods, reason = "We don't need pin_drop")]
impl Drop for $owned {
fn drop(&mut self) {
unsafe { euroscope_sys::$free(self.inner.as_ptr()) }
}
}
};
}
macro_rules! iter {
($iter:ident, $owned:ident, $handle:ident, $free:ident, $first:ident, $next:ident) => {
#[doc = concat!("Iterator over all `", stringify!($handle),
"`s, yielding owned handles.")]
pub struct $iter {
plugin: PluginPtr,
next: Option<EsHandle>,
}
impl $iter {
pub(crate) fn new(plugin: PluginPtr) -> Self {
let first = unsafe { euroscope_sys::$first(plugin) };
Self {
plugin,
next: (!first.is_null()).then_some(first),
}
}
}
#[expect(
clippy::missing_trait_methods,
reason = "We don't need the extra methods for this type"
)]
impl Iterator for $iter {
type Item = $owned;
fn next(&mut self) -> Option<$owned> {
let cur = self.next.take()?;
let following = unsafe { euroscope_sys::$next(self.plugin, cur) };
self.next = (!following.is_null()).then_some(following);
Some($owned {
inner: crate::$handle::from_raw(cur),
})
}
}
#[expect(clippy::missing_trait_methods, reason = "We don't need pin_drop")]
impl Drop for $iter {
fn drop(&mut self) {
if let Some(p) = self.next.take() {
unsafe { euroscope_sys::$free(p) }
}
}
}
};
}
owned!(OwnedFlightPlan, FlightPlan, es_flightplan_free);
iter!(
FlightPlans,
OwnedFlightPlan,
FlightPlan,
es_flightplan_free,
es_plugin_flightplan_select_first,
es_plugin_flightplan_select_next
);
owned!(OwnedRadarTarget, RadarTarget, es_radartarget_free);
iter!(
RadarTargets,
OwnedRadarTarget,
RadarTarget,
es_radartarget_free,
es_plugin_radartarget_select_first,
es_plugin_radartarget_select_next
);
owned!(OwnedController, Controller, es_controller_free);
iter!(
Controllers,
OwnedController,
Controller,
es_controller_free,
es_plugin_controller_select_first,
es_plugin_controller_select_next
);
owned!(OwnedFlightPlanList, FlightPlanList, es_fplist_free);
owned!(
OwnedGroundToAirChannel,
GroundToAirChannel,
es_gtachannel_free
);
iter!(
GroundToAirChannels,
OwnedGroundToAirChannel,
GroundToAirChannel,
es_gtachannel_free,
es_plugin_gtachannel_select_first,
es_plugin_gtachannel_select_next
);
owned!(OwnedSectorElement, SectorElement, es_sectorelement_free);
pub struct SectorElements {
plugin: PluginPtr,
element_type: i32,
next: Option<EsHandle>,
}
impl SectorElements {
pub(crate) fn new(plugin: PluginPtr, element_type: i32) -> Self {
let first =
unsafe { euroscope_sys::es_plugin_sectorelement_select_first(plugin, element_type) };
Self {
plugin,
element_type,
next: (!first.is_null()).then_some(first),
}
}
}
#[expect(
clippy::missing_trait_methods,
reason = "We don't need the extra methods for this type"
)]
impl Iterator for SectorElements {
type Item = OwnedSectorElement;
fn next(&mut self) -> Option<OwnedSectorElement> {
let cur = self.next.take()?;
let following = unsafe {
euroscope_sys::es_plugin_sectorelement_select_next(self.plugin, cur, self.element_type)
};
self.next = (!following.is_null()).then_some(following);
Some(OwnedSectorElement {
inner: crate::SectorElement::from_raw(cur),
})
}
}
#[expect(clippy::missing_trait_methods, reason = "We don't need pin_drop")]
impl Drop for SectorElements {
fn drop(&mut self) {
if let Some(p) = self.next.take() {
unsafe { euroscope_sys::es_sectorelement_free(p) }
}
}
}
owned!(
OwnedRadarTargetPosition,
RadarTargetPosition,
es_rtposdata_free
);
pub struct PositionHistory {
radar_target: EsHandle,
next: Option<EsHandle>,
}
impl PositionHistory {
pub(crate) fn new(radar_target: EsHandle) -> Self {
let first = unsafe { euroscope_sys::es_radartarget_current_position(radar_target) };
Self {
radar_target,
next: (!first.is_null()).then_some(first),
}
}
}
#[expect(
clippy::missing_trait_methods,
reason = "We don't need the extra methods for this type"
)]
impl Iterator for PositionHistory {
type Item = OwnedRadarTargetPosition;
fn next(&mut self) -> Option<OwnedRadarTargetPosition> {
let cur = self.next.take()?;
let following =
unsafe { euroscope_sys::es_radartarget_previous_position(self.radar_target, cur) };
self.next = (!following.is_null()).then_some(following);
Some(OwnedRadarTargetPosition {
inner: crate::RadarTargetPosition::from_raw(cur),
})
}
}
#[expect(clippy::missing_trait_methods, reason = "We don't need pin_drop")]
impl Drop for PositionHistory {
fn drop(&mut self) {
if let Some(p) = self.next.take() {
unsafe { euroscope_sys::es_rtposdata_free(p) }
}
}
}
impl Context {
pub fn flight_plans(&self) -> FlightPlans {
FlightPlans::new(self.as_ptr())
}
pub fn radar_targets(&self) -> RadarTargets {
RadarTargets::new(self.as_ptr())
}
pub fn controllers(&self) -> Controllers {
Controllers::new(self.as_ptr())
}
pub fn ground_to_air_channels(&self) -> GroundToAirChannels {
GroundToAirChannels::new(self.as_ptr())
}
pub fn sector_file_elements(&self, element_type: SectorElementType) -> SectorElements {
SectorElements::new(self.as_ptr(), element_type.to_raw())
}
pub fn flight_plan(&self, callsign: &str) -> Option<OwnedFlightPlan> {
OwnedFlightPlan::from_owned(unsafe {
euroscope_sys::es_plugin_flightplan_select(self.as_ptr(), cstr_lossy(callsign).as_ptr())
})
}
pub fn radar_target(&self, callsign: &str) -> Option<OwnedRadarTarget> {
OwnedRadarTarget::from_owned(unsafe {
euroscope_sys::es_plugin_radartarget_select(
self.as_ptr(),
cstr_lossy(callsign).as_ptr(),
)
})
}
pub fn controller(&self, callsign: &str) -> Option<OwnedController> {
OwnedController::from_owned(unsafe {
euroscope_sys::es_plugin_controller_select(self.as_ptr(), cstr_lossy(callsign).as_ptr())
})
}
pub fn controller_by_position_id(&self, position_id: &str) -> Option<OwnedController> {
OwnedController::from_owned(unsafe {
euroscope_sys::es_plugin_controller_select_by_position_id(
self.as_ptr(),
cstr_lossy(position_id).as_ptr(),
)
})
}
pub fn controller_myself(&self) -> Option<OwnedController> {
OwnedController::from_owned(unsafe {
euroscope_sys::es_plugin_controller_myself(self.as_ptr())
})
}
pub fn selected_flight_plan(&self) -> Option<OwnedFlightPlan> {
OwnedFlightPlan::from_owned(unsafe {
euroscope_sys::es_plugin_flightplan_select_asel(self.as_ptr())
})
}
pub fn selected_radar_target(&self) -> Option<OwnedRadarTarget> {
OwnedRadarTarget::from_owned(unsafe {
euroscope_sys::es_plugin_radartarget_select_asel(self.as_ptr())
})
}
pub fn set_asel_flight_plan(&self, flight_plan: crate::FlightPlan<'_>) {
unsafe { euroscope_sys::es_plugin_set_asel_flightplan(self.as_ptr(), flight_plan.as_ptr()) }
}
pub fn set_asel_radar_target(&self, radar_target: crate::RadarTarget<'_>) {
unsafe {
euroscope_sys::es_plugin_set_asel_radartarget(self.as_ptr(), radar_target.as_ptr());
}
}
pub fn register_fp_list(&self, name: &str) -> Option<OwnedFlightPlanList> {
OwnedFlightPlanList::from_owned(unsafe {
euroscope_sys::es_plugin_register_fp_list(self.as_ptr(), cstr_lossy(name).as_ptr())
})
}
pub fn transition_altitude(&self) -> i32 {
unsafe { euroscope_sys::es_plugin_transition_altitude(self.as_ptr()) }
}
pub fn get_setting(&self, key: &str) -> Option<String> {
unsafe {
let ptr = euroscope_sys::es_plugin_get_data_from_settings(
self.as_ptr(),
cstr_lossy(key).as_ptr(),
);
let s = crate::utils::cstr(ptr);
(!s.is_empty()).then(|| s.to_owned())
}
}
pub fn save_setting(&self, key: &str, description: &str, value: &str) {
unsafe {
euroscope_sys::es_plugin_save_data_to_settings(
self.as_ptr(),
cstr_lossy(key).as_ptr(),
cstr_lossy(description).as_ptr(),
cstr_lossy(value).as_ptr(),
);
}
}
}