@@ -135,6 +135,23 @@ pub struct DeviceSnapshot {
135135}
136136
137137impl DeviceSnapshot {
138+ pub fn update_camera_formats (
139+ & mut self ,
140+ queried : & CameraOption ,
141+ formats : Vec < CameraFormat > ,
142+ ) -> bool {
143+ let Some ( camera) = self
144+ . cameras
145+ . iter_mut ( )
146+ . find ( |camera| camera. same_device ( queried) )
147+ else {
148+ return false ;
149+ } ;
150+ camera. best_format = formats. first ( ) . copied ( ) ;
151+ camera. formats = formats;
152+ true
153+ }
154+
138155 /// Enumerate everything. This blocks — AVFoundation camera discovery and the
139156 /// window-server queries are both slow enough to drop frames — so callers
140157 /// should run it on the background executor, never inside `render`.
@@ -282,6 +299,48 @@ mod input_enumeration_tests {
282299 } ]
283300 }
284301
302+ #[ test]
303+ fn completed_query_updates_matching_camera_capabilities ( ) {
304+ let queried = camera_fixture ( "camera-a" ) ;
305+ let mut snapshot = DeviceSnapshot {
306+ cameras : vec ! [ queried. clone( ) ] ,
307+ ..Default :: default ( )
308+ } ;
309+ assert ! ( snapshot. update_camera_formats( & queried, formats_fixture( ) ) ) ;
310+ assert_eq ! ( snapshot. cameras[ 0 ] . formats, formats_fixture( ) ) ;
311+ assert_eq ! (
312+ snapshot. cameras[ 0 ] . best_format,
313+ formats_fixture( ) . first( ) . copied( )
314+ ) ;
315+ }
316+
317+ #[ test]
318+ fn completed_query_cannot_overwrite_replacement_camera_capabilities ( ) {
319+ let queried = camera_fixture ( "camera-a" ) ;
320+ let mut changed_id = queried. clone ( ) ;
321+ changed_id. device_id = "camera-b" . into ( ) ;
322+ let mut changed_model = queried. clone ( ) ;
323+ changed_model. model_id =
324+ Some ( cap_camera:: ModelID :: try_from ( "046d:08e5" . to_string ( ) ) . unwrap ( ) ) ;
325+ let mut changed_label = queried. clone ( ) ;
326+ changed_label. label = "Replacement camera" . into ( ) ;
327+ for replacement in [ changed_id, changed_model, changed_label] {
328+ let mut snapshot = DeviceSnapshot {
329+ cameras : vec ! [ replacement. clone( ) ] ,
330+ ..Default :: default ( )
331+ } ;
332+ assert ! ( !snapshot. update_camera_formats( & queried, formats_fixture( ) ) ) ;
333+ assert_eq ! ( snapshot. cameras, vec![ replacement] ) ;
334+ }
335+ }
336+
337+ #[ test]
338+ fn completed_query_cannot_restore_disconnected_camera ( ) {
339+ let mut snapshot = DeviceSnapshot :: default ( ) ;
340+ assert ! ( !snapshot. update_camera_formats( & camera_fixture( "camera-a" ) , formats_fixture( ) ) ) ;
341+ assert ! ( snapshot. cameras. is_empty( ) ) ;
342+ }
343+
285344 #[ test]
286345 fn repeated_camera_refresh_does_not_reopen_unchanged_devices ( ) {
287346 let mut previous = vec ! [ camera_fixture( "camera-a" ) . with_formats( & [ ] , formats_fixture) ] ;
0 commit comments