chore: refactor camera trait, optimize setter

Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
2025-10-18 23:46:14 +01:00
parent 0f5997042c
commit 6b0753b072
6 changed files with 132 additions and 130 deletions

View File

@@ -1,4 +1,3 @@
use anyhow::bail;
use rusb::GlobalContext;
use super::CameraImpl;
@@ -13,26 +12,6 @@ pub struct SupportedCamera<P: rusb::UsbContext> {
pub impl_factory: ImplFactory<P>,
}
impl<P: rusb::UsbContext> SupportedCamera<P> {
pub fn new_camera(&self, device: &rusb::Device<P>) -> anyhow::Result<Box<dyn CameraImpl<P>>> {
let descriptor = device.device_descriptor()?;
let matches =
descriptor.vendor_id() == self.vendor && descriptor.product_id() == self.product;
if !matches {
bail!(
"Device with vendor {:04x} and product {:04x} does not match {}",
descriptor.vendor_id(),
descriptor.product_id(),
self.name
);
}
Ok((self.impl_factory)())
}
}
macro_rules! default_camera_impl {
(
$const_name:ident,

View File

@@ -21,6 +21,8 @@ use ptp::{
use ptp_cursor::{PtpDeserialize, PtpSerialize};
use rusb::{GlobalContext, constants::LIBUSB_CLASS_IMAGE};
use crate::usb::find_endpoint;
const SESSION: u32 = 1;
pub struct Camera {
@@ -29,75 +31,6 @@ pub struct Camera {
}
impl Camera {
pub fn from_device(device: &rusb::Device<GlobalContext>) -> anyhow::Result<Self> {
for supported_camera in devices::SUPPORTED {
if let Ok(r#impl) = supported_camera.new_camera(device) {
let bus = device.bus_number();
let address = device.address();
let config_descriptor = device.active_config_descriptor()?;
let interface_descriptor = config_descriptor
.interfaces()
.flat_map(|i| i.descriptors())
.find(|x| x.class_code() == LIBUSB_CLASS_IMAGE)
.ok_or(rusb::Error::NotFound)?;
let interface = interface_descriptor.interface_number();
debug!("Found interface {interface}");
let handle = device.open()?;
handle.claim_interface(interface)?;
let bulk_in = Self::find_endpoint(
&interface_descriptor,
rusb::Direction::In,
rusb::TransferType::Bulk,
)?;
let bulk_out = Self::find_endpoint(
&interface_descriptor,
rusb::Direction::Out,
rusb::TransferType::Bulk,
)?;
let transaction_id = 0;
let chunk_size = r#impl.chunk_size();
let mut ptp = Ptp {
bus,
address,
interface,
bulk_in,
bulk_out,
handle,
transaction_id,
chunk_size,
};
debug!("Opening session");
let () = r#impl.open_session(&mut ptp, SESSION)?;
debug!("Session opened");
return Ok(Self { r#impl, ptp });
}
}
bail!("Device not supported");
}
fn find_endpoint(
interface_descriptor: &rusb::InterfaceDescriptor<'_>,
direction: rusb::Direction,
transfer_type: rusb::TransferType,
) -> Result<u8, rusb::Error> {
interface_descriptor
.endpoint_descriptors()
.find(|ep| ep.direction() == direction && ep.transfer_type() == transfer_type)
.map(|x| x.address())
.ok_or(rusb::Error::NotFound)
}
pub fn name(&self) -> &'static str {
self.r#impl.supported_camera().name
}
@@ -336,6 +269,76 @@ impl Drop for Camera {
}
}
impl TryFrom<&rusb::Device<GlobalContext>> for Camera {
type Error = anyhow::Error;
fn try_from(device: &rusb::Device<GlobalContext>) -> anyhow::Result<Self> {
let descriptor = device.device_descriptor()?;
let vendor = descriptor.vendor_id();
let product = descriptor.product_id();
for supported_camera in devices::SUPPORTED {
if vendor != supported_camera.vendor || product != supported_camera.product {
continue;
}
let r#impl = (supported_camera.impl_factory)();
let bus = device.bus_number();
let address = device.address();
let config_descriptor = device.active_config_descriptor()?;
let interface_descriptor = config_descriptor
.interfaces()
.flat_map(|i| i.descriptors())
.find(|x| x.class_code() == LIBUSB_CLASS_IMAGE)
.ok_or(rusb::Error::NotFound)?;
let interface = interface_descriptor.interface_number();
debug!("Found interface {interface}");
let handle = device.open()?;
handle.claim_interface(interface)?;
let bulk_in = find_endpoint(
&interface_descriptor,
rusb::Direction::In,
rusb::TransferType::Bulk,
)?;
let bulk_out = find_endpoint(
&interface_descriptor,
rusb::Direction::Out,
rusb::TransferType::Bulk,
)?;
let transaction_id = 0;
let chunk_size = r#impl.chunk_size();
let mut ptp = Ptp {
bus,
address,
interface,
bulk_in,
bulk_out,
handle,
transaction_id,
chunk_size,
};
debug!("Opening session");
let () = r#impl.open_session(&mut ptp, SESSION)?;
debug!("Session opened");
return Ok(Self { r#impl, ptp });
}
bail!("Device not supported");
}
}
pub trait CameraImpl<P: rusb::UsbContext> {
fn supported_camera(&self) -> &'static SupportedCamera<P>;

View File

@@ -279,7 +279,8 @@ impl DerefMut for FujiCustomSettingName {
impl TryFrom<String> for FujiCustomSettingName {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, anyhow::Error> {
fn try_from(value: String) -> anyhow::Result<Self> {
if value.len() > Self::MAX_LEN {
bail!("Value '{}' exceeds max length of {}", value, Self::MAX_LEN);
}
@@ -289,7 +290,8 @@ impl TryFrom<String> for FujiCustomSettingName {
impl FromStr for FujiCustomSettingName {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, anyhow::Error> {
fn from_str(s: &str) -> anyhow::Result<Self> {
if s.len() > Self::MAX_LEN {
bail!("Value '{}' exceeds max length of {}", s, Self::MAX_LEN);
}

View File

@@ -8,6 +8,7 @@ pub enum Input {
impl FromStr for Input {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "-" {
Ok(Self::Stdin)
@@ -34,6 +35,7 @@ pub enum Output {
impl FromStr for Output {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "-" {
Ok(Self::Stdout)

View File

@@ -97,7 +97,6 @@ fn handle_list(json: bool, device_id: Option<&str>) -> anyhow::Result<()> {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FilmSimulationRepr {
pub slot: FujiCustomSetting,
pub name: FujiCustomSettingName,
pub simulation: FujiFilmSimulation,
pub size: FujiImageSize,
@@ -121,7 +120,6 @@ pub struct FilmSimulationRepr {
impl fmt::Display for FilmSimulationRepr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Slot: {}", self.slot)?;
writeln!(f, "Name: {}", self.name)?;
writeln!(f, "Simulation: {}", self.simulation)?;
writeln!(f, "Size: {}", self.size)?;
@@ -156,7 +154,6 @@ fn handle_get(json: bool, device_id: Option<&str>, slot: FujiCustomSetting) -> a
camera.set_active_custom_setting(slot)?;
let repr = FilmSimulationRepr {
slot,
name: camera.get_custom_setting_name()?,
simulation: camera.get_film_simulation()?,
size: camera.get_image_size()?,
@@ -242,15 +239,17 @@ fn handle_set(
}
// White Balance
let white_balance = match &options.white_balance {
Some(white_balance) => {
if let Some(white_balance) = &options.white_balance {
camera.set_white_balance(*white_balance)?;
white_balance
}
None => &camera.get_white_balance()?,
};
if let Some(temperature) = &options.white_balance_temperature {
let white_balance = if let Some(white_balance) = &options.white_balance {
white_balance
} else {
&camera.get_white_balance()?
};
if *white_balance != FujiWhiteBalance::Temperature {
warn!("White Balance mode is not set to 'Temperature', refusing to set temperature")
} else {
@@ -267,12 +266,16 @@ fn handle_set(
}
// Exposure
let dynamic_range_priority = match &options.dynamic_range_priority {
Some(dynamic_range_priority) => {
if let Some(dynamic_range_priority) = &options.dynamic_range_priority {
camera.set_dynamic_range_priority(*dynamic_range_priority)?;
dynamic_range_priority
}
None => &camera.get_dynamic_range_priority()?,
if options.dynamic_range.is_some() || options.highlight.is_some() || options.shadow.is_some() {
let dynamic_range_priority =
if let Some(dynamic_range_priority) = &options.dynamic_range_priority {
dynamic_range_priority
} else {
&camera.get_dynamic_range_priority()?
};
if let Some(dynamic_range) = &options.dynamic_range {
@@ -298,6 +301,7 @@ fn handle_set(
camera.set_shadow_tone(*shadows)?;
}
}
}
Ok(())
}

View File

@@ -2,11 +2,23 @@ use anyhow::{anyhow, bail};
use crate::camera::Camera;
pub fn find_endpoint(
interface_descriptor: &rusb::InterfaceDescriptor<'_>,
direction: rusb::Direction,
transfer_type: rusb::TransferType,
) -> Result<u8, rusb::Error> {
interface_descriptor
.endpoint_descriptors()
.find(|ep| ep.direction() == direction && ep.transfer_type() == transfer_type)
.map(|x| x.address())
.ok_or(rusb::Error::NotFound)
}
pub fn get_connected_cameras() -> anyhow::Result<Vec<Camera>> {
let mut connected_cameras = Vec::new();
for device in rusb::devices()?.iter() {
if let Ok(camera) = Camera::from_device(&device) {
if let Ok(camera) = Camera::try_from(&device) {
connected_cameras.push(camera);
}
}
@@ -25,7 +37,7 @@ pub fn get_connected_camera_by_id(id: &str) -> anyhow::Result<Camera> {
for device in rusb::devices()?.iter() {
if device.bus_number() == bus && device.address() == address {
return Camera::from_device(&device);
return Camera::try_from(&device);
}
}