11use crate :: fakers:: {
22 Child , Command , CommandRunner , FdMode , InnerCommandRunner , NullCommandRunnerBuilder ,
33} ;
4+ use serde:: { Deserialize , Deserializer } ;
45use std:: {
56 cell:: LazyCell ,
67 collections:: BTreeMap ,
8+ env,
9+ ffi:: OsString ,
710 future:: Future ,
811 io,
12+ os:: unix:: ffi:: OsStringExt ,
913 path:: { Path , PathBuf } ,
1014 pin:: Pin ,
1115 process:: Output ,
@@ -18,7 +22,110 @@ mod desktop_file;
1822
1923pub use desktop_file:: * ;
2024
21- const POSIX_FIND_AND_CONCAT_DESKTOP_FILES : & str = "grep -L '^[[:space:]]*NoDisplay[[:space:]]*=[[:space:]]*true[[:space:]]*$' /usr/share/applications/*.desktop 2>/dev/null | while IFS= read -r file; do printf '# START FILE %s\n ' \" $file\" ; cat \" $file\" ; done" ;
25+ const POSIX_FIND_AND_CONCAT_DESKTOP_FILES : & str = r#"
26+ set -o pipefail >/dev/null 2>&1 # TODO: Join with the following (without piping), as it is part of POSIX 2022, but Debian 13 Trixie and Ubuntu 24.10 Oracular Oriole are the first releases with a `dash` version (>=0.5.12-7) supporting this flag. See also https://github.com/koalaman/shellcheck/issues/2555 and https://metadata.ftp-master.debian.org/changelogs/main/d/dash/stable_changelog
27+ set -eu
28+
29+ base16FunctionDefinition="$(cat <<'EOF'
30+ base16() {
31+ if [ "$#" -eq 0 ]; then
32+ cat
33+ else
34+ printf '%s' "$1"
35+ fi | od -vt x1 -A n | tr -d '[[:space:]]'
36+ }
37+ EOF
38+ )"
39+
40+ eval "$base16FunctionDefinition"
41+
42+ dumpDesktopFiles() {
43+ if ! [ -d "$1" ]; then
44+ return
45+ fi
46+
47+ find "$1" -name '*.desktop' -not -exec grep -q '^[[:space:]]*NoDisplay[[:space:]]*=[[:space:]]*true[[:space:]]*$' '{}' \; -exec sh -c "$(set +o);$base16FunctionDefinition;"'printf '\''"%s"="%s"\n'\'' "$(base16 "$1")" "$(base16 <"$1")"' - '{}' \;
48+ }
49+
50+ printf 'home_dir="%s"\n' "$(base16 "$HOME")"
51+
52+ printf '[system]\n'
53+ dumpDesktopFiles /usr/share/applications
54+
55+ printf '[user]\n'
56+ dumpDesktopFiles "$HOME/.local/share/applications"
57+ "# ;
58+
59+ #[ derive( Deserialize , Debug ) ]
60+ struct DesktopFiles {
61+ #[ serde( deserialize_with = "DesktopFiles::deserialize_path" ) ]
62+ home_dir : PathBuf ,
63+ #[ serde( deserialize_with = "DesktopFiles::deserialize_desktop_files" ) ]
64+ system : BTreeMap < PathBuf , String > ,
65+ #[ serde( deserialize_with = "DesktopFiles::deserialize_desktop_files" ) ]
66+ user : BTreeMap < PathBuf , String > ,
67+ }
68+
69+ impl DesktopFiles {
70+ fn decode_hex < E : serde:: de:: Error > ( hex_str : & str ) -> Result < Vec < u8 > , E > {
71+ if hex_str. len ( ) % 2 != 0 {
72+ return Err ( E :: invalid_length (
73+ hex_str. len ( ) ,
74+ & "hex string to have an even lenght" ,
75+ ) ) ;
76+ }
77+
78+ ( 0 ..hex_str. len ( ) )
79+ . step_by ( 2 )
80+ . map ( |i| u8:: from_str_radix ( & hex_str[ i..=i + 1 ] , 16 ) )
81+ . collect :: < Result < _ , _ > > ( )
82+ . map_err ( |e| {
83+ E :: custom ( format_args ! (
84+ "hex string contains non hex characters: {e:?}"
85+ ) )
86+ } )
87+ }
88+
89+ fn decode_utf8_from_hex < E : serde:: de:: Error > ( hex_str : & str ) -> Result < String , E > {
90+ String :: from_utf8 ( Self :: decode_hex ( hex_str) ?) . map_err ( |e| {
91+ E :: custom ( format_args ! (
92+ "decoded hex string does not represent valid UTF-8: {e:?}"
93+ ) )
94+ } )
95+ }
96+
97+ fn decode_path_from_hex < E : serde:: de:: Error > ( hex_str : & str ) -> Result < PathBuf , E > {
98+ Ok ( PathBuf :: from ( OsString :: from_vec ( Self :: decode_hex (
99+ hex_str,
100+ ) ?) ) )
101+ }
102+
103+ fn deserialize_path < ' de , D : Deserializer < ' de > > ( deserializer : D ) -> Result < PathBuf , D :: Error > {
104+ Self :: decode_path_from_hex ( & String :: deserialize ( deserializer) ?)
105+ }
106+
107+ fn deserialize_desktop_files < ' de , D : Deserializer < ' de > > (
108+ deserializer : D ,
109+ ) -> Result < BTreeMap < PathBuf , String > , D :: Error > {
110+ BTreeMap :: < String , String > :: deserialize ( deserializer) ?
111+ . into_iter ( )
112+ . map ( |( path, content) | {
113+ Ok ( (
114+ Self :: decode_path_from_hex ( & path) ?,
115+ Self :: decode_utf8_from_hex ( & content) ?,
116+ ) )
117+ } )
118+ . collect ( )
119+ }
120+
121+ fn into_map ( self ) -> BTreeMap < PathBuf , String > {
122+ let mut desktop_files = self . system ;
123+ if env:: home_dir ( ) != Some ( self . home_dir ) {
124+ desktop_files. extend ( self . user )
125+ }
126+ desktop_files
127+ }
128+ }
22129
23130#[ derive( Clone ) ]
24131pub struct FlatpakCommandRunner {
@@ -624,20 +731,15 @@ impl Distrobox {
624731 "-c" ,
625732 POSIX_FIND_AND_CONCAT_DESKTOP_FILES ,
626733 ] ) ;
627- let concatenated_files = self . cmd_output_string ( cmd) . await ?;
628- debug ! ( concatenated_files = concatenated_files) ;
629- let res = concatenated_files
630- . split ( "# START FILE " )
631- . skip ( 1 )
632- . map ( |file_content| {
633- let file_path = file_content. lines ( ) . next ( ) . map ( |name| name. trim_start ( ) ) ;
634- (
635- file_path. unwrap_or_default ( ) . to_string ( ) ,
636- file_content. to_string ( ) ,
637- )
638- } )
639- . collect ( ) ;
640- Ok ( res)
734+ let desktop_files: DesktopFiles = toml:: from_str ( & self . cmd_output_string ( cmd) . await ?)
735+ . map_err ( |e| Error :: ParseOutput ( format ! ( "{e:?}" ) ) ) ?;
736+ debug ! ( desktop_files = format_args!( "{desktop_files:#?}" ) ) ;
737+
738+ Ok ( desktop_files
739+ . into_map ( )
740+ . into_iter ( )
741+ . map ( |( path, content) | ( path. to_string_lossy ( ) . into_owned ( ) , content) )
742+ . collect :: < Vec < _ > > ( ) )
641743 }
642744
643745 pub async fn list_apps ( & self , box_name : & str ) -> Result < Vec < ExportableApp > , Error > {
0 commit comments