use mtp_rs::mtp::{MtpDeviceInfo, Storage};
use mtp_rs::ptp::ObjectInfo;
use serde::Serialize;
use std::io::Write;
use super::error::{CliError, CliErrorKind};
pub fn print_json<T: Serialize>(value: &T) -> Result<(), CliError> {
serde_json::to_writer_pretty(std::io::stdout(), value)
.map_err(|e| CliError::new(CliErrorKind::Other, format!("write JSON: {e}")))?;
println!();
std::io::stdout()
.flush()
.map_err(|e| CliError::new(CliErrorKind::Other, format!("flush JSON: {e}")))?;
Ok(())
}
pub fn print_progress(label: &str, done: u64, total: u64, last_percent: &mut u64) {
let percent = done.saturating_mul(100).checked_div(total).unwrap_or(100);
if percent != *last_percent {
eprint!("\r{}: {}% ({}/{})", label, percent, done, total);
let _ = std::io::stderr().flush();
*last_percent = percent;
}
}
pub fn finish_progress() {
eprintln!();
}
#[derive(Debug, Serialize)]
pub struct DeviceRow {
pub vendor_id: u16,
pub product_id: u16,
pub manufacturer: Option<String>,
pub product: Option<String>,
pub serial_number: Option<String>,
pub location_id: u64,
pub location: String,
pub speed: Option<String>,
pub match_reason: String,
}
impl From<&MtpDeviceInfo> for DeviceRow {
fn from(info: &MtpDeviceInfo) -> Self {
Self {
vendor_id: info.vendor_id,
product_id: info.product_id,
manufacturer: info.manufacturer.clone(),
product: info.product.clone(),
serial_number: info.serial_number.clone(),
location_id: info.location_id,
location: format!("{:08x}", info.location_id),
speed: info.speed.map(|speed| format!("{speed:?}")),
match_reason: info.match_reason.as_str().to_string(),
}
}
}
#[derive(Debug, Serialize)]
pub struct StorageRow {
pub index: usize,
pub id: String,
pub id_raw: u32,
pub description: String,
pub volume_identifier: String,
pub max_capacity: u64,
pub free_space_bytes: u64,
pub access_capability: String,
pub storage_type: String,
pub filesystem_type: String,
}
impl StorageRow {
pub fn from_storage(index: usize, storage: &Storage) -> Self {
Self {
index,
id: format!("{:08x}", storage.id().0),
id_raw: storage.id().0,
description: storage.info().description.clone(),
volume_identifier: storage.info().volume_identifier.clone(),
max_capacity: storage.info().max_capacity,
free_space_bytes: storage.info().free_space_bytes,
access_capability: format!("{:?}", storage.info().access_capability),
storage_type: format!("{:?}", storage.info().storage_type),
filesystem_type: format!("{:?}", storage.info().filesystem_type),
}
}
}
#[derive(Debug, Serialize)]
pub struct ObjectRow {
pub handle: u32,
pub storage_id: u32,
pub parent: u32,
pub filename: String,
pub kind: String,
pub size: u64,
pub format: String,
}
impl From<&ObjectInfo> for ObjectRow {
fn from(info: &ObjectInfo) -> Self {
Self {
handle: info.handle.0,
storage_id: info.storage_id.0,
parent: info.parent.0,
filename: info.filename.clone(),
kind: if info.is_folder() { "folder" } else { "file" }.to_string(),
size: info.size,
format: format!("{:?}", info.format),
}
}
}