1
1
use crate :: version;
2
2
use progress_streams:: ProgressReader ;
3
3
use std:: path:: { Path , PathBuf } ;
4
- use std:: sync:: mpsc;
4
+ use std:: sync:: mpsc:: { self , TryRecvError } ;
5
5
use std:: sync:: mpsc:: { Receiver , Sender } ;
6
6
use std:: { fs, thread} ;
7
7
use tar:: Archive ;
8
8
use xz2:: read:: XzDecoder ;
9
9
10
- enum ExtractionStatusInternal {
10
+ enum ExtractionReport {
11
+ InProgress { percentage : usize } ,
12
+ Finished ,
13
+ }
14
+
15
+ enum ExtractionStateMachine {
11
16
NotStarted ,
12
- Started ( Receiver < Result < usize , String > > ) ,
17
+ Started ( Receiver < Result < ExtractionReport , String > > ) ,
13
18
Finished ,
14
19
}
15
20
@@ -21,22 +26,22 @@ pub enum ExtractionStatus {
21
26
}
22
27
23
28
pub struct ArchiveExtractor {
24
- receiver : ExtractionStatusInternal ,
29
+ receiver : ExtractionStateMachine ,
25
30
}
26
31
27
32
impl ArchiveExtractor {
28
33
pub fn new ( ) -> ArchiveExtractor {
29
34
ArchiveExtractor {
30
- receiver : ExtractionStatusInternal :: NotStarted ,
35
+ receiver : ExtractionStateMachine :: NotStarted ,
31
36
}
32
37
}
33
38
34
39
pub fn start_extraction ( & mut self , sub_folder_path : & Path ) {
35
40
match self . receiver {
36
- ExtractionStatusInternal :: NotStarted => {
37
- let ( sender, receiver) = mpsc:: channel :: < Result < usize , String > > ( ) ;
41
+ ExtractionStateMachine :: NotStarted => {
42
+ let ( sender, receiver) = mpsc:: channel :: < Result < ExtractionReport , String > > ( ) ;
38
43
extract_archive_new_thread ( sub_folder_path, sender) ;
39
- self . receiver = ExtractionStatusInternal :: Started ( receiver) ;
44
+ self . receiver = ExtractionStateMachine :: Started ( receiver) ;
40
45
}
41
46
_ => { }
42
47
}
@@ -45,31 +50,47 @@ impl ArchiveExtractor {
45
50
// This doesn't correctly handle the case where
46
51
pub fn poll_status ( & mut self ) -> ExtractionStatus {
47
52
match & mut self . receiver {
48
- ExtractionStatusInternal :: NotStarted => ExtractionStatus :: NotStarted ,
49
- ExtractionStatusInternal :: Started ( receiver) => {
50
- if let Ok ( progress) = receiver. try_recv ( ) {
51
- match progress {
52
- Ok ( progress) => {
53
- if progress >= 100 {
54
- self . receiver = ExtractionStatusInternal :: Finished
55
- }
56
- ExtractionStatus :: Started ( Some ( progress) )
53
+ ExtractionStateMachine :: NotStarted => ExtractionStatus :: NotStarted ,
54
+ ExtractionStateMachine :: Started ( receiver) => {
55
+ // Check if an extraction update was received, nothing was received, or if there was an error
56
+ let progress = match receiver. try_recv ( ) {
57
+ Ok ( progress) => progress,
58
+ Err ( e) => match e {
59
+ TryRecvError :: Empty => return ExtractionStatus :: Started ( None ) ,
60
+ TryRecvError :: Disconnected => {
61
+ return ExtractionStatus :: Error (
62
+ "ArchiveExtractor channel disconnected unexpectedly" . to_string ( ) ,
63
+ )
57
64
}
58
- Err ( error_str) => ExtractionStatus :: Error ( error_str) ,
65
+ } ,
66
+ } ;
67
+
68
+ // If there was an extraction error, report the error
69
+ let progress = match progress {
70
+ Ok ( progress) => progress,
71
+ Err ( error_str) => return ExtractionStatus :: Error ( error_str) ,
72
+ } ;
73
+
74
+ // If extraction complete, report 100%, and advance to final state
75
+ // Otherwise, return the percentage completion and stay in same state
76
+ let percentage: usize = match progress {
77
+ ExtractionReport :: Finished => {
78
+ self . receiver = ExtractionStateMachine :: Finished ;
79
+ 100
59
80
}
60
- } else {
61
- // Extraction is started but no additional progress to tell
62
- ExtractionStatus :: Started ( None )
63
- }
81
+ ExtractionReport :: InProgress { percentage } => percentage ,
82
+ } ;
83
+
84
+ ExtractionStatus :: Started ( Some ( percentage ) )
64
85
}
65
- ExtractionStatusInternal :: Finished => ExtractionStatus :: Finished ,
86
+ ExtractionStateMachine :: Finished => ExtractionStatus :: Finished ,
66
87
}
67
88
}
68
89
}
69
90
70
91
fn extract_archive_new_thread (
71
92
sub_folder_path : & Path ,
72
- progress_update : Sender < Result < usize , String > > ,
93
+ progress_update : Sender < Result < ExtractionReport , String > > ,
73
94
) {
74
95
let mut path_copy = PathBuf :: new ( ) ;
75
96
path_copy. push ( sub_folder_path) ;
@@ -79,7 +100,10 @@ fn extract_archive_new_thread(
79
100
} ) ;
80
101
}
81
102
82
- fn extract_archive ( sub_folder_path : & Path , progress_update : Sender < Result < usize , String > > ) {
103
+ fn extract_archive (
104
+ sub_folder_path : & Path ,
105
+ progress_update : Sender < Result < ExtractionReport , String > > ,
106
+ ) {
83
107
let saved_git_tag_path = sub_folder_path. join ( "installer_loader_extraction_lock.txt" ) ;
84
108
85
109
// During compilation, include the installer archive in .tar.xz format
@@ -103,7 +127,7 @@ fn extract_archive(sub_folder_path: &Path, progress_update: Sender<Result<usize,
103
127
if let Some ( percentage) = progress_counter. update ( progress_bytes) {
104
128
println ! ( "Extraction {}%" , percentage) ;
105
129
progress_update
106
- . send ( Ok ( percentage) )
130
+ . send ( Ok ( ExtractionReport :: InProgress { percentage } ) )
107
131
. expect ( "Failed to send progress update - aborting extraction" ) ;
108
132
}
109
133
} ) ;
@@ -123,7 +147,7 @@ You can also try 'Run as Administrator', but the installer may not work correctl
123
147
// so we don't need to extract again unless installer's version changes
124
148
write_extraction_lock ( & saved_git_tag_path) ;
125
149
progress_update
126
- . send ( Ok ( 100 ) )
150
+ . send ( Ok ( ExtractionReport :: Finished ) )
127
151
. expect ( "Failed to send progress update - aborting extraction" ) ;
128
152
println ! ( "Extraction Complete." ) ;
129
153
}
0 commit comments