refactor: simplify lact-client by removing ResponseBuffer

This commit is contained in:
Ilya Zlobintsev
2025-01-05 10:14:22 +02:00
parent 3afbcb2221
commit e93025164e
4 changed files with 49 additions and 126 deletions

View File

@@ -19,8 +19,8 @@ pub fn run(args: CliArgs) -> Result<()> {
}
async fn list_gpus(_: &CliArgs, client: &DaemonClient) -> Result<()> {
let buffer = client.list_devices().await?;
for entry in buffer.inner()? {
let entries = client.list_devices().await?;
for entry in entries {
let id = entry.id;
if let Some(name) = entry.name {
println!("{id} ({name})");
@@ -33,8 +33,7 @@ async fn list_gpus(_: &CliArgs, client: &DaemonClient) -> Result<()> {
async fn info(args: &CliArgs, client: &DaemonClient) -> Result<()> {
for id in extract_gpu_ids(args, client).await {
let info_buffer = client.get_device_info(&id).await?;
let info = info_buffer.inner()?;
let info = client.get_device_info(&id).await?;
let pci_info = info.pci_info.context("GPU reports no pci info")?;
if let Some(ref vendor) = pci_info.device_pci_info.vendor {
@@ -56,10 +55,8 @@ async fn extract_gpu_ids(args: &CliArgs, client: &DaemonClient) -> Vec<String> {
match args.gpu_id {
Some(ref id) => vec![id.clone()],
None => {
let buffer = client.list_devices().await.expect("Could not list GPUs");
buffer
.inner()
.expect("Could not deserialize GPUs response")
let entries = client.list_devices().await.expect("Could not list GPUs");
entries
.into_iter()
.map(|entry| entry.id.to_owned())
.collect()
@@ -68,8 +65,7 @@ async fn extract_gpu_ids(args: &CliArgs, client: &DaemonClient) -> Vec<String> {
}
async fn snapshot(client: &DaemonClient) -> Result<()> {
let buffer = client.generate_debug_snapshot().await?;
let path = buffer.inner()?;
let path = client.generate_debug_snapshot().await?;
println!("Generated debug snapshot in {path}");
Ok(())
}

View File

@@ -16,10 +16,9 @@ use schema::{
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanOptions, PowerStates, ProfilesInfo,
Request, Response, SystemInfo,
};
use serde::Deserialize;
use serde::de::DeserializeOwned;
use std::{
future::Future, marker::PhantomData, os::unix::net::UnixStream, path::PathBuf, pin::Pin,
rc::Rc, time::Duration,
future::Future, os::unix::net::UnixStream, path::PathBuf, pin::Pin, rc::Rc, time::Duration,
};
use tokio::{
net::ToSocketAddrs,
@@ -73,19 +72,24 @@ impl DaemonClient {
self.status_tx.subscribe()
}
fn make_request<'a, 'r, T: Deserialize<'r>>(
fn make_request<'a, T: DeserializeOwned>(
&'a self,
request: Request<'a>,
) -> Pin<Box<dyn Future<Output = anyhow::Result<ResponseBuffer<T>>> + 'a>> {
) -> Pin<Box<dyn Future<Output = anyhow::Result<T>> + 'a>> {
Box::pin(async {
let mut stream = self.stream.lock().await;
let request_payload = serde_json::to_string(&request)?;
match stream.request(&request_payload).await {
Ok(response_payload) => Ok(ResponseBuffer {
buf: response_payload,
_phantom: PhantomData,
}),
Ok(response_payload) => {
let response: Response<T> = serde_json::from_str(&response_payload)
.context("Could not deserialize response from daemon")?;
match response {
Response::Ok(data) => Ok(data),
Response::Error(err) => Err(anyhow::Error::new(err)
.context("Got error from daemon, end of client boundary")),
}
}
Err(err) => {
error!("Could not make request: {err}, reconnecting to socket");
let _ = self.status_tx.send(ConnectionStatusMsg::Disconnected);
@@ -113,20 +117,16 @@ impl DaemonClient {
})
}
pub async fn list_devices(&self) -> anyhow::Result<ResponseBuffer<Vec<DeviceListEntry>>> {
pub async fn list_devices(&self) -> anyhow::Result<Vec<DeviceListEntry>> {
self.make_request(Request::ListDevices).await
}
pub async fn set_fan_control(&self, cmd: FanOptions<'_>) -> anyhow::Result<u64> {
self.make_request(Request::SetFanControl(cmd))
.await?
.inner()
self.make_request(Request::SetFanControl(cmd)).await
}
pub async fn set_power_cap(&self, id: &str, cap: Option<f64>) -> anyhow::Result<u64> {
self.make_request(Request::SetPowerCap { id, cap })
.await?
.inner()
self.make_request(Request::SetPowerCap { id, cap }).await
}
request_plain!(get_system_info, SystemInfo, SystemInfo);
@@ -148,38 +148,31 @@ impl DaemonClient {
pub async fn list_profiles(&self, include_state: bool) -> anyhow::Result<ProfilesInfo> {
self.make_request(Request::ListProfiles { include_state })
.await?
.inner()
.await
}
pub async fn set_profile(&self, name: Option<String>, auto_switch: bool) -> anyhow::Result<()> {
self.make_request(Request::SetProfile { name, auto_switch })
.await?
.inner()
.await
}
pub async fn create_profile(&self, name: String, base: ProfileBase) -> anyhow::Result<()> {
self.make_request(Request::CreateProfile { name, base })
.await?
.inner()
.await
}
pub async fn delete_profile(&self, name: String) -> anyhow::Result<()> {
self.make_request(Request::DeleteProfile { name })
.await?
.inner()
self.make_request(Request::DeleteProfile { name }).await
}
pub async fn move_profile(&self, name: String, new_position: usize) -> anyhow::Result<()> {
self.make_request(Request::MoveProfile { name, new_position })
.await?
.inner()
.await
}
pub async fn evaluate_profile_rule(&self, rule: ProfileRule) -> anyhow::Result<bool> {
self.make_request(Request::EvaluateProfileRule { rule })
.await?
.inner()
.await
}
pub async fn set_profile_rule(
@@ -188,8 +181,7 @@ impl DaemonClient {
rule: Option<ProfileRule>,
) -> anyhow::Result<()> {
self.make_request(Request::SetProfileRule { name, rule })
.await?
.inner()
.await
}
pub async fn set_performance_level(
@@ -201,8 +193,7 @@ impl DaemonClient {
id,
performance_level,
})
.await?
.inner()
.await
}
pub async fn set_clocks_value(
@@ -211,8 +202,7 @@ impl DaemonClient {
command: SetClocksCommand,
) -> anyhow::Result<u64> {
self.make_request(Request::SetClocksValue { id, command })
.await?
.inner()
.await
}
pub async fn batch_set_clocks_value(
@@ -221,8 +211,7 @@ impl DaemonClient {
commands: Vec<SetClocksCommand>,
) -> anyhow::Result<u64> {
self.make_request(Request::BatchSetClocksValue { id, commands })
.await?
.inner()
.await
}
pub async fn set_enabled_power_states(
@@ -232,8 +221,7 @@ impl DaemonClient {
states: Vec<u8>,
) -> anyhow::Result<u64> {
self.make_request(Request::SetEnabledPowerStates { id, kind, states })
.await?
.inner()
.await
}
pub async fn set_power_profile_mode(
@@ -247,14 +235,12 @@ impl DaemonClient {
index,
custom_heuristics,
})
.await?
.inner()
.await
}
pub async fn confirm_pending_config(&self, command: ConfirmCommand) -> anyhow::Result<()> {
self.make_request(Request::ConfirmPendingConfig(command))
.await?
.inner()
.await
}
}
@@ -275,25 +261,6 @@ fn get_socket_path() -> Option<PathBuf> {
}
}
pub struct ResponseBuffer<T> {
buf: String,
_phantom: PhantomData<T>,
}
impl<'a, T: Deserialize<'a>> ResponseBuffer<T> {
pub fn inner(&'a self) -> anyhow::Result<T> {
let response: Response<T> = serde_json::from_str(&self.buf)
.context("Could not deserialize response from daemon")?;
match response {
Response::Ok(data) => Ok(data),
Response::Error(err) => {
Err(anyhow::Error::new(err)
.context("Got error from daemon, end of client boundary"))
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ConnectionStatusMsg {
Disconnected,

View File

@@ -1,6 +1,6 @@
macro_rules! request_with_id {
($name:ident, $variant:ident, $response:ty) => {
pub async fn $name(&self, id: &str) -> anyhow::Result<ResponseBuffer<$response>> {
pub async fn $name(&self, id: &str) -> anyhow::Result<$response> {
self.make_request(Request::$variant { id }).await
}
};
@@ -8,7 +8,7 @@ macro_rules! request_with_id {
macro_rules! request_plain {
($name:ident, $variant:ident, $response:ty) => {
pub async fn $name(&self) -> anyhow::Result<ResponseBuffer<$response>> {
pub async fn $name(&self) -> anyhow::Result<$response> {
self.make_request(Request::$variant).await
}
};

View File

@@ -161,17 +161,15 @@ impl AsyncComponent for AppModel {
register_actions(&sender);
let system_info_buf = daemon_client
let system_info = daemon_client
.get_system_info()
.await
.expect("Could not fetch system info");
let system_info = system_info_buf.inner().expect("Invalid system info buffer");
let devices_buf = daemon_client
let devices = daemon_client
.list_devices()
.await
.expect("Could not list devices");
let devices = devices_buf.inner().expect("Could not access devices");
if system_info.version != GUI_VERSION || system_info.commit.as_deref() != Some(GIT_COMMIT) {
let err = anyhow!("Version mismatch between GUI and daemon ({GUI_VERSION}-{GIT_COMMIT} vs {}-{})! If you have updated LACT, you need to restart the service with `sudo systemctl restart lactd`.", system_info.version, system_info.commit.as_deref().unwrap_or_default());
@@ -449,7 +447,7 @@ impl AppModel {
.get_device_info(&gpu_id)
.await
.context("Could not fetch info")?;
let info = Arc::new(info_buf.inner()?);
let info = Arc::new(info_buf);
// Plain `nvidia` means that the nvidia driver is loaded, but it does not contain a version fetched from NVML
if info.driver == "nvidia" {
@@ -492,8 +490,7 @@ impl AppModel {
.daemon_client
.get_device_stats(&gpu_id)
.await
.context("Could not fetch stats")?
.inner()?;
.context("Could not fetch stats")?;
let stats = Arc::new(stats);
self.oc_page.set_stats(&stats, true);
@@ -502,13 +499,7 @@ impl AppModel {
self.info_page.emit(PageUpdate::Stats(stats));
let maybe_clocks_table = match self.daemon_client.get_device_clocks_info(&gpu_id).await {
Ok(clocks_buf) => match clocks_buf.inner() {
Ok(info) => info.table,
Err(err) => {
debug!("could not extract clocks info: {err:?}");
None
}
},
Ok(info) => info.table,
Err(err) => {
debug!("could not fetch clocks info: {err:?}");
None
@@ -521,13 +512,7 @@ impl AppModel {
.get_device_power_profile_modes(&gpu_id)
.await
{
Ok(buf) => match buf.inner() {
Ok(table) => Some(table),
Err(err) => {
debug!("Could not extract profile modes table: {err:?}");
None
}
},
Ok(buf) => Some(buf),
Err(err) => {
debug!("Could not get profile modes table: {err:?}");
None
@@ -537,12 +522,7 @@ impl AppModel {
.performance_frame
.set_power_profile_modes(maybe_modes_table);
match self
.daemon_client
.get_power_states(&gpu_id)
.await
.and_then(|states| states.inner())
{
match self.daemon_client.get_power_states(&gpu_id).await {
Ok(power_states) => {
self.oc_page
.power_states_frame
@@ -773,12 +753,7 @@ impl AppModel {
}
async fn dump_vbios(&self, gpu_id: &str, root: &gtk::ApplicationWindow) {
match self
.daemon_client
.dump_vbios(gpu_id)
.await
.and_then(|response| response.inner())
{
match self.daemon_client.dump_vbios(gpu_id).await {
Ok(vbios_data) => {
let file_chooser = FileChooserDialog::new(
Some("Save VBIOS file"),
@@ -826,12 +801,7 @@ impl AppModel {
}
async fn generate_debug_snapshot(&self, root: &gtk::ApplicationWindow) {
match self
.daemon_client
.generate_debug_snapshot()
.await
.and_then(|response| response.inner())
{
match self.daemon_client.generate_debug_snapshot().await {
Ok(path) => {
let path_label = gtk::Label::builder()
.use_markup(true)
@@ -968,11 +938,7 @@ fn start_stats_update_loop(
loop {
tokio::time::sleep(duration).await;
match daemon_client
.get_device_stats(&gpu_id)
.await
.and_then(|buffer| buffer.inner())
{
match daemon_client.get_device_stats(&gpu_id).await {
Ok(stats) => {
sender.input(AppMsg::Stats(Arc::new(stats)));
}
@@ -1043,15 +1009,9 @@ async fn toggle_overdrive(daemon_client: &DaemonClient, enable: bool, root: Appl
dialog.show();
let result = if enable {
daemon_client
.enable_overdrive()
.await
.and_then(|buffer| buffer.inner())
daemon_client.enable_overdrive().await
} else {
daemon_client
.disable_overdrive()
.await
.and_then(|buffer| buffer.inner())
daemon_client.disable_overdrive().await
};
dialog.hide();