@@ -6,11 +6,13 @@ use std::thread;
66use std:: fs;
77use std:: time:: Duration ;
88use clap:: { Parser , Subcommand } ;
9+ use std:: path:: Path ;
910
1011use colored:: * ;
1112
13+ /// CLI root
1214#[ derive( Parser ) ]
13- #[ command( name = "ShellShare " ) ]
15+ #[ command( name = "shellshare " ) ]
1416#[ command( about = "CLI application to share files peer-to-peer" , long_about = None ) ]
1517struct Cli {
1618 #[ command( subcommand) ]
@@ -19,24 +21,37 @@ struct Cli {
1921
2022#[ derive( Subcommand ) ]
2123enum Commands {
24+ /// Start the tracker
2225 Tracker {
2326 #[ arg( short, long) ]
2427 port : String ,
2528 } ,
29+
30+ /// Start or interact with a peer
2631 Peer {
2732 #[ arg( short, long) ]
2833 port : String ,
2934
3035 #[ arg( short, long) ]
3136 tracker : String ,
37+
38+ #[ command( subcommand) ]
39+ action : Option < PeerAction > ,
3240 } ,
33- Download {
34- #[ arg( short, long) ]
35- file : String ,
41+ }
3642
37- #[ arg( short, long) ]
38- tracker : String ,
43+ #[ derive( Subcommand ) ]
44+ enum PeerAction {
45+ /// Upload a file to the network
46+ Upload {
47+ file_path : String ,
3948 } ,
49+ /// Download a file from the network
50+ Download {
51+ file_name : String ,
52+ } ,
53+ /// List available files
54+ List ,
4055}
4156
4257fn start_file_server ( port : & str ) {
@@ -55,10 +70,10 @@ fn start_file_server(port: &str) {
5570 let path = format ! ( "files/{}" , filename) ;
5671 match fs:: read ( & path) {
5772 Ok ( data) => {
58- stream. write_all ( & data) . unwrap ( ) ;
73+ let _ = stream. write_all ( & data) ;
5974 }
6075 Err ( _) => {
61- stream. write_all ( b"ERROR" ) . unwrap ( ) ;
76+ let _ = stream. write_all ( b"ERROR" ) ;
6277 }
6378 }
6479 }
@@ -69,9 +84,12 @@ fn start_file_server(port: &str) {
6984}
7085
7186fn register_with_tracker ( peer_addr : & str , tracker_addr : & str ) {
72- let files = fs:: read_dir ( "files" ) . unwrap ( ) ;
73- let mut file_list = Vec :: new ( ) ;
87+ let files = fs:: read_dir ( "files" ) . unwrap_or_else ( |_| {
88+ fs:: create_dir_all ( "files" ) . unwrap ( ) ;
89+ fs:: read_dir ( "files" ) . unwrap ( )
90+ } ) ;
7491
92+ let mut file_list = Vec :: new ( ) ;
7593 for entry in files {
7694 if let Ok ( entry) = entry {
7795 if let Some ( name) = entry. file_name ( ) . to_str ( ) {
@@ -82,15 +100,39 @@ fn register_with_tracker(peer_addr: &str, tracker_addr: &str) {
82100
83101 let msg = format ! ( "REGISTER|{}|{}" , peer_addr, file_list. join( "," ) ) ;
84102 if let Ok ( mut stream) = TcpStream :: connect ( tracker_addr) {
85- stream. write_all ( msg. as_bytes ( ) ) . unwrap ( ) ;
103+ let _ = stream. write_all ( msg. as_bytes ( ) ) ;
86104 let mut buf = [ 0 ; 512 ] ;
87- stream. read ( & mut buf) . unwrap ( ) ;
105+ let _ = stream. read ( & mut buf) ;
88106 println ! ( "Registered with tracker" ) ;
89107 } else {
90108 println ! ( "Failed to connect to tracker" ) ;
91109 }
92110}
93111
112+ fn list_files_from_tracker ( tracker_addr : & str ) {
113+ let query = "LIST" . to_string ( ) ;
114+
115+ if let Ok ( mut tracker_stream) = TcpStream :: connect ( tracker_addr) {
116+ if tracker_stream. write_all ( query. as_bytes ( ) ) . is_err ( ) {
117+ println ! ( "Failed to send LIST command to tracker." ) ;
118+ return ;
119+ }
120+
121+ let mut buf = [ 0 ; 4096 ] ;
122+ let size = tracker_stream. read ( & mut buf) . unwrap_or ( 0 ) ;
123+ let response = String :: from_utf8_lossy ( & buf[ ..size] ) ;
124+
125+ if response. trim ( ) . is_empty ( ) {
126+ println ! ( "No files available on the network." ) ;
127+ } else {
128+ println ! ( "Available files on network:\n {}" , response) ;
129+ }
130+ } else {
131+ println ! ( "Failed to connect to tracker." ) ;
132+ }
133+ }
134+
135+
94136fn query_tracker_and_download ( filename : & str , tracker_addr : & str ) {
95137 let query = format ! ( "QUERY|{}" , filename) ;
96138 if let Ok ( mut tracker_stream) = TcpStream :: connect ( tracker_addr) {
@@ -119,6 +161,7 @@ fn query_tracker_and_download(filename: &str, tracker_addr: &str) {
119161 continue ;
120162 }
121163
164+ fs:: create_dir_all ( "downloads" ) . unwrap ( ) ;
122165 fs:: write ( format ! ( "downloads/{}" , filename) , & file_buf) . unwrap ( ) ;
123166 println ! ( "Downloaded {} from {}" , filename, peer) ;
124167 return ;
@@ -129,18 +172,30 @@ fn query_tracker_and_download(filename: &str, tracker_addr: &str) {
129172 }
130173}
131174
175+ fn upload_file ( file_path : & str , tracker_addr : & str , peer_addr : & str ) {
176+ let path = Path :: new ( file_path) ;
177+ if !path. exists ( ) {
178+ println ! ( "File {} does not exist!" , file_path) ;
179+ return ;
180+ }
181+
182+ fs:: create_dir_all ( "files" ) . unwrap ( ) ;
183+ let filename = path. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( ) ;
184+ let dest = format ! ( "files/{}" , filename) ;
185+
186+ if let Err ( e) = fs:: copy ( file_path, & dest) {
187+ println ! ( "Failed to copy file: {}" , e) ;
188+ return ;
189+ }
190+
191+ println ! ( "Uploaded {} to local peer storage." , filename) ;
192+ register_with_tracker ( peer_addr, tracker_addr) ;
193+ }
194+
132195fn print_ascii_logo ( ) {
133196 match fs:: read_to_string ( "logo-ascii.txt" ) {
134197 Ok ( logo) => {
135- // Define a smoother gradient palette
136- let palette = [
137- "bright_red" ,
138- "red" ,
139- "magenta" ,
140- "bright_magenta" ,
141- "purple" ,
142- ] ;
143-
198+ let palette = [ "bright_red" , "red" , "magenta" , "bright_magenta" , "purple" ] ;
144199 for ( i, line) in logo. lines ( ) . enumerate ( ) {
145200 let color = match i % palette. len ( ) {
146201 0 => line. bright_red ( ) ,
@@ -163,8 +218,6 @@ fn print_ascii_logo() {
163218 }
164219}
165220
166-
167-
168221fn main ( ) {
169222 print_ascii_logo ( ) ;
170223 let cli = Cli :: parse ( ) ;
@@ -173,23 +226,37 @@ fn main() {
173226 Commands :: Tracker { port } => {
174227 tracker:: start_tracker ( & port) ;
175228 }
176- Commands :: Peer { port, tracker } => {
229+ Commands :: Peer { port, tracker, action } => {
177230 let peer_addr = format ! ( "127.0.0.1:{}" , port) ;
178- let port_clone = port. clone ( ) ;
179231
180- thread:: spawn ( move || {
181- start_file_server ( & port_clone) ;
182- } ) ;
232+ match action {
233+ Some ( PeerAction :: List ) => {
234+ list_files_from_tracker ( & tracker) ;
235+ }
236+ Some ( PeerAction :: Download { file_name } ) => {
237+ query_tracker_and_download ( & file_name, & tracker) ;
238+ }
239+ Some ( PeerAction :: Upload { file_path } ) => {
240+ upload_file ( & file_path, & tracker, & peer_addr) ;
241+ }
242+ None => {
243+ let port_clone = port. clone ( ) ;
244+
245+ thread:: spawn ( move || {
246+ start_file_server ( & port_clone) ;
247+ } ) ;
183248
184- thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
185- register_with_tracker ( & peer_addr, & tracker) ;
186- println ! ( "Peer running. Listening on {} and connected to tracker {}" , port, tracker) ;
187- loop {
188- thread:: sleep ( Duration :: from_secs ( 60 ) ) ;
249+ thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
250+ register_with_tracker ( & peer_addr, & tracker) ;
251+ println ! (
252+ "Peer running. Listening on {} and connected to tracker {}" ,
253+ port, tracker
254+ ) ;
255+ loop {
256+ thread:: sleep ( Duration :: from_secs ( 60 ) ) ;
257+ }
258+ }
189259 }
190260 }
191- Commands :: Download { file, tracker } => {
192- query_tracker_and_download ( & file, & tracker) ;
193- }
194261 }
195262}
0 commit comments