1+ use  std:: sync:: atomic:: { AtomicUsize ,  Ordering } ; 
2+ use  anyhow:: { bail,  Result } ; 
3+ use  std:: path:: { Path ,  PathBuf } ; 
4+ use  std:: process:: Command ; 
5+ use  std:: io:: { self ,  Read ,  Write } ; 
6+ use  std:: fs; 
7+ use  rayon:: prelude:: * ; 
8+ use  num_cpus; 
9+ 
10+ pub  fn  run ( 
11+     stdin_path :  Option < String > , 
12+     all :  bool , 
13+     check :  bool , 
14+     files :  Vec < String > , 
15+     bsc_path_arg :  Option < PathBuf > , 
16+ )  -> Result < ( ) >  { 
17+     let  bsc_exe = match  bsc_path_arg { 
18+         Some ( path)  => path, 
19+         None  => find_bsc_exe ( ) ?, 
20+     } ; 
21+ 
22+     if  check && stdin_path. is_some ( )  { 
23+         bail ! ( "format -stdin cannot be used with -check flag" ) ; 
24+     } 
25+ 
26+     if  all { 
27+         if  stdin_path. is_some ( )  || !files. is_empty ( )  { 
28+             bail ! ( "format -all can not be in use with other flags" ) ; 
29+         } 
30+         format_all ( & bsc_exe,  check) ?; 
31+     }  else  if  stdin_path. is_some ( )  { 
32+         format_stdin ( & bsc_exe,  stdin_path. unwrap ( ) ) ?; 
33+     }  else  { 
34+         format_files ( & bsc_exe,  files,  check) ?; 
35+     } 
36+ 
37+     Ok ( ( ) ) 
38+ } 
39+ 
40+ fn  find_bsc_exe ( )  -> Result < PathBuf >  { 
41+     let  current_exe = std:: env:: current_exe ( ) ?; 
42+     let  mut  current_dir = current_exe. parent ( ) . unwrap_or_else ( || Path :: new ( "/" ) ) ; 
43+ 
44+     // Traverse up to find node_modules 
45+     let  node_modules_path = loop  { 
46+         let  potential_path = current_dir. join ( "node_modules" ) ; 
47+         if  potential_path. exists ( )  { 
48+             break  Some ( potential_path) ; 
49+         } 
50+         if  current_dir. parent ( ) . is_none ( )  { 
51+             break  None ; 
52+         } 
53+         current_dir = current_dir. parent ( ) . unwrap ( ) ; 
54+     } 
55+     . ok_or_else ( || anyhow:: anyhow!( "Could not find node_modules directory" ) ) ?; 
56+ 
57+     let  target = format ! ( "{}-{}" ,  std:: env:: consts:: OS ,  std:: env:: consts:: ARCH ) ; 
58+     let  bsc_path = node_modules_path
59+         . join ( "@rescript" ) 
60+         . join ( target) 
61+         . join ( "bsc.exe" ) ; 
62+ 
63+     if  !bsc_path. exists ( )  { 
64+         bail ! ( "bsc executable not found at {}" ,  bsc_path. display( ) ) ; 
65+     } 
66+     Ok ( bsc_path) 
67+ } 
68+ 
69+ fn  format_all ( bsc_exe :  & Path ,  check :  bool )  -> Result < ( ) >  { 
70+     let  output = Command :: new ( std:: env:: current_exe ( ) ?) 
71+         . arg ( "info" ) 
72+         . arg ( "-list-files" ) 
73+         . output ( ) ?; 
74+ 
75+     if  !output. status . success ( )  { 
76+         io:: stderr ( ) . write_all ( & output. stderr ) ?; 
77+         bail ! ( "Failed to list files" ) ; 
78+     } 
79+ 
80+     let  files_str = String :: from_utf8_lossy ( & output. stdout ) ; 
81+     let  files:  Vec < String >  = files_str
82+         . split ( '\n' ) 
83+         . filter ( |s| !s. trim ( ) . is_empty ( ) ) 
84+         . map ( |s| s. trim ( ) . to_string ( ) ) 
85+         . collect ( ) ; 
86+ 
87+     format_files ( bsc_exe,  files,  check) ?; 
88+     Ok ( ( ) ) 
89+ } 
90+ 
91+ fn  format_stdin ( bsc_exe :  & Path ,  stdin_path :  String )  -> Result < ( ) >  { 
92+     let  mut  input = String :: new ( ) ; 
93+     io:: stdin ( ) . read_to_string ( & mut  input) ?; 
94+ 
95+     let  mut  cmd = Command :: new ( bsc_exe) ; 
96+     cmd. arg ( "-format" ) . arg ( & stdin_path) ; 
97+     cmd. stdin ( std:: process:: Stdio :: piped ( ) ) ; 
98+     cmd. stdout ( std:: process:: Stdio :: piped ( ) ) ; 
99+     cmd. stderr ( std:: process:: Stdio :: piped ( ) ) ; 
100+ 
101+     let  mut  child = cmd. spawn ( ) ?; 
102+     let  mut  stdin = child. stdin . take ( ) . unwrap ( ) ; 
103+     std:: thread:: spawn ( move  || { 
104+         stdin. write_all ( input. as_bytes ( ) ) . unwrap ( ) ; 
105+     } ) ; 
106+ 
107+     let  output = child. wait_with_output ( ) ?; 
108+ 
109+     if  output. status . success ( )  { 
110+         io:: stdout ( ) . write_all ( & output. stdout ) ?; 
111+     } 
112+     else  { 
113+         io:: stderr ( ) . write_all ( & output. stderr ) ?; 
114+         bail ! ( "bsc exited with an error" ) ; 
115+     } 
116+ 
117+     Ok ( ( ) ) 
118+ } 
119+ 
120+ fn  format_files ( bsc_exe :  & Path ,  files :  Vec < String > ,  check :  bool )  -> Result < ( ) >  { 
121+     let  batch_size = 4  *  num_cpus:: get ( ) ; 
122+     let  incorrectly_formatted_files = AtomicUsize :: new ( 0 ) ; 
123+ 
124+     files. par_chunks ( batch_size) . try_for_each ( |batch| { 
125+         batch. iter ( ) . try_for_each ( |file| { 
126+             let  mut  cmd = Command :: new ( bsc_exe) ; 
127+             if  check { 
128+                 cmd. arg ( "-format" ) . arg ( file) ; 
129+             } 
130+             else  { 
131+                 cmd. arg ( "-o" ) . arg ( file) . arg ( "-format" ) . arg ( file) ; 
132+             } 
133+ 
134+             let  output = cmd. output ( ) ?; 
135+ 
136+             if  output. status . success ( )  { 
137+                 if  check { 
138+                     let  original_content = fs:: read_to_string ( file) ?; 
139+                     let  formatted_content = String :: from_utf8_lossy ( & output. stdout ) ; 
140+                     if  original_content != formatted_content { 
141+                         eprintln ! ( "[format check] {}" ,  file) ; 
142+                         incorrectly_formatted_files. fetch_add ( 1 ,  Ordering :: SeqCst ) ; 
143+                     } 
144+                 } 
145+             } 
146+             else  { 
147+                 io:: stderr ( ) . write_all ( & output. stderr ) ?; 
148+                 bail ! ( "bsc exited with an error for file {}" ,  file) ; 
149+             } 
150+             Ok ( ( ) )  as  Result < ( ) > 
151+         } ) 
152+     } ) ?; 
153+ 
154+     let  count = incorrectly_formatted_files. load ( Ordering :: SeqCst ) ; 
155+     if  count > 0  { 
156+         if  count == 1  { 
157+             eprintln ! ( "The file listed above needs formatting" ) ; 
158+         } 
159+         else  { 
160+             eprintln ! ( 
161+                 "The {} files listed above need formatting" , 
162+                 count
163+             ) ; 
164+         } 
165+         bail ! ( "Formatting check failed" ) ; 
166+     } 
167+ 
168+     Ok ( ( ) ) 
169+ } 
0 commit comments