@@ -9,8 +9,12 @@ use gtk::prelude::*;
99use gtk:: { gio, glib} ;
1010use std:: cell:: OnceCell ;
1111use std:: cell:: RefCell ;
12+ use std:: collections:: HashSet ;
13+ use std:: collections:: HashMap ;
1214use std:: path:: Path ;
1315use std:: time:: Duration ;
16+ use std:: pin:: Pin ;
17+ use std:: task:: { Context as TaskContext , Poll } ;
1418use tracing:: error;
1519use tracing:: info;
1620use tracing:: { debug, warn} ;
@@ -29,9 +33,6 @@ use crate::supported_terminals::{Terminal, TerminalRepository};
2933use crate :: tagged_object:: TaggedObject ;
3034
3135use serde:: Deserialize ;
32- use std:: collections:: HashMap ;
33- use std:: pin:: Pin ;
34- use std:: task:: { Context as TaskContext , Poll } ;
3536
3637/// Podman event structure
3738#[ derive( Debug , Clone , Deserialize ) ]
@@ -116,6 +117,15 @@ pub fn listen_podman_events(
116117 } )
117118}
118119
120+ #[ derive( Debug , Clone , Deserialize , Hash , Eq , PartialEq ) ]
121+ #[ serde( rename_all = "PascalCase" ) ]
122+ pub struct Image {
123+ #[ serde( rename = "Id" ) ]
124+ pub id : String ,
125+ #[ serde( rename = "Names" ) ]
126+ pub names : Option < Vec < String > > ,
127+ }
128+
119129mod imp {
120130 use crate :: query:: Query ;
121131
@@ -131,6 +141,7 @@ mod imp {
131141
132142 pub distrobox_version : Query < String > ,
133143 pub images_query : Query < Vec < String > > ,
144+ pub downloaded_images_query : Query < HashSet < String > > ,
134145 pub containers_query : Query < Vec < Container > > ,
135146
136147 pub containers : TypedListStore < Container > ,
@@ -166,6 +177,7 @@ mod imp {
166177 Ok ( String :: new ( ) )
167178 } ) ,
168179 images_query : Query :: new ( "images" . into ( ) , || async { Ok ( vec ! [ ] ) } ) ,
180+ downloaded_images_query : Query :: new ( "downloaded_images" . into ( ) , || async { Ok ( HashSet :: new ( ) ) } ) ,
169181 containers_query : Query :: new ( "containers" . into ( ) , || async { Ok ( vec ! [ ] ) } ) ,
170182 tasks : TypedListStore :: new ( ) ,
171183 selected_task : Default :: default ( ) ,
@@ -238,6 +250,15 @@ impl RootStore {
238250 }
239251 } ) ;
240252
253+ let this_clone = this. clone ( ) ;
254+ this. imp ( ) . downloaded_images_query . set_fetcher ( move || {
255+ let this_clone = this_clone. clone ( ) ;
256+ async move {
257+ dbg ! ( "Fetching downloaded images" ) ;
258+ this_clone. fetch_downloaded_images ( ) . await
259+ }
260+ } ) ;
261+
241262 let this_clone = this. clone ( ) ;
242263 this. imp ( ) . containers_query . set_fetcher ( move || {
243264 let this_clone = this_clone. clone ( ) ;
@@ -291,6 +312,10 @@ impl RootStore {
291312 self . imp ( ) . images_query . clone ( )
292313 }
293314
315+ pub fn downloaded_images_query ( & self ) -> Query < HashSet < String > > {
316+ self . imp ( ) . downloaded_images_query . clone ( )
317+ }
318+
294319 pub fn containers_query ( & self ) -> Query < Vec < Container > > {
295320 self . imp ( ) . containers_query . clone ( )
296321 }
@@ -703,6 +728,42 @@ impl RootStore {
703728 * self . imp ( ) . container_runtime . borrow_mut ( ) = Some ( runtime) ;
704729 Ok ( runtime)
705730 }
731+
732+ async fn fetch_downloaded_images ( & self ) -> anyhow:: Result < HashSet < String > > {
733+ let runtime = self . get_container_runtime ( ) . await ?;
734+ let mut cmd = Command :: new ( runtime. as_str ( ) ) ;
735+ cmd. arg ( "images" ) . arg ( "--format" ) . arg ( "json" ) ;
736+
737+ let output = self . run_to_string ( cmd) . await ?;
738+ dbg ! ( & output) ;
739+ // Some versions of podman/docker might return empty string if no images?
740+ if output. trim ( ) . is_empty ( ) {
741+ return Ok ( HashSet :: new ( ) ) ;
742+ }
743+
744+ // Handle potential JSON Lines vs JSON Array
745+ // Try parsing as array first
746+ let images_vec: Vec < Image > = match serde_json:: from_str :: < Vec < Image > > ( & output) {
747+ Ok ( images) => images,
748+ Err ( _) => {
749+ // Try parsing as JSON lines
750+ let mut images = Vec :: new ( ) ;
751+ for line in output. lines ( ) {
752+ if !line. trim ( ) . is_empty ( ) {
753+ images. push ( serde_json:: from_str :: < Image > ( line) ?) ;
754+ }
755+ }
756+ images
757+ }
758+ } ;
759+
760+ let names: HashSet < String > = images_vec
761+ . into_iter ( )
762+ . flat_map ( |img| img. names . unwrap_or_default ( ) )
763+ . collect ( ) ;
764+
765+ Ok ( names)
766+ }
706767}
707768
708769impl Default for RootStore {
@@ -728,7 +789,7 @@ mod tests {
728789 Ok ( "/home/user/Documents/custom-home-folder" ) ,
729790 ) ,
730791 ( "/home/user/Documents/custom-home-folder" , Ok ( "" ) , {
731- Ok ( "/home/user/Documents/custom- home- folder" )
792+ Ok ( "/home/user/Documents/custom/ home/ folder" )
732793 } ) ,
733794 // If the resolution fails and the path is from a sandbox, we expect an error
734795 ( "/run/user/1000/doc/xyz456" , Err ( ( ) ) , Err ( ( ) ) ) ,
0 commit comments