Compare commits

..

1 commit

Author SHA1 Message Date
f963d9c0b3
WIP: Try using xmpp.send_message in another select! branch
Requires tokio-xmpp's xmpp-send-buffered-message branch (currently
bc0b1684e), available in the repo.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2022-07-28 17:55:42 +02:00
7 changed files with 303 additions and 479 deletions

View file

@ -7,8 +7,21 @@ edition = "2021"
categories = ["gateway", "bridge", "transport", "modemmanager", "xmpp", "jabber"] categories = ["gateway", "bridge", "transport", "modemmanager", "xmpp", "jabber"]
[dependencies] [dependencies]
tokio = { version = "1", features = [ "macros", "rt-multi-thread" ] } async-trait = "0.1.56"
mmdbus = "1.18.6"
dbus-tokio = "0.7.5"
tokio = { version = "1.19.2", features = [ "time", "macros", "rt-multi-thread" ] }
futures = "0.3" futures = "0.3"
futures-util = "0.3"
tokio-xmpp = "3.2"
xmpp = "0.3"
xmpp-parsers = "0.19"
log = "0.4" log = "0.4"
pretty_env_logger = "0.5" env_logger = "0.8"
zbus = "3.14.1"
[patch.crates-io]
jid = { path = "../xmpp-rs/jid" }
minidom = { path = "../xmpp-rs/minidom" }
tokio-xmpp = { path = "../xmpp-rs/tokio-xmpp" }
xmpp = { path = "../xmpp-rs/xmpp" }
xmpp-parsers = { path = "../xmpp-rs/parsers" }

View file

@ -18,17 +18,22 @@
use std::convert::From; use std::convert::From;
use std::error::Error as StdError; use std::error::Error as StdError;
use mmdbus::dbus;
use xmpp_parsers::JidParseError;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
Zbus(zbus::Error), DBusError(dbus::Error),
ZbusFdo(zbus::fdo::Error), InvalidArgs,
JidParseError(JidParseError),
} }
impl StdError for Error { impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> { fn cause(&self) -> Option<&dyn StdError> {
match self { match self {
Error::Zbus(e) => Some(e), Error::DBusError(e) => Some(e),
Error::ZbusFdo(e) => Some(e), Error::InvalidArgs => None,
Error::JidParseError(e) => Some(e),
} }
} }
} }
@ -36,20 +41,21 @@ impl StdError for Error {
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Error::Zbus(e) => write!(fmt, "zbus error: {}", e), Error::DBusError(e) => write!(fmt, "dbus error: {}", e),
Error::ZbusFdo(e) => write!(fmt, "zbus fdo error: {}", e), Error::InvalidArgs => write!(fmt, "invalid arguments"),
Error::JidParseError(e) => write!(fmt, "jid parse error: {}", e),
} }
} }
} }
impl From<zbus::Error> for Error { impl From<dbus::Error> for Error {
fn from(err: zbus::Error) -> Error { fn from(err: dbus::Error) -> Error {
Error::Zbus(err) Error::DBusError(err)
} }
} }
impl From<zbus::fdo::Error> for Error { impl From<JidParseError> for Error {
fn from(err: zbus::fdo::Error) -> Error { fn from(err: JidParseError) -> Error {
Error::ZbusFdo(err) Error::JidParseError(err)
} }
} }

View file

