use crate::tools::{
errors::DeviceError,
ssh::SshClient,
types::Device,
};
use anyhow::{Context, Result};
use russh::client::Handle;
use std::path::Path;
pub struct DeviceSession {
device: Device,
session: Handle<SshClient>,
}
impl DeviceSession {
pub fn connect(device: &Device) -> Result<Self, DeviceError> {
let session = SshClient::connect(&device.host, device.port, &device.auth_path())
.map_err(|e| DeviceError::ConnectionFailed(format!("{}", e)))?;
Ok(Self {
device: device.clone(),
session,
})
}
pub fn exec(&mut self, command: &str) -> Result<Vec<String>> {
SshClient::exec(&mut self.session, command)
.with_context(|| format!("Failed to execute: {}", command))
}
pub fn exec_as_root(&mut self, command: &str) -> Result<Vec<String>, DeviceError> {
if self.device.root_password.is_empty() {
return Err(DeviceError::RootPasswordNotConfigured(
self.device.display_name(),
));
}
SshClient::exec_as_devel_su(&mut self.session, command, &self.device.root_password)
.map_err(DeviceError::SshError)
.with_context(|| format!("Failed to execute as root: {}", command))
.map_err(DeviceError::SshError)
}
pub fn upload_file(&mut self, local_path: &Path, remote_path: &Path) -> Result<()> {
SshClient::upload(&mut self.session, local_path, remote_path)
.with_context(|| {
format!(
"Failed to upload {} to {}",
local_path.display(),
remote_path.display()
)
})
}
pub fn read_file_base64(&mut self, remote_path: &Path) -> Result<String, DeviceError> {
if self.device.root_password.is_empty() {
return Err(DeviceError::RootPasswordNotConfigured(
self.device.display_name(),
));
}
SshClient::read_file_base64(&mut self.session, remote_path, &self.device.root_password)
.map_err(DeviceError::SshError)
}
pub fn device(&self) -> &Device {
&self.device
}
pub fn device_name(&self) -> String {
self.device.display_name()
}
}