From 3cea94b72dbfdd2077a59fc52a7460e200b6061e Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Fri, 18 Sep 2026 15:13:01 +0800 Subject: refactor: qml_plugin --- qml_plugin/src/launcher.rs | 5 -- qml_plugin/src/launcher/duckduckgo.rs | 88 --------------------- qml_plugin/src/launcher/mathcalc.rs | 106 ------------------------- qml_plugin/src/launcher/smartcalc.rs | 123 ----------------------------- qml_plugin/src/lib.rs | 3 +- qml_plugin/src/qml_interface.rs | 8 ++ qml_plugin/src/qml_interface/duckduckgo.rs | 88 +++++++++++++++++++++ qml_plugin/src/qml_interface/mathcalc.rs | 106 +++++++++++++++++++++++++ qml_plugin/src/qml_interface/network.rs | 55 +++++++++++++ qml_plugin/src/qml_interface/smartcalc.rs | 123 +++++++++++++++++++++++++++++ qml_plugin/src/services.rs | 1 + qml_plugin/src/services/network.rs | 66 ++++++++++++++++ qml_plugin/src/web/duckduckgo.rs | 78 ++++++++++-------- 13 files changed, 495 insertions(+), 355 deletions(-) delete mode 100644 qml_plugin/src/launcher.rs delete mode 100644 qml_plugin/src/launcher/duckduckgo.rs delete mode 100644 qml_plugin/src/launcher/mathcalc.rs delete mode 100644 qml_plugin/src/launcher/smartcalc.rs create mode 100644 qml_plugin/src/qml_interface.rs create mode 100644 qml_plugin/src/qml_interface/duckduckgo.rs create mode 100644 qml_plugin/src/qml_interface/mathcalc.rs create mode 100644 qml_plugin/src/qml_interface/network.rs create mode 100644 qml_plugin/src/qml_interface/smartcalc.rs create mode 100644 qml_plugin/src/services.rs create mode 100644 qml_plugin/src/services/network.rs (limited to 'qml_plugin/src') diff --git a/qml_plugin/src/launcher.rs b/qml_plugin/src/launcher.rs deleted file mode 100644 index 180a756..0000000 --- a/qml_plugin/src/launcher.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod duckduckgo; -pub mod mathcalc; -pub mod smartcalc; - -// TODO: use background worker + mpsc diff --git a/qml_plugin/src/launcher/duckduckgo.rs b/qml_plugin/src/launcher/duckduckgo.rs deleted file mode 100644 index 0375cdf..0000000 --- a/qml_plugin/src/launcher/duckduckgo.rs +++ /dev/null @@ -1,88 +0,0 @@ -use cxx_qt::{CxxQtType, Threading}; -use cxx_qt_lib::QString; -use std::pin::Pin; -use unwrap_print::PrintableResult; - -use crate::{ExclusiveExecutor, web::duckduckgo}; - -#[cxx_qt::bridge] -mod qobject { - extern "C++" { - include!("cxx-qt-lib/qstring.h"); - type QString = cxx_qt_lib::QString; - } - - extern "RustQt" { - #[qobject] - #[qml_element] - #[qml_singleton] - #[qproperty(bool, ok)] - #[qproperty(QString, title)] - #[qproperty(QString, description_html)] - #[qproperty(QString, image)] - type DuckDuckGo = super::DuckDuckGoRs; - - #[qinvokable] - fn query(self: Pin<&mut DuckDuckGo>, input: QString); - - #[qinvokable] - fn reset(self: Pin<&mut DuckDuckGo>); - } - - impl cxx_qt::Threading for DuckDuckGo {} -} - -#[derive(Default)] -pub struct QmlDuckDuckGo { - pub title: QString, - pub description_html: QString, - pub image: QString, -} - -#[derive(Default)] -pub struct DuckDuckGoRs { - ok: bool, - title: QString, - description_html: QString, - image: QString, - - executor: ExclusiveExecutor, -} - -impl qobject::DuckDuckGo { - fn query(self: Pin<&mut Self>, input: QString) { - let qt_thread = self.qt_thread(); - self.rust_mut().executor.spawn(async move { - let qinput = input.to_string(); - let result: Result = - duckduckgo::query(&qinput).await.map(|r| r.into()); - // consider just unwrapping since this is in a separate thread and - // is already end of life anw - _ = qt_thread - .queue(move |mut qo| match result { - Ok(r) => { - if (r.title.to_lower() == input.to_lower() || r.title.is_empty()) - && r.description_html.is_empty() - && r.image.is_empty() - { - qo.as_mut().set_ok(false); - } else { - qo.as_mut().set_ok(true); - qo.as_mut().set_title(r.title); - qo.as_mut().set_description_html(r.description_html); - qo.as_mut().set_image(r.image); - } - } - Err(_) => qo.set_ok(false), - }) - .unwrap_print(); - }); - } - - fn reset(mut self: Pin<&mut Self>) { - self.as_mut().set_ok(false); - self.as_mut().set_title("".into()); - self.as_mut().set_description_html("".into()); - self.as_mut().set_image("".into()); - } -} diff --git a/qml_plugin/src/launcher/mathcalc.rs b/qml_plugin/src/launcher/mathcalc.rs deleted file mode 100644 index 601dc3b..0000000 --- a/qml_plugin/src/launcher/mathcalc.rs +++ /dev/null @@ -1,106 +0,0 @@ -use cxx_qt::{CxxQtType, Threading}; -use cxx_qt_lib::{QString, QVariant}; -use kalk::parser; -use std::{pin::Pin, sync::Arc}; -use tokio::sync::Mutex; -use unwrap_print::PrintableResult; - -use crate::ExclusiveExecutor; - -const DEFAULT_ANGLE_UNIT: &str = "deg"; - -#[cxx_qt::bridge] -mod qobject { - extern "C++" { - include!("cxx-qt-lib/qstring.h"); - include!("cxx-qt-lib/qvariant.h"); - type QString = cxx_qt_lib::QString; - type QVariant = cxx_qt_lib::QVariant; - } - - extern "RustQt" { - #[qobject] - #[qml_element] - #[qml_singleton] - #[qproperty(QVariant, result)] - type MathCalc = super::MathCalcRs; - - #[qinvokable] - fn query(self: Pin<&mut MathCalc>, input: QString); - - #[qinvokable] - fn reset(self: Pin<&mut MathCalc>); - - #[qinvokable] - fn reset_ctx(self: Pin<&mut MathCalc>); - - #[qinvokable] - fn reset_result(self: Pin<&mut MathCalc>); - } - - impl cxx_qt::Threading for MathCalc {} -} - -pub struct MathCalcRs { - result: QVariant, - ctx: Arc>, - - executor: ExclusiveExecutor, -} - -impl Default for MathCalcRs { - fn default() -> Self { - Self { - result: QVariant::default(), - ctx: Arc::new(Mutex::new(qobject::MathCalc::new_ctx())), - executor: ExclusiveExecutor::default(), - } - } -} - -impl qobject::MathCalc { - fn query(mut self: Pin<&mut Self>, input: QString) { - let qt_thread = self.qt_thread(); - let ctx = Arc::clone(&self.as_mut().rust_mut().ctx); - // TODO: consider moving to spawn_blocking - self.rust_mut().executor.spawn(async move { - let input = input.to_string(); - let result = parser::eval( - &mut *ctx.lock().await, - &input, - // TODO: move to const/config - 53, - ) - .map_or_default(|r| { - r.map_or_default(|r| { - let r: QString = r - // to display scientific notation properly - // other ones show both version without e or shows *10^_ - // and have a = prefix - // to_string_big will result in 1/2=5.0000000000000000e − 1 - .to_string() - .into(); - if r != input.into() { QVariant::from(&r) } else { QVariant::default() } - }) - }); - _ = qt_thread.queue(|mut qo| qo.as_mut().set_result(result)).unwrap_print(); - }); - } - - fn reset(mut self: Pin<&mut Self>) { - self.as_mut().reset_result(); - self.reset_ctx(); - } - - fn new_ctx() -> parser::Context { - parser::Context::default().set_angle_unit(DEFAULT_ANGLE_UNIT) - } - - fn reset_ctx(mut self: Pin<&mut Self>) { - self.as_mut().rust_mut().ctx = Arc::new(Mutex::new(Self::new_ctx())) - } - - fn reset_result(mut self: Pin<&mut Self>) { - self.as_mut().set_result(QVariant::default()) - } -} diff --git a/qml_plugin/src/launcher/smartcalc.rs b/qml_plugin/src/launcher/smartcalc.rs deleted file mode 100644 index 9b5f05a..0000000 --- a/qml_plugin/src/launcher/smartcalc.rs +++ /dev/null @@ -1,123 +0,0 @@ -use cxx_qt::{CxxQtType, Threading}; -use cxx_qt_lib::{QString, QVariant}; -use std::pin::Pin; -use unwrap_print::PrintableResult; - -use crate::ExclusiveExecutor; - -#[cxx_qt::bridge] -mod qobject { - extern "C++" { - include!("cxx-qt-lib/qstring.h"); - include!("cxx-qt-lib/qvariant.h"); - type QString = cxx_qt_lib::QString; - type QVariant = cxx_qt_lib::QVariant; - } - - extern "RustQt" { - #[qobject] - #[qml_element] - #[qml_singleton] - #[qproperty(QVariant, result)] - #[qproperty(ResultType, result_type)] - #[qproperty(QVariant, cached_query)] - type SmartCalc = super::SmartCalcRs; - - #[qinvokable] - fn query(self: Pin<&mut SmartCalc>, input: QString); - - #[qinvokable] - fn reset(self: Pin<&mut SmartCalc>); - - #[qinvokable] - fn reset_result(self: Pin<&mut SmartCalc>); - } - - impl cxx_qt::Threading for SmartCalc {} - - #[qenum(SmartCalc)] - pub enum ResultType { - Math, - Unit, - Currency, - Crypto, - Time, - Date, - Unset, - } -} - -impl Default for qobject::ResultType { - fn default() -> Self { - qobject::ResultType::Unset - } -} - -impl From<&smart_calculator::types::ResultType> for qobject::ResultType { - fn from(value: &smart_calculator::types::ResultType) -> Self { - match value { - smart_calculator::types::ResultType::Math => qobject::ResultType::Math, - smart_calculator::types::ResultType::Unit => qobject::ResultType::Unit, - smart_calculator::types::ResultType::Currency => qobject::ResultType::Currency, - smart_calculator::types::ResultType::Crypto => qobject::ResultType::Crypto, - smart_calculator::types::ResultType::Time => qobject::ResultType::Time, - smart_calculator::types::ResultType::Date => qobject::ResultType::Date, - } - } -} - -#[derive(Default)] -pub struct SmartCalcRs { - result: QVariant, - result_type: qobject::ResultType, - - cached_query: QVariant, - - executor: ExclusiveExecutor, -} - -impl qobject::SmartCalc { - fn query(mut self: Pin<&mut Self>, input: QString) { - if self.cached_query == (&input).into() { - return; - } - self.as_mut().set_cached_query((&input.clone()).into()); - let qt_thread = self.qt_thread(); - self.as_mut().rust_mut().executor.spawn(async move { - let result = smart_calculator::calculate(&input.to_string(), None).await; - _ = qt_thread - .queue(|mut qo| { - qo.as_mut().set_result_type(match &result { - Ok(value) => (&value.res_type).into(), - Err(_) => qobject::ResultType::Unset, - }); - qo.as_mut().set_result( - // TODO: do the calc outside of ts main qt ui render - // thread.(all *.rs) - match result { - Ok(value) => { - if value.formatted == value.input { - QVariant::default() - } else { - let result: QString = value.formatted.into(); - QVariant::from(&result) - } - } - Err(_) => QVariant::default(), - }, - ) - }) - .unwrap_print(); - }); - } - - fn reset(mut self: Pin<&mut Self>) { - self.as_mut().set_cached_query(QVariant::default()); - self.reset_result(); - } - - fn reset_result(mut self: Pin<&mut Self>) { - self.as_mut().set_result(QVariant::default()); - self.as_mut().set_result_type(qobject::ResultType::Unset); - } -} diff --git a/qml_plugin/src/lib.rs b/qml_plugin/src/lib.rs index 0f1411c..b00b8be 100644 --- a/qml_plugin/src/lib.rs +++ b/qml_plugin/src/lib.rs @@ -34,5 +34,6 @@ impl ExclusiveExecutor { } } -mod launcher; +mod qml_interface; +mod services; mod web; diff --git a/qml_plugin/src/qml_interface.rs b/qml_plugin/src/qml_interface.rs new file mode 100644 index 0000000..f93fa23 --- /dev/null +++ b/qml_plugin/src/qml_interface.rs @@ -0,0 +1,8 @@ +// TODO: use background worker + mpsc +/// Launchers +pub mod duckduckgo; +pub mod mathcalc; +pub mod smartcalc; + +/// Services +pub mod network; diff --git a/qml_plugin/src/qml_interface/duckduckgo.rs b/qml_plugin/src/qml_interface/duckduckgo.rs new file mode 100644 index 0000000..0375cdf --- /dev/null +++ b/qml_plugin/src/qml_interface/duckduckgo.rs @@ -0,0 +1,88 @@ +use cxx_qt::{CxxQtType, Threading}; +use cxx_qt_lib::QString; +use std::pin::Pin; +use unwrap_print::PrintableResult; + +use crate::{ExclusiveExecutor, web::duckduckgo}; + +#[cxx_qt::bridge] +mod qobject { + extern "C++" { + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + #[qproperty(bool, ok)] + #[qproperty(QString, title)] + #[qproperty(QString, description_html)] + #[qproperty(QString, image)] + type DuckDuckGo = super::DuckDuckGoRs; + + #[qinvokable] + fn query(self: Pin<&mut DuckDuckGo>, input: QString); + + #[qinvokable] + fn reset(self: Pin<&mut DuckDuckGo>); + } + + impl cxx_qt::Threading for DuckDuckGo {} +} + +#[derive(Default)] +pub struct QmlDuckDuckGo { + pub title: QString, + pub description_html: QString, + pub image: QString, +} + +#[derive(Default)] +pub struct DuckDuckGoRs { + ok: bool, + title: QString, + description_html: QString, + image: QString, + + executor: ExclusiveExecutor, +} + +impl qobject::DuckDuckGo { + fn query(self: Pin<&mut Self>, input: QString) { + let qt_thread = self.qt_thread(); + self.rust_mut().executor.spawn(async move { + let qinput = input.to_string(); + let result: Result = + duckduckgo::query(&qinput).await.map(|r| r.into()); + // consider just unwrapping since this is in a separate thread and + // is already end of life anw + _ = qt_thread + .queue(move |mut qo| match result { + Ok(r) => { + if (r.title.to_lower() == input.to_lower() || r.title.is_empty()) + && r.description_html.is_empty() + && r.image.is_empty() + { + qo.as_mut().set_ok(false); + } else { + qo.as_mut().set_ok(true); + qo.as_mut().set_title(r.title); + qo.as_mut().set_description_html(r.description_html); + qo.as_mut().set_image(r.image); + } + } + Err(_) => qo.set_ok(false), + }) + .unwrap_print(); + }); + } + + fn reset(mut self: Pin<&mut Self>) { + self.as_mut().set_ok(false); + self.as_mut().set_title("".into()); + self.as_mut().set_description_html("".into()); + self.as_mut().set_image("".into()); + } +} diff --git a/qml_plugin/src/qml_interface/mathcalc.rs b/qml_plugin/src/qml_interface/mathcalc.rs new file mode 100644 index 0000000..601dc3b --- /dev/null +++ b/qml_plugin/src/qml_interface/mathcalc.rs @@ -0,0 +1,106 @@ +use cxx_qt::{CxxQtType, Threading}; +use cxx_qt_lib::{QString, QVariant}; +use kalk::parser; +use std::{pin::Pin, sync::Arc}; +use tokio::sync::Mutex; +use unwrap_print::PrintableResult; + +use crate::ExclusiveExecutor; + +const DEFAULT_ANGLE_UNIT: &str = "deg"; + +#[cxx_qt::bridge] +mod qobject { + extern "C++" { + include!("cxx-qt-lib/qstring.h"); + include!("cxx-qt-lib/qvariant.h"); + type QString = cxx_qt_lib::QString; + type QVariant = cxx_qt_lib::QVariant; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + #[qproperty(QVariant, result)] + type MathCalc = super::MathCalcRs; + + #[qinvokable] + fn query(self: Pin<&mut MathCalc>, input: QString); + + #[qinvokable] + fn reset(self: Pin<&mut MathCalc>); + + #[qinvokable] + fn reset_ctx(self: Pin<&mut MathCalc>); + + #[qinvokable] + fn reset_result(self: Pin<&mut MathCalc>); + } + + impl cxx_qt::Threading for MathCalc {} +} + +pub struct MathCalcRs { + result: QVariant, + ctx: Arc>, + + executor: ExclusiveExecutor, +} + +impl Default for MathCalcRs { + fn default() -> Self { + Self { + result: QVariant::default(), + ctx: Arc::new(Mutex::new(qobject::MathCalc::new_ctx())), + executor: ExclusiveExecutor::default(), + } + } +} + +impl qobject::MathCalc { + fn query(mut self: Pin<&mut Self>, input: QString) { + let qt_thread = self.qt_thread(); + let ctx = Arc::clone(&self.as_mut().rust_mut().ctx); + // TODO: consider moving to spawn_blocking + self.rust_mut().executor.spawn(async move { + let input = input.to_string(); + let result = parser::eval( + &mut *ctx.lock().await, + &input, + // TODO: move to const/config + 53, + ) + .map_or_default(|r| { + r.map_or_default(|r| { + let r: QString = r + // to display scientific notation properly + // other ones show both version without e or shows *10^_ + // and have a = prefix + // to_string_big will result in 1/2=5.0000000000000000e − 1 + .to_string() + .into(); + if r != input.into() { QVariant::from(&r) } else { QVariant::default() } + }) + }); + _ = qt_thread.queue(|mut qo| qo.as_mut().set_result(result)).unwrap_print(); + }); + } + + fn reset(mut self: Pin<&mut Self>) { + self.as_mut().reset_result(); + self.reset_ctx(); + } + + fn new_ctx() -> parser::Context { + parser::Context::default().set_angle_unit(DEFAULT_ANGLE_UNIT) + } + + fn reset_ctx(mut self: Pin<&mut Self>) { + self.as_mut().rust_mut().ctx = Arc::new(Mutex::new(Self::new_ctx())) + } + + fn reset_result(mut self: Pin<&mut Self>) { + self.as_mut().set_result(QVariant::default()) + } +} diff --git a/qml_plugin/src/qml_interface/network.rs b/qml_plugin/src/qml_interface/network.rs new file mode 100644 index 0000000..7ece121 --- /dev/null +++ b/qml_plugin/src/qml_interface/network.rs @@ -0,0 +1,55 @@ +use cxx_qt::{CxxQtType, Threading}; +use cxx_qt_lib::{QString, QVariant}; +use std::pin::Pin; +use unwrap_print::PrintableResult; +use zbus::zvariant::NoneValue; + +use crate::{ExclusiveExecutor, services::network}; + +#[cxx_qt::bridge] +mod qobject { + extern "C++" { + include!("cxx-qt-lib/qvariant.h"); + type QVariant = cxx_qt_lib::QVariant; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + #[qproperty( + QVariant, result + )] + type Network = super::NetworkRs; + + #[qinvokable] + fn query_primary_device(self: Pin<&mut Network>); + } + + impl cxx_qt::Threading for Network {} +} + +#[derive(Default)] +pub struct NetworkRs { + result: QVariant, + + executor: ExclusiveExecutor, +} + +impl qobject::Network { + fn query_primary_device(self: Pin<&mut Self>) { + let qt_thread = self.qt_thread(); + self.rust_mut().executor.spawn( + async move { + let result = match network::get().await { + Ok(o) => { + let r: QString = o.into(); + QVariant::from(&r) + } + Err(_) => QVariant::null_value(), + }; + _ = qt_thread.queue(|mut qo| qo.as_mut().set_result(result)).unwrap_print(); + }, + ); + } +} diff --git a/qml_plugin/src/qml_interface/smartcalc.rs b/qml_plugin/src/qml_interface/smartcalc.rs new file mode 100644 index 0000000..9b5f05a --- /dev/null +++ b/qml_plugin/src/qml_interface/smartcalc.rs @@ -0,0 +1,123 @@ +use cxx_qt::{CxxQtType, Threading}; +use cxx_qt_lib::{QString, QVariant}; +use std::pin::Pin; +use unwrap_print::PrintableResult; + +use crate::ExclusiveExecutor; + +#[cxx_qt::bridge] +mod qobject { + extern "C++" { + include!("cxx-qt-lib/qstring.h"); + include!("cxx-qt-lib/qvariant.h"); + type QString = cxx_qt_lib::QString; + type QVariant = cxx_qt_lib::QVariant; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + #[qproperty(QVariant, result)] + #[qproperty(ResultType, result_type)] + #[qproperty(QVariant, cached_query)] + type SmartCalc = super::SmartCalcRs; + + #[qinvokable] + fn query(self: Pin<&mut SmartCalc>, input: QString); + + #[qinvokable] + fn reset(self: Pin<&mut SmartCalc>); + + #[qinvokable] + fn reset_result(self: Pin<&mut SmartCalc>); + } + + impl cxx_qt::Threading for SmartCalc {} + + #[qenum(SmartCalc)] + pub enum ResultType { + Math, + Unit, + Currency, + Crypto, + Time, + Date, + Unset, + } +} + +impl Default for qobject::ResultType { + fn default() -> Self { + qobject::ResultType::Unset + } +} + +impl From<&smart_calculator::types::ResultType> for qobject::ResultType { + fn from(value: &smart_calculator::types::ResultType) -> Self { + match value { + smart_calculator::types::ResultType::Math => qobject::ResultType::Math, + smart_calculator::types::ResultType::Unit => qobject::ResultType::Unit, + smart_calculator::types::ResultType::Currency => qobject::ResultType::Currency, + smart_calculator::types::ResultType::Crypto => qobject::ResultType::Crypto, + smart_calculator::types::ResultType::Time => qobject::ResultType::Time, + smart_calculator::types::ResultType::Date => qobject::ResultType::Date, + } + } +} + +#[derive(Default)] +pub struct SmartCalcRs { + result: QVariant, + result_type: qobject::ResultType, + + cached_query: QVariant, + + executor: ExclusiveExecutor, +} + +impl qobject::SmartCalc { + fn query(mut self: Pin<&mut Self>, input: QString) { + if self.cached_query == (&input).into() { + return; + } + self.as_mut().set_cached_query((&input.clone()).into()); + let qt_thread = self.qt_thread(); + self.as_mut().rust_mut().executor.spawn(async move { + let result = smart_calculator::calculate(&input.to_string(), None).await; + _ = qt_thread + .queue(|mut qo| { + qo.as_mut().set_result_type(match &result { + Ok(value) => (&value.res_type).into(), + Err(_) => qobject::ResultType::Unset, + }); + qo.as_mut().set_result( + // TODO: do the calc outside of ts main qt ui render + // thread.(all *.rs) + match result { + Ok(value) => { + if value.formatted == value.input { + QVariant::default() + } else { + let result: QString = value.formatted.into(); + QVariant::from(&result) + } + } + Err(_) => QVariant::default(), + }, + ) + }) + .unwrap_print(); + }); + } + + fn reset(mut self: Pin<&mut Self>) { + self.as_mut().set_cached_query(QVariant::default()); + self.reset_result(); + } + + fn reset_result(mut self: Pin<&mut Self>) { + self.as_mut().set_result(QVariant::default()); + self.as_mut().set_result_type(qobject::ResultType::Unset); + } +} diff --git a/qml_plugin/src/services.rs b/qml_plugin/src/services.rs new file mode 100644 index 0000000..a61610b --- /dev/null +++ b/qml_plugin/src/services.rs @@ -0,0 +1 @@ +pub mod network; diff --git a/qml_plugin/src/services/network.rs b/qml_plugin/src/services/network.rs new file mode 100644 index 0000000..4883dba --- /dev/null +++ b/qml_plugin/src/services/network.rs @@ -0,0 +1,66 @@ +use zbus::{Connection, proxy, zvariant::OwnedObjectPath}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("The connection is not active.")] + InactiveConnection, + #[error("Device doesn't exist")] + DeviceNotFound, + #[error("Failed to communicate with NetworkManager: {0}")] + Zbus(#[from] zbus::Error), +} + +#[proxy( + interface = "org.freedesktop.NetworkManager", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager" +)] +trait NetworkManager { + #[zbus(property)] + fn primary_connection(&self) -> zbus::Result; +} + +#[proxy( + interface = "org.freedesktop.NetworkManager.Connection.Active", + default_service = "org.freedesktop.NetworkManager" +)] +trait ActiveConnection { + #[zbus(property)] + fn id(&self) -> zbus::Result; + + #[zbus( + property, + name = "Type" + )] + fn type_(&self) -> zbus::Result; + + #[zbus(property)] + fn devices(&self) -> zbus::Result>; +} + +#[proxy( + interface = "org.freedesktop.NetworkManager.Device", + default_service = "org.freedesktop.NetworkManager" +)] +trait Device { + #[zbus(property)] + fn interface(&self) -> zbus::Result; +} + +pub async fn get() -> Result { + let conn = Connection::system().await?; + + let nm_conn = NetworkManagerProxy::new(&conn).await?.primary_connection().await?; + + if nm_conn.as_str() == "/" { + return Err(Error::InactiveConnection); + } + + if let Some(device_path) = + ActiveConnectionProxy::builder(&conn).path(nm_conn)?.build().await?.devices().await?.first() + { + Ok(DeviceProxy::builder(&conn).path(device_path)?.build().await?.interface().await?) + } else { + Err(Error::DeviceNotFound) + } +} diff --git a/qml_plugin/src/web/duckduckgo.rs b/qml_plugin/src/web/duckduckgo.rs index 6b4edae..e5f1b64 100644 --- a/qml_plugin/src/web/duckduckgo.rs +++ b/qml_plugin/src/web/duckduckgo.rs @@ -1,6 +1,5 @@ use std::ops::{Deref, DerefMut}; -use crate::launcher::duckduckgo::QmlDuckDuckGo; use duckduckgo::{ browser::Browser, params::{SafeSearch, SearchParams, Toggle}, @@ -8,6 +7,8 @@ use duckduckgo::{ use reqwest::Url; use serde::Deserialize; +use crate::qml_interface::duckduckgo::QmlDuckDuckGo; + const DUCKDUCKGO: &str = "https://duckduckgo.com"; #[derive(Default, PartialEq)] @@ -83,36 +84,49 @@ pub async fn query(input: &str) -> anyhow::Result { _ => None, }; - let alt_desc = resp.entity.unwrap_or(match infobox { - Some(i) => i - .iter() - .find(|i| i.data_type == "wd_description" || i.data_type == "official_website") - .map_or(String::new(), |i| { - if i.data_type == "official_website" { - i.label.clone() - } else { - match i.value { - InfoboxValue::Entity(_) => unreachable!(), - InfoboxValue::String(ref s) => s.to_string(), + let alt_desc = resp.entity.unwrap_or( + match infobox { + Some(i) => i + .iter() + .find(|i| i.data_type == "wd_description" || i.data_type == "official_website") + .map_or( + String::new(), + |i| { + if i.data_type == "official_website" { + i.label.clone() + } else { + match i.value { + InfoboxValue::Entity(_) => unreachable!(), + InfoboxValue::String(ref s) => s.to_string(), + } + } + }, + ), + // TODO: find better way to get title from infobox + None => String::new(), + }, + ); + + Ok( + DuckDuckGoContent { + title: resp + .heading + .filter(|h| !h.is_empty()) + .unwrap_or(resp.definition.filter(|d| !d.is_empty()).unwrap_or(alt_desc.clone())), + description_html: resp + .answer + .filter(|a| !a.is_empty()) + .unwrap_or(resp.r#abstract.filter(|a| !a.is_empty()).unwrap_or(alt_desc)), + // TODO: try also Results[*].Icon.URL and RelatedTopics[*].Icon.URL + image: resp.image.filter(|i| !i.is_empty()).and_then( + |i| { + if !i.is_empty() { + Url::parse(&format!("{DUCKDUCKGO}{i}",)).ok() + } else { + None } - } - }), - // TODO: find better way to get title from infobox - None => String::new(), - }); - - Ok(DuckDuckGoContent { - title: resp - .heading - .filter(|h| !h.is_empty()) - .unwrap_or(resp.definition.filter(|d| !d.is_empty()).unwrap_or(alt_desc.clone())), - description_html: resp - .answer - .filter(|a| !a.is_empty()) - .unwrap_or(resp.r#abstract.filter(|a| !a.is_empty()).unwrap_or(alt_desc)), - // TODO: try also Results[*].Icon.URL and RelatedTopics[*].Icon.URL - image: resp.image.filter(|i| !i.is_empty()).and_then(|i| { - if !i.is_empty() { Url::parse(&format!("{DUCKDUCKGO}{i}",)).ok() } else { None } - }), - }) + }, + ), + }, + ) } -- cgit v1.2.3