@ -13,45 +13,227 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
mod error; pub mod error;
mod modem; pub mod mm;
use crate::error::Error; use error::Error;
use crate::modem::ModemManager; use mm::modem::Modem as ModemAccess;
use futures::{StreamExt, future::join_all}; use std::fmt;
use std::sync::Arc;
use std::str::FromStr;
use std::env::args;
#[tokio::main] use env_logger;
async fn main() -> Result<(), Error> {
pretty_env_logger::init();
let mut join_handlers = vec![]; use tokio::{
let manager = ModemManager::connect().await?; task::JoinHandle,
for modem in manager { time::Duration,
println!("FOO0: {:?}", modem); };
println!("FOO0: {:?}", modem.foo().manufacturer().await?);
println!("FOO0: {:?}", modem.foo().model().await?);
println!("FOO1: {:?}", modem.messaging().list().await?); use mmdbus::{
dbus::{
// message::MatchRule,
nonblock::{
SyncConnection, Proxy,
stdintf::org_freedesktop_dbus::ObjectManager,
},
strings::{Path as DBusPath},
},
};
let mut sms_added_stream = modem.messaging().receive_added().await?; use dbus_tokio::connection;
let mut call_added_stream = modem.voice().receive_call_added().await?;
let handle_sms = tokio::spawn(async move { use xmpp::{ClientBuilder, ClientType};
while let Some(sms) = sms_added_stream.next().await { use xmpp_parsers::{
println!("BAR0: {:?}", sms); Jid, BareJid,
message::MessageType,
};
// The following source has been used as an example:
// https://github.com/soerenmeier/linux-info/blob/master/src/network/modem_manager.rs
const MM_NAME: &str = "org.freedesktop.ModemManager1";
const MM_PATH: &str = "/org/freedesktop/ModemManager1";
const TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Clone)]
pub struct DBus {
pub conn: Arc<SyncConnection>,
pub handle: Arc<JoinHandle<()>>,
} }
impl DBus {
pub fn connect() -> Result<Self, Error> {
let (resource, conn) = connection::new_system_sync()?;
let handle: JoinHandle<()> = tokio::spawn(async {
let err = resource.await;
panic!("Lost connection to D-Bus: {}", err);
}); });
let handle_call = tokio::spawn(async move {
while let Some(call) = call_added_stream.next().await { Ok(Self { conn, handle: Arc::new(handle) })
println!("MEH0: {:?}", call);
}
});
join_handlers.push(handle_sms);
join_handlers.push(handle_call);
} }
let _ = join_all(join_handlers).await; pub fn proxy<'a, 'b, P>(&'b self, path: P) -> Proxy<'a, &'b SyncConnection>
where
P: Into<DBusPath<'a>>,
{
Proxy::new(MM_NAME, path, TIMEOUT, &*self.conn)
}
}
#[derive(Clone)]
pub struct ModemManager {
dbus: DBus,
}
impl ModemManager {
pub fn connect() -> Result<Self, Error> {
DBus::connect().map(|dbus| Self { dbus })
}
pub async fn modems(&self) -> Result<Vec<Modem>, Error> {
let objects = self.dbus.proxy(MM_PATH).get_managed_objects().await?;
let modems = objects
.into_iter()
.map(|(path, _)| {
Modem {
dbus: self.dbus.clone(),
path,
}
})
.collect();
Ok(modems)
}
}
#[derive(Clone)]
pub struct Modem {
dbus: DBus,
pub path: DBusPath<'static>,
}
impl fmt::Debug for Modem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Modem {{ {}, {:?}, .. }}", self.path, "1234")
}
}
impl Modem {
pub async fn enabled(&self) -> Result<bool, Error> {
let state: ModemState = ModemAccess::state(&self.dbus.proxy(&self.path)).await.map(Into::into)?;
Ok(match state {
ModemState::Enabling
| ModemState::Enabled
| ModemState::Searching
| ModemState::Registered
| ModemState::Connecting
| ModemState::Connected => true,
_ => false,
})
}
pub async fn model(&self) -> Result<String, Error> {
ModemAccess::model(&self.dbus.proxy(&self.path)).await.map_err(Error::DBusError)
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModemState {
/// The modem is unusable.
Failed = -1,
/// State unknown or not reportable.
Unknown = 0,
/// The modem is currently being initialized.
Initializing = 1,
/// The modem needs to be unlocked.
Locked = 2,
/// The modem is not enabled and is powered down.
Disabled = 3,
/// The modem is currently transitioning to the MM_MODEM_STATE_DISABLED
/// state.
Disabling = 4,
/// The modem is currently transitioning to the MM_MODEM_STATE_ENABLED
/// state.
Enabling = 5,
/// The modem is enabled and powered on but not registered with a network
/// provider and not available for data connections.
Enabled = 6,
/// The modem is searching for a network provider to register with.
Searching = 7,
/// The modem is registered with a network provider, and data connections
/// and messaging may be available for use.
Registered = 8,
/// The modem is disconnecting and deactivating the last active packet data
/// bearer. This state will not be entered if more than one packet data
/// bearer is active and one of the active bearers is deactivated.
Disconnecting = 9,
/// The modem is activating and connecting the first packet data bearer.
/// Subsequent bearer activations when another bearer is already active
/// do not cause this state to be entered.
Connecting = 10,
/// One or more packet data bearers is active and connected.
Connected = 11,
}
impl From<i32> for ModemState {
fn from(num: i32) -> Self {
if num < -1 || num > 11 {
Self::Unknown
} else {
unsafe { *(&num as *const i32 as *const Self) }
}
}
}
async fn print_foo(mm: ModemManager) -> Result<(), Error> {
let modems = mm.modems().await?;
println!("Modems: {:?}", modems);
for modem in modems {
println!("Modem: {:?}", modem);
println!("Enabled: {:?}", modem.enabled().await);
println!("Model: {:?}", modem.model().await);
}
Ok(()) Ok(())
} }
#[tokio::main]
pub async fn main() -> Result<(), Error> {
env_logger::init();
let args: Vec<String> = args().collect();
if args.len() != 3 {
println!("Usage: {} <jid> <password>", args[0]);
return Err(Error::InvalidArgs)
}
let mut interval = tokio::time::interval(Duration::from_secs(5));
// XMPP
let jid_str = args[1].as_str();
let jid = Jid::Bare(BareJid::from_str(jid_str).unwrap());
let passwd = args[2].as_str();
// Client instance
let mut xmpp = ClientBuilder::new(jid_str, passwd)
.set_client(ClientType::Bot, "gateway_mm")
.set_website("https://codeberg.org/pep./gateway_mm")
.build()
.unwrap();
loop {
tokio::select! {
events = xmpp.wait_for_events() => {
println!("Events: {:?}", events);
},
_ = interval.tick() => {
println!("Foo1");
// Uncomment to have tokio-xmpp stall right after successful bind.
// xmpp.send_message(jid.clone(), MessageType::Chat, "en", "Bar").await;
},
}
}
}

16
src/mm/mod.rs Normal file
View file

@ -0,0 +1,16 @@
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
// for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub mod modem;

42
src/mm/modem.rs Normal file
View file

@ -0,0 +1,42 @@
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
// for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::ops::Deref;
use std::marker::Sync;
use async_trait::async_trait;
use mmdbus::dbus::{nonblock, Error as DBusError};
#[async_trait]
pub trait Modem {
async fn enable(&self, enable: bool) -> Result<(), DBusError>;
async fn state(&self) -> Result<i32, DBusError>;
async fn model(&self) -> Result<String, DBusError>;
}
#[async_trait]
impl<'a, T: nonblock::NonblockReply, C: Deref<Target=T> + Sync> Modem for nonblock::Proxy<'a, C> {
async fn enable(&self, enable: bool) -> Result<(), DBusError> {
self.method_call("org.freedesktop.ModemManager1.Modem", "Enable", (enable,)).await
}
async fn state(&self) -> Result<i32, DBusError> {
<Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.ModemManager1.Modem", "State").await
}
async fn model(&self) -> Result<String, DBusError> {
<Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.ModemManager1.Modem", "Model").await
}
}

View file

@ -1,320 +0,0 @@
//! DBus interface proxies for:
//!
//! - `org.freedesktop.ModemManager1.Modem`,
//! - `org.freedesktop.ModemManager1.Modem.Messaging`,
//! - `org.freedesktop.ModemManager1.Modem.Simple`,
//! - `org.freedesktop.ModemManager1.Modem.Voice`
//!
//! This code was generated by `zbus-xmlgen` `3.1.1` from DBus introspection data.
//! Source: `ModemManager1.xml`.
use zbus::dbus_proxy;
#[dbus_proxy(
interface = "org.freedesktop.ModemManager1.Modem.Messaging",
default_service = "org.freedesktop.ModemManager1",
assume_defaults = true
)]
trait Messaging {
/// Create method
fn create(
&self,
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// Delete method
fn delete(&self, path: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// List method
fn list(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// Added signal
#[dbus_proxy(signal)]
fn added(&self, path: zbus::zvariant::ObjectPath<'_>, received: bool) -> zbus::Result<()>;
/// Deleted signal
#[dbus_proxy(signal)]
fn deleted(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// DefaultStorage property
#[dbus_proxy(property)]
fn default_storage(&self) -> zbus::Result<u32>;
/// Messages property
#[dbus_proxy(property)]
fn messages(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// SupportedStorages property
#[dbus_proxy(property)]
fn supported_storages(&self) -> zbus::Result<Vec<u32>>;
}
#[dbus_proxy(
interface = "org.freedesktop.ModemManager1.Modem",
default_service = "org.freedesktop.ModemManager1",
assume_defaults = true
)]
trait Modem {
/// Command method
fn command(&self, cmd: &str, timeout: u32) -> zbus::Result<String>;
/// CreateBearer method
fn create_bearer(
&self,
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// DeleteBearer method
fn delete_bearer(&self, bearer: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// Enable method
fn enable(&self, enable: bool) -> zbus::Result<()>;
/// FactoryReset method
fn factory_reset(&self, code: &str) -> zbus::Result<()>;
/// GetCellInfo method
fn get_cell_info(
&self,
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
/// ListBearers method
fn list_bearers(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// Reset method
fn reset(&self) -> zbus::Result<()>;
/// SetCurrentBands method
fn set_current_bands(&self, bands: &[u32]) -> zbus::Result<()>;
/// SetCurrentCapabilities method
fn set_current_capabilities(&self, capabilities: u32) -> zbus::Result<()>;
/// SetCurrentModes method
fn set_current_modes(&self, modes: &(u32, u32)) -> zbus::Result<()>;
/// SetPowerState method
fn set_power_state(&self, state: u32) -> zbus::Result<()>;
/// SetPrimarySimSlot method
fn set_primary_sim_slot(&self, sim_slot: u32) -> zbus::Result<()>;
// TODO: This fails to compile: duplicate definitions for `receive_state_changed`
// /// StateChanged signal
// #[dbus_proxy(signal)]
// fn state_changed(&self, old: i32, new: i32, reason: u32) -> zbus::Result<()>;
/// AccessTechnologies property
#[dbus_proxy(property)]
fn access_technologies(&self) -> zbus::Result<u32>;
/// Bearers property
#[dbus_proxy(property)]
fn bearers(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// CarrierConfiguration property
#[dbus_proxy(property)]
fn carrier_configuration(&self) -> zbus::Result<String>;
/// CarrierConfigurationRevision property
#[dbus_proxy(property)]
fn carrier_configuration_revision(&self) -> zbus::Result<String>;
/// CurrentBands property
#[dbus_proxy(property)]
fn current_bands(&self) -> zbus::Result<Vec<u32>>;
/// CurrentCapabilities property
#[dbus_proxy(property)]
fn current_capabilities(&self) -> zbus::Result<u32>;
/// CurrentModes property
#[dbus_proxy(property)]
fn current_modes(&self) -> zbus::Result<(u32, u32)>;
/// Device property
#[dbus_proxy(property)]
fn device(&self) -> zbus::Result<String>;
/// DeviceIdentifier property
#[dbus_proxy(property)]
fn device_identifier(&self) -> zbus::Result<String>;
/// Drivers property
#[dbus_proxy(property)]
fn drivers(&self) -> zbus::Result<Vec<String>>;
/// EquipmentIdentifier property
#[dbus_proxy(property)]
fn equipment_identifier(&self) -> zbus::Result<String>;
/// HardwareRevision property
#[dbus_proxy(property)]
fn hardware_revision(&self) -> zbus::Result<String>;
/// Manufacturer property
#[dbus_proxy(property)]
fn manufacturer(&self) -> zbus::Result<String>;
/// MaxActiveBearers property
#[dbus_proxy(property)]
fn max_active_bearers(&self) -> zbus::Result<u32>;
/// MaxActiveMultiplexedBearers property
#[dbus_proxy(property)]
fn max_active_multiplexed_bearers(&self) -> zbus::Result<u32>;
/// MaxBearers property
#[dbus_proxy(property)]
fn max_bearers(&self) -> zbus::Result<u32>;
/// Model property
#[dbus_proxy(property)]
fn model(&self) -> zbus::Result<String>;
/// OwnNumbers property
#[dbus_proxy(property)]
fn own_numbers(&self) -> zbus::Result<Vec<String>>;
/// Plugin property
#[dbus_proxy(property)]
fn plugin(&self) -> zbus::Result<String>;
/// Ports property
#[dbus_proxy(property)]
fn ports(&self) -> zbus::Result<Vec<(String, u32)>>;
/// PowerState property
#[dbus_proxy(property)]
fn power_state(&self) -> zbus::Result<u32>;
/// PrimaryPort property
#[dbus_proxy(property)]
fn primary_port(&self) -> zbus::Result<String>;
/// PrimarySimSlot property
#[dbus_proxy(property)]
fn primary_sim_slot(&self) -> zbus::Result<u32>;
/// Revision property
#[dbus_proxy(property)]
fn revision(&self) -> zbus::Result<String>;
/// SignalQuality property
#[dbus_proxy(property)]
fn signal_quality(&self) -> zbus::Result<(u32, bool)>;
/// Sim property
#[dbus_proxy(property)]
fn sim(&self) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// SimSlots property
#[dbus_proxy(property)]
fn sim_slots(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// State property
#[dbus_proxy(property)]
fn state(&self) -> zbus::Result<i32>;
/// StateFailedReason property
#[dbus_proxy(property)]
fn state_failed_reason(&self) -> zbus::Result<u32>;
/// SupportedBands property
#[dbus_proxy(property)]
fn supported_bands(&self) -> zbus::Result<Vec<u32>>;
/// SupportedCapabilities property
#[dbus_proxy(property)]
fn supported_capabilities(&self) -> zbus::Result<Vec<u32>>;
/// SupportedIpFamilies property
#[dbus_proxy(property)]
fn supported_ip_families(&self) -> zbus::Result<u32>;
/// SupportedModes property
#[dbus_proxy(property)]
fn supported_modes(&self) -> zbus::Result<Vec<(u32, u32)>>;
/// UnlockRequired property
#[dbus_proxy(property)]
fn unlock_required(&self) -> zbus::Result<u32>;
/// UnlockRetries property
#[dbus_proxy(property)]
fn unlock_retries(&self) -> zbus::Result<std::collections::HashMap<u32, u32>>;
}
#[dbus_proxy(
interface = "org.freedesktop.ModemManager1.Modem.Simple",
default_service = "org.freedesktop.ModemManager1",
assume_defaults = true
)]
trait Simple {
/// Connect method
fn connect(
&self,
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// Disconnect method
fn disconnect(&self, bearer: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// GetStatus method
fn get_status(
&self,
) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
}
#[dbus_proxy(
interface = "org.freedesktop.ModemManager1.Modem.Voice",
default_service = "org.freedesktop.ModemManager1",
assume_defaults = true
)]
trait Voice {
/// CallWaitingQuery method
fn call_waiting_query(&self) -> zbus::Result<bool>;
/// CallWaitingSetup method
fn call_waiting_setup(&self, enable: bool) -> zbus::Result<()>;
/// CreateCall method
fn create_call(
&self,
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
/// DeleteCall method
fn delete_call(&self, path: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// HangupAll method
fn hangup_all(&self) -> zbus::Result<()>;
/// HangupAndAccept method
fn hangup_and_accept(&self) -> zbus::Result<()>;
/// HoldAndAccept method
fn hold_and_accept(&self) -> zbus::Result<()>;
/// ListCalls method
fn list_calls(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// Transfer method
fn transfer(&self) -> zbus::Result<()>;
/// CallAdded signal
#[dbus_proxy(signal)]
fn call_added(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// CallDeleted signal
#[dbus_proxy(signal)]
fn call_deleted(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
/// Calls property
#[dbus_proxy(property)]
fn calls(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
/// EmergencyOnly property
#[dbus_proxy(property)]
fn emergency_only(&self) -> zbus::Result<bool>;
}

View file

@ -1,115 +0,0 @@
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
// for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
mod introspect;
use crate::error::Error;
use crate::modem::introspect::{ModemProxy, MessagingProxy, VoiceProxy};
use std::fmt;
use zbus::{fdo::ObjectManagerProxy, zvariant::ObjectPath, Connection};
const MM_NAME: &str = "org.freedesktop.ModemManager1";
const MM_PATH: &str = "/org/freedesktop/ModemManager1";
#[derive(Clone)]
pub struct ModemManager<'a> {
connection: Connection,
modems: Vec<Modem<'a>>,
}
impl<'a> ModemManager<'a> {
pub async fn connect() -> Result<ModemManager<'a>, Error> {
let connection = Connection::system().await?;
Ok(ModemManager {
modems: ModemManager::modems(&connection).await?,
connection,
})
}
pub async fn modems(conn: &Connection) -> Result<Vec<Modem<'a>>, Error> {
let proxy = ObjectManagerProxy::builder(&conn)
.destination(MM_NAME)?
.path(MM_PATH)?
.build()
.await?;
let mut res = vec![];
for (path, _) in proxy.get_managed_objects().await? {
res.push(Modem::new(&conn, path.into()).await?)
}
Ok(res)
}
}
impl fmt::Debug for ModemManager<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModemManager")
.field("modems", &self.modems)
.finish_non_exhaustive()
}
}
impl<'a> IntoIterator for ModemManager<'a> {
type Item = Modem<'a>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.modems.into_iter()
}
}
#[derive(Clone)]
pub struct Modem<'a> {
modem: ModemProxy<'a>,
messaging: MessagingProxy<'a>,
voice: VoiceProxy<'a>,
path: ObjectPath<'a>,
}
impl<'a> Modem<'a> {
pub async fn new(connection: &Connection, path: ObjectPath<'a>) -> Result<Modem<'a>, Error> {
Ok(Modem {
modem: ModemProxy::builder(connection).path(path.clone())?.build().await?,
messaging: MessagingProxy::builder(connection).path(path.clone())?.build().await?,
voice: VoiceProxy::builder(connection).path(path.clone())?.build().await?,
path,
})
}
pub fn foo(&self) -> &ModemProxy<'a> {
&self.modem
}
pub fn messaging(&self) -> &MessagingProxy<'a> {
&self.messaging
}
pub fn voice(&self) -> &VoiceProxy<'a> {
&self.voice
}
}
impl fmt::Debug for Modem<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Modem")
.field("destination", &self.foo().destination())
.field("path", &self.foo().inner().path())
.field("interface", &self.foo().inner().interface())
.finish_non_exhaustive()
}
}