mod monitoring;
mod types;
use std::sync::Arc;
use derive_more::Debug;
use futures::{Stream, StreamExt};
use tokio_util::sync::CancellationToken;
use tracing::warn;
pub(crate) use types::{ActiveConnectionParams, LiveActiveConnectionParams};
pub use types::{ActiveConnectionStateChangedEvent, VpnConnectionStateChangedEvent};
use wayle_core::{Property, unwrap_dbus};
use wayle_traits::{ModelMonitoring, Reactive};
use zbus::{Connection, zvariant::OwnedObjectPath};
use crate::{
error::Error,
proxy::active_connection::{ConnectionActiveProxy, vpn::VPNConnectionProxy},
types::{
flags::NMActivationStateFlags,
states::{
NMActiveConnectionState, NMActiveConnectionStateReason, NMVpnConnectionState,
NMVpnConnectionStateReason,
},
},
};
#[derive(Debug, Clone)]
pub struct ActiveConnection {
#[debug(skip)]
pub(crate) zbus_connection: Connection,
#[debug(skip)]
pub(crate) cancellation_token: Option<CancellationToken>,
pub object_path: OwnedObjectPath,
pub connection_path: Property<OwnedObjectPath>,
pub specific_object: Property<OwnedObjectPath>,
pub id: Property<String>,
pub uuid: Property<String>,
pub type_: Property<String>,
pub devices: Property<Vec<OwnedObjectPath>>,
pub state: Property<NMActiveConnectionState>,
pub state_flags: Property<NMActivationStateFlags>,
pub default: Property<bool>,
pub ip4_config: Property<OwnedObjectPath>,
pub dhcp4_config: Property<OwnedObjectPath>,
pub default6: Property<bool>,
pub ip6_config: Property<OwnedObjectPath>,
pub dhcp6_config: Property<OwnedObjectPath>,
pub vpn: Property<bool>,
pub controller: Property<OwnedObjectPath>,
}
impl Reactive for ActiveConnection {
type Context<'a> = ActiveConnectionParams<'a>;
type LiveContext<'a> = LiveActiveConnectionParams<'a>;
type Error = Error;
async fn get(params: Self::Context<'_>) -> Result<Self, Self::Error> {
Self::from_path(params.connection, params.path, None).await
}
async fn get_live(params: Self::LiveContext<'_>) -> Result<Arc<Self>, Self::Error> {
let active_connection = Self::from_path(
params.connection,
params.path.clone(),
Some(params.cancellation_token.child_token()),
)
.await?;
let active_connection = Arc::new(active_connection);
active_connection.clone().start_monitoring().await?;
Ok(active_connection)
}
}
impl ActiveConnection {
async fn from_path(
connection: &Connection,
path: OwnedObjectPath,
cancellation_token: Option<CancellationToken>,
) -> Result<Self, Error> {
let connection_proxy = ConnectionActiveProxy::new(connection, &path).await?;
if connection_proxy.connection().await.is_err() {
warn!(
"Active Connection at path '{}' does not exist.",
path.clone()
);
return Err(Error::ObjectNotFound(path.clone()));
}
let (
connection_path,
specific_object,
id,
uuid,
type_,
devices,
state,
state_flags,
default,
ip4_config,
dhcp4_config,
default6,
ip6_config,
dhcp6_config,
vpn,
controller,
) = tokio::join!(
connection_proxy.connection(),
connection_proxy.specific_object(),
connection_proxy.id(),
connection_proxy.uuid(),
connection_proxy.type_(),
connection_proxy.devices(),
connection_proxy.state(),
connection_proxy.state_flags(),
connection_proxy.default(),
connection_proxy.ip4_config(),
connection_proxy.dhcp4_config(),
connection_proxy.default6(),
connection_proxy.ip6_config(),
connection_proxy.dhcp6_config(),
connection_proxy.vpn(),
connection_proxy.controller(),
);
let connection_path = unwrap_dbus!(connection_path, path);
let specific_object = unwrap_dbus!(specific_object, path);
let id = unwrap_dbus!(id, path);
let uuid = unwrap_dbus!(uuid, path);
let type_ = unwrap_dbus!(type_, path);
let devices = unwrap_dbus!(devices, path);
let state = NMActiveConnectionState::from_u32(unwrap_dbus!(state, path));
let state_flags =
NMActivationStateFlags::from_bits_truncate(unwrap_dbus!(state_flags, path));
let default = unwrap_dbus!(default, path);
let ip4_config = unwrap_dbus!(ip4_config, path);
let dhcp4_config = unwrap_dbus!(dhcp4_config, path);
let default6 = unwrap_dbus!(default6, path);
let ip6_config = unwrap_dbus!(ip6_config, path);
let dhcp6_config = unwrap_dbus!(dhcp6_config, path);
let vpn = unwrap_dbus!(vpn, path);
let controller = unwrap_dbus!(controller, path);
Ok(Self {
connection_path: Property::new(connection_path),
specific_object: Property::new(specific_object),
id: Property::new(id),
uuid: Property::new(uuid),
type_: Property::new(type_),
devices: Property::new(devices),
state: Property::new(state),
state_flags: Property::new(state_flags),
default: Property::new(default),
ip4_config: Property::new(ip4_config),
dhcp4_config: Property::new(dhcp4_config),
default6: Property::new(default6),
ip6_config: Property::new(ip6_config),
dhcp6_config: Property::new(dhcp6_config),
vpn: Property::new(vpn),
controller: Property::new(controller),
zbus_connection: connection.clone(),
object_path: path,
cancellation_token,
})
}
pub async fn active_connection_state_changed_signal(
&self,
) -> Result<impl Stream<Item = ActiveConnectionStateChangedEvent>, Error> {
let proxy = ConnectionActiveProxy::new(&self.zbus_connection, &self.object_path).await?;
let stream = proxy.receive_active_connection_state_changed().await?;
Ok(stream.filter_map(|signal| async move {
signal
.args()
.ok()
.map(|args| ActiveConnectionStateChangedEvent {
state: NMActiveConnectionState::from_u32(args.state),
reason: NMActiveConnectionStateReason::from_u32(args.reason),
})
}))
}
pub async fn vpn_connection_state_changed_signal(
&self,
) -> Result<impl Stream<Item = VpnConnectionStateChangedEvent>, Error> {
let proxy = VPNConnectionProxy::new(&self.zbus_connection, &self.object_path).await?;
let stream = proxy.receive_vpn_connection_state_changed().await?;
Ok(stream.filter_map(|signal| async move {
signal
.args()
.ok()
.map(|args| VpnConnectionStateChangedEvent {
state: NMVpnConnectionState::from_u32(args.state),
reason: NMVpnConnectionStateReason::from_u32(args.reason),
})
}))
}
}