11use std:: fs;
22use std:: process;
33
4- use crate :: apk:: { fetch_remote_index, find_best_compatible_version, parse_index_tar_gz, Apk , Package } ;
4+ use crate :: apk:: {
5+ fetch_remote_index, find_best_compatible_version, parse_apk_file, parse_index_tar_gz, Apk ,
6+ Package ,
7+ } ;
58use crate :: constants:: VELLUM_ROOT ;
69use crate :: device:: get_apk_arch;
710
@@ -22,6 +25,7 @@ pub fn handle_add(apk: &Apk, args: &[String]) {
2225
2326 let mut resolved_args: Vec < String > = Vec :: new ( ) ;
2427 let mut resolved_packages: Vec < String > = Vec :: new ( ) ;
28+ let mut local_apk_packages: Vec < Package > = Vec :: new ( ) ;
2529 let mut has_incompatible = false ;
2630
2731 for arg in args {
@@ -30,6 +34,14 @@ pub fn handle_add(apk: &Apk, args: &[String]) {
3034 continue ;
3135 }
3236
37+ if arg. ends_with ( ".apk" ) {
38+ resolved_args. push ( arg. clone ( ) ) ;
39+ if let Ok ( pkg) = parse_apk_file ( arg) {
40+ local_apk_packages. push ( pkg) ;
41+ }
42+ continue ;
43+ }
44+
3345 match find_best_compatible_version ( arg, & os_version, & index) {
3446 Some ( pkg) => {
3547 resolved_args. push ( format ! ( "{}={}" , pkg. name, pkg. version) ) ;
@@ -51,6 +63,34 @@ pub fn handle_add(apk: &Apk, args: &[String]) {
5163 process:: exit ( 1 ) ;
5264 }
5365
66+ let fetch_args: Vec < & str > = resolved_args
67+ . iter ( )
68+ . filter ( |a| !a. starts_with ( '-' ) && !a. ends_with ( ".apk" ) )
69+ . map ( |s| s. as_str ( ) )
70+ . collect ( ) ;
71+ if !fetch_args. is_empty ( ) {
72+ let _ = apk. fetch ( & fetch_args) ;
73+ }
74+
75+ let cached_packages = find_cached_apk_packages ( apk, & resolved_args) ;
76+ let mut packages_to_replace = find_packages_to_replace ( apk, & cached_packages) ;
77+ packages_to_replace. extend ( find_packages_to_replace ( apk, & local_apk_packages) ) ;
78+ if !packages_to_replace. is_empty ( ) {
79+ println ! ( "The following packages will be replaced:" ) ;
80+ for pkg in & packages_to_replace {
81+ println ! ( " - {pkg}" ) ;
82+ }
83+ println ! ( ) ;
84+
85+ let del_args: Vec < & str > = packages_to_replace. iter ( ) . map ( |s| s. as_str ( ) ) . collect ( ) ;
86+ let mut del_cmd = vec ! [ "del" ] ;
87+ del_cmd. extend ( del_args) ;
88+ if let Err ( e) = apk. run ( & del_cmd) {
89+ eprintln ! ( "Failed to remove replaced packages: {e}" ) ;
90+ process:: exit ( 1 ) ;
91+ }
92+ }
93+
5494 let mut cmd_args = vec ! [ "add" , "--cache-predownload" ] ;
5595 cmd_args. extend ( resolved_args. iter ( ) . map ( |s| s. as_str ( ) ) ) ;
5696
@@ -148,3 +188,70 @@ fn clean_world_file_pins(packages: &[String]) {
148188
149189 let _ = fs:: write ( & world_path, new_content + "\n " ) ;
150190}
191+
192+ fn find_packages_to_replace ( apk : & Apk , cached_packages : & [ Package ] ) -> Vec < String > {
193+ let installed = match apk. list_installed ( ) {
194+ Ok ( list) => list,
195+ Err ( _) => return Vec :: new ( ) ,
196+ } ;
197+
198+ let mut to_replace = Vec :: new ( ) ;
199+
200+ for pkg in cached_packages {
201+ for replaced in & pkg. replaces {
202+ if installed. contains ( replaced) && !to_replace. contains ( replaced) {
203+ to_replace. push ( replaced. clone ( ) ) ;
204+ }
205+ }
206+ }
207+
208+ to_replace
209+ }
210+
211+ fn find_cached_apk_packages ( apk : & Apk , package_specs : & [ String ] ) -> Vec < Package > {
212+ let cache_dir = apk. cache_dir ( ) ;
213+ let entries = match fs:: read_dir ( & cache_dir) {
214+ Ok ( e) => e,
215+ Err ( _) => return Vec :: new ( ) ,
216+ } ;
217+
218+ let apk_files: Vec < _ > = entries
219+ . flatten ( )
220+ . filter_map ( |entry| {
221+ let path = entry. path ( ) ;
222+ let name = path. file_name ( ) ?. to_str ( ) ?;
223+ if name. ends_with ( ".apk" ) {
224+ Some ( path)
225+ } else {
226+ None
227+ }
228+ } )
229+ . collect ( ) ;
230+
231+ let mut packages = Vec :: new ( ) ;
232+
233+ for spec in package_specs {
234+ let pkg_name = spec. split ( '=' ) . next ( ) . unwrap_or ( spec) ;
235+
236+ for apk_path in & apk_files {
237+ let file_name = match apk_path. file_name ( ) . and_then ( |n| n. to_str ( ) ) {
238+ Some ( n) => n,
239+ None => continue ,
240+ } ;
241+
242+ if file_name. starts_with ( & format ! ( "{pkg_name}-" ) ) {
243+ if let Some ( path_str) = apk_path. to_str ( ) {
244+ if let Ok ( pkg) = parse_apk_file ( path_str) {
245+ if pkg. name == pkg_name {
246+ packages. push ( pkg) ;
247+ break ;
248+ }
249+ }
250+ }
251+ }
252+ }
253+ }
254+
255+ packages
256+ }
257+
0 commit comments