1- use std:: io:: { self , Write } ;
1+ use std:: io:: { self , BufRead , Write } ;
2+
3+ #[ cfg( unix) ]
4+ use std:: fs:: File ;
5+ #[ cfg( windows) ]
6+ use std:: fs:: OpenOptions ;
27
38use clap:: Args ;
49use color_eyre:: eyre:: eyre;
@@ -24,34 +29,69 @@ pub struct DeleteArgs {
2429
2530impl SubcommandExecutor for DeleteArgs {
2631 fn run_command ( self , mut otp_database : OTPDatabase ) -> color_eyre:: Result < OTPDatabase > {
32+ if otp_database. elements_ref ( ) . is_empty ( ) {
33+ return Err ( eyre ! ( "There are no elements to delete" ) ) ;
34+ }
35+
2736 let index_to_delete = self
2837 . index
2938 . and_then ( |i| i. checked_sub ( 1 ) )
39+ // Match by issues or label if index filter is missing
3040 . or_else ( || get_first_matching_element ( & otp_database, & self ) )
3141 . ok_or ( eyre ! ( "No code has been found using the given arguments" ) ) ?;
3242
33- let mut output = String :: with_capacity ( 1 ) ;
34-
35- let element = otp_database. elements_ref ( ) . get ( index_to_delete) . unwrap ( ) ;
36- print ! (
37- "Are you sure you want to delete the {}th code ({}, {}) [Y,N]: " ,
38- index_to_delete + 1 ,
39- element. issuer,
40- element. label
41- ) ;
42- io:: stdout ( ) . flush ( ) ?;
43+ if let Some ( element) = otp_database. elements_ref ( ) . get ( index_to_delete) {
44+ print ! (
45+ "Are you sure you want to delete the {}th code ({}, {}) [Y,N]: " ,
46+ index_to_delete + 1 ,
47+ element. issuer,
48+ element. label
49+ ) ;
50+ io:: stdout ( ) . flush ( ) ?;
4351
44- io :: stdin ( ) . read_line ( & mut output) ?;
52+ let output = read_confirmation_line ( ) ?;
4553
46- if output. trim ( ) . eq_ignore_ascii_case ( "y" ) {
47- otp_database. delete_element ( index_to_delete) ;
48- Ok ( otp_database)
54+ if output. trim ( ) . eq_ignore_ascii_case ( "y" ) {
55+ otp_database. delete_element ( index_to_delete) ;
56+ Ok ( otp_database)
57+ } else {
58+ Err ( eyre ! ( "Operation interrupt by the user" ) )
59+ }
4960 } else {
50- Err ( eyre ! ( "Operation interrupt by the user" ) )
61+ Err ( eyre ! ( "Missing {}th code to delete" , index_to_delete + 1 ) )
5162 }
5263 }
5364}
5465
66+ fn read_confirmation_line ( ) -> color_eyre:: Result < String > {
67+ let mut output = String :: with_capacity ( 1 ) ;
68+
69+ if io:: stdin ( ) . read_line ( & mut output) ? > 0 {
70+ return Ok ( output) ;
71+ }
72+
73+ #[ cfg( unix) ]
74+ {
75+ output. clear ( ) ;
76+ let mut tty = io:: BufReader :: new ( File :: open ( "/dev/tty" ) ?) ;
77+ tty. read_line ( & mut output) ?;
78+ return Ok ( output) ;
79+ }
80+
81+ #[ cfg( windows) ]
82+ {
83+ output. clear ( ) ;
84+ let mut tty = io:: BufReader :: new ( OpenOptions :: new ( ) . read ( true ) . open ( "CONIN$" ) ?) ;
85+ tty. read_line ( & mut output) ?;
86+ return Ok ( output) ;
87+ }
88+
89+ #[ allow( unreachable_code) ]
90+ Err ( eyre ! (
91+ "Unable to read confirmation answer from standard input or terminal"
92+ ) )
93+ }
94+
5595fn get_first_matching_element (
5696 otp_database : & OTPDatabase ,
5797 delete_args : & DeleteArgs ,
0 commit comments