1- use std:: {
2- collections:: HashMap ,
3- io:: { BufRead , Read } ,
4- path:: PathBuf ,
5- process:: Command ,
6- } ;
1+ use std:: { collections:: HashMap , io:: Read , path:: PathBuf } ;
72
83use anyhow:: { Context , bail} ;
94use bytesize:: ByteSize ;
@@ -21,7 +16,6 @@ use tokio::{
2116} ;
2217
2318type Str = Box < str > ;
24- type PackageVersions = HashMap < Str , Vec < ( Str , Str , Str , PathBuf ) > > ;
2519
2620static PACMAN_CACHE : & str = "/var/cache/pacman/pkg/" ;
2721
@@ -70,49 +64,32 @@ pub(crate) async fn do_boring_download(
7064}
7165
7266pub ( crate ) fn find_deltaupgrade_candidates (
73- global : & crate :: GlobalState ,
67+ _global : & crate :: GlobalState ,
7468 blacklist : & [ Str ] ,
75- fuz : bool ,
69+ _fuz : bool ,
7670) -> Result < ( Vec < ( String , Package , Package , Mmap , u64 ) > , Vec < Url > ) , anyhow:: Error > {
77- let upgrades = Command :: new ( "pacman" ) . args ( [ "-Sup" ] ) . output ( ) ?. stdout ;
78- let packageversions = build_package_versions ( ) . expect ( "io error on local disk" ) ;
79- let ( mut delta_upgrades, downloads) : ( Vec < _ > , Vec < _ > ) = upgrades
80- . lines ( )
81- . map ( |l| l. expect ( "pacman abborted output???" ) )
82- . filter ( |l| !l. starts_with ( "file" ) )
83- . map ( |line| {
84- let ( _, filename) = line. rsplit_once ( '/' ) . unwrap ( ) ;
85- let pkg = Package :: try_from ( filename) . unwrap ( ) ;
86- let name = pkg. get_name ( ) ;
87- if ( & blacklist) . into_iter ( ) . any ( |e| * * e == * name) {
88- info ! ( "{name} is blacklisted, skipping" ) ;
89- return Err ( line) ;
90- }
91- if let Some ( ( oldpkg, oldpath) ) = newest_cached ( & packageversions, & pkg. get_name ( ) ) . or_else ( || {
92- if fuz {
93- let ( alternative, _) = packageversions
94- . keys ( )
95- . map ( |name| ( name, strsim:: levenshtein ( name, pkg. get_name ( ) ) ) )
96- . filter ( |( _name, sim) | sim <= & 2 )
97- . min ( ) ?;
98- let prompt = format ! (
99- "could not find cached package for {name}, {alternative} has a similar name, use that instead?"
100- ) ;
101- global. multi . suspend ( || {
102- if dialoguer:: Confirm :: new ( ) . with_prompt ( prompt) . interact ( ) . unwrap ( ) {
103- newest_cached ( & packageversions, alternative)
104- } else {
105- None
106- }
107- } )
108- } else {
109- None
110- }
111- } ) {
112- // Try to find the decompressed size for better progress monitoring
113- let oldfile = std:: fs:: File :: open ( oldpath) . expect ( "io error on local disk" ) ;
71+ let upgrades = libalpm_rs:: upgrade_urls ( & [ "core" , "extra" , "multilib" ] ) ;
72+
73+ let mut direct_downloads = Vec :: new ( ) ;
74+ let mut deltas = Vec :: new ( ) ;
75+ for ( url, old, new) in upgrades {
76+ assert_eq ! ( old. i, new. i) ;
77+ use libalpm_rs:: db:: QuickResolve ;
78+ // Download already done
79+ if url. starts_with ( "file:/" ) {
80+ continue ;
81+ }
82+ let i = old. i . borrow ( ) ;
83+ let old_name = old. name . r ( & i) ;
84+ if ( & blacklist) . into_iter ( ) . any ( |e| * * e == * old_name) {
85+ info ! ( "{old_name} is blacklisted, skipping" ) ;
86+ }
87+ let old_version = old. version . r ( & i) ;
88+ let cached = std:: fs:: File :: open ( format ! ( "{PACMAN_CACHE}/{old_name}-{old_version}.pkg.tar.zstd" ) ) ;
89+ match cached {
90+ Ok ( f) => {
11491 // Safety: I promise to not open the same file as writable at the same time
115- let oldfile = unsafe { Mmap :: map ( & oldfile ) . expect ( "mmap failed" ) } ;
92+ let oldfile = unsafe { Mmap :: map ( & f ) . expect ( "mmap failed" ) } ;
11693 // Testing reveals the average size to be 17.2 MB
11794 let default_size = 17 * 1024 * 1024 ;
11895 // Due to pacman packages being compressed in streaming mode
@@ -121,19 +98,32 @@ pub(crate) fn find_deltaupgrade_candidates(
12198 // The real ratio is 18/47.4, 4/11 is close enough
12299 . map ( |s| ( s as u64 ) * 4 / 11 )
123100 . unwrap_or_else ( || {
124- debug ! ( "using default size for {name}" ) ;
101+ let new_name = new. name . r ( & i) ;
102+ debug ! ( "using default size for {new_name}" ) ;
125103 default_size
126104 } ) ;
127- Ok ( ( line, pkg, oldpkg, oldfile, ( dec_size as u64 ) ) )
128- } else {
129- info ! ( "no cached package found, leaving {} for pacman" , filename) ;
130- return Err ( line) ;
105+ let oldpkg = Package :: from_parts ( (
106+ old_name. into ( ) ,
107+ old_version. into ( ) ,
108+ old. arch . as_str ( ) . into ( ) ,
109+ "pkg.tar.zstd" . into ( ) ,
110+ ) ) ;
111+ let new_filename = new. filename . unwrap ( ) . r ( & i) ;
112+ let newpkg = Package :: try_from ( new_filename) ?;
113+
114+ deltas. push ( ( url, oldpkg, newpkg, oldfile, ( dec_size as u64 ) ) ) ;
131115 }
132- } )
133- . partition_result ( ) ;
134- delta_upgrades. sort_unstable_by_key ( |( _, _, _, _, size) | std:: cmp:: Reverse ( * size) ) ;
135- let downloads: Result < Vec < Url > , _ > = downloads. into_iter ( ) . map ( |l| Url :: parse ( & l) ) . collect ( ) ;
136- Ok ( ( delta_upgrades, downloads?) )
116+ Err ( e) => {
117+ if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
118+ //TODO: re-add fuzzy logic
119+ direct_downloads. push ( Url :: parse ( & url) ?) ;
120+ } else {
121+ return Err ( e) ?;
122+ }
123+ }
124+ }
125+ }
126+ return Ok ( ( deltas, direct_downloads) ) ;
137127}
138128
139129pub async fn sync_db ( global : crate :: GlobalState , server : Url , name : Str ) -> anyhow:: Result < ( ) > {
@@ -235,54 +225,6 @@ pub async fn sync_db(global: crate::GlobalState, server: Url, name: Str) -> anyh
235225 Ok ( ( ) )
236226}
237227
238- /// {package -> [(version, arch, trailer, path)]}
239- fn build_package_versions ( ) -> std:: io:: Result < PackageVersions > {
240- let mut package_versions: PackageVersions = HashMap :: new ( ) ;
241- for line in std:: fs:: read_dir ( PACMAN_CACHE ) ? {
242- let line = line?;
243- if !line. file_type ( ) ?. is_file ( ) {
244- continue ;
245- }
246- let filename = line. file_name ( ) ;
247- let filename = filename. to_string_lossy ( ) ;
248- if !filename. ends_with ( ".zst" ) {
249- continue ;
250- }
251- let path = line. path ( ) . into ( ) ;
252- let package = Package :: try_from ( & * filename) . expect ( "non-pkg zstd file in pacman cache dir?" ) ;
253- let ( name, version, arch, trailer) = package. destructure ( ) ;
254-
255- match package_versions. entry ( name) {
256- std:: collections:: hash_map:: Entry :: Occupied ( mut e) => {
257- e. get_mut ( ) . push ( ( version, arch, trailer, path) ) ;
258- }
259- std:: collections:: hash_map:: Entry :: Vacant ( e) => {
260- e. insert ( vec ! [ ( version, arch, trailer, path) ] ) ;
261- }
262- }
263- }
264- package_versions. shrink_to_fit ( ) ;
265-
266- for e in package_versions. values_mut ( ) {
267- e. sort ( )
268- }
269-
270- Ok ( package_versions)
271- }
272-
273- /// returns the newest package in /var/cache/pacman/pkg that
274- /// fits the passed package
275- fn newest_cached ( pv : & PackageVersions , package_name : & str ) -> Option < ( Package , PathBuf ) > {
276- pv. get ( package_name)
277- . map ( |e| e. last ( ) . expect ( "each entry has at least one version" ) )
278- . map ( |( version, arch, trailer, path) | {
279- (
280- Package :: from_parts ( ( package_name. into ( ) , version. clone ( ) , arch. clone ( ) , trailer. clone ( ) ) ) ,
281- path. clone ( ) ,
282- )
283- } )
284- }
285-
286228/// Calculates and prints some stats about bandwidth savings
287229pub ( crate ) fn calc_stats ( count : usize ) -> std:: io:: Result < ( ) > {
288230 let mut deltas: HashMap < ( Str , Str , Str ) , u64 > = HashMap :: new ( ) ;
0 commit comments