@@ -12,23 +12,42 @@ pub fn handle_stat(args: &[String], default_style: &str) {
1212
1313 let mut style = default_style;
1414 let mut show_history = None ;
15+ let mut filter_user = None ;
16+ let mut filter_job_id = None ;
17+
1518 let mut i = 0 ;
1619 while i < args. len ( ) {
17- if args[ i] == "--style" && i + 1 < args. len ( ) {
20+ let arg = & args[ i] ;
21+ if arg == "stat" || arg. ends_with ( "fbqueue" ) || arg. ends_with ( "qstat" ) {
22+ i += 1 ;
23+ continue ;
24+ }
25+ if arg == "--style" && i + 1 < args. len ( ) {
1826 style = & args[ i+1 ] ;
1927 i += 2 ;
20- } else if args [ i ] == "-H" || args [ i ] == "--history" {
28+ } else if arg == "-H" || arg == "--history" {
2129 if i + 1 < args. len ( ) && args[ i+1 ] . parse :: < usize > ( ) . is_ok ( ) {
2230 show_history = Some ( args[ i+1 ] . parse :: < usize > ( ) . unwrap_or ( conf. history_limit ) ) ;
2331 i += 2 ;
2432 } else {
2533 show_history = Some ( conf. history_limit ) ;
2634 i += 1 ;
2735 }
36+ } else if arg == "-u" && i + 1 < args. len ( ) {
37+ filter_user = Some ( args[ i+1 ] . clone ( ) ) ;
38+ i += 2 ;
39+ } else if !arg. starts_with ( '-' ) {
40+ // Positional argument: Job ID (handle both "123" and "123.master")
41+ let id_part = arg. split ( '.' ) . next ( ) . unwrap_or ( arg) ;
42+ if id_part. chars ( ) . next ( ) . map_or ( false , |c| c. is_ascii_digit ( ) ) {
43+ filter_job_id = Some ( id_part. to_string ( ) ) ;
44+ }
45+ i += 1 ;
2846 } else {
2947 i += 1 ;
3048 }
3149 }
50+ // eprintln!("DEBUG: filter_job_id={:?}, filter_user={:?}, style={}", filter_job_id, filter_user, style);
3251
3352 let new_entries: Vec < _ > = fs:: read_dir ( fbq_dir. join ( "queue/new" ) ) . map ( |d| d. filter_map ( |e| e. ok ( ) ) . collect ( ) ) . unwrap_or_default ( ) ;
3453 let running_entries: Vec < _ > = fs:: read_dir ( fbq_dir. join ( "queue/running" ) ) . map ( |d| d. filter_map ( |e| e. ok ( ) ) . collect ( ) ) . unwrap_or_default ( ) ;
@@ -40,35 +59,62 @@ pub fn handle_stat(args: &[String], default_style: &str) {
4059
4160 for entry in & running_entries {
4261 if let Ok ( j) = job:: parse_job_file ( & entry. path ( ) ) {
62+ if let Some ( ref fid) = filter_job_id {
63+ let jid_norm = j. id . split ( '.' ) . next ( ) . unwrap_or ( & j. id ) ;
64+ if jid_norm != fid { continue ; }
65+ }
66+ if let Some ( ref u) = filter_user { if & j. user != u { continue ; } }
4367 * used_caps. entry ( j. queue . clone ( ) ) . or_insert ( 0 ) += j. cost ;
4468 total_used += j. cost ;
4569 running_jobs. push ( j) ;
4670 }
4771 }
72+
4873 let mut sorted_new = new_entries;
4974 sorted_new. sort_by_key ( |e| e. file_name ( ) . to_str ( ) . unwrap_or ( "0" ) . trim_end_matches ( ".job" ) . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ;
5075 for entry in sorted_new {
51- if let Ok ( j) = job:: parse_job_file ( & entry. path ( ) ) { pending_jobs. push ( j) ; }
76+ if let Ok ( j) = job:: parse_job_file ( & entry. path ( ) ) {
77+ if let Some ( ref fid) = filter_job_id {
78+ let jid_norm = j. id . split ( '.' ) . next ( ) . unwrap_or ( & j. id ) ;
79+ if jid_norm != fid { continue ; }
80+ }
81+ if let Some ( ref u) = filter_user { if & j. user != u { continue ; } }
82+ pending_jobs. push ( j) ;
83+ }
84+ }
85+
86+ let mut history_jobs = Vec :: new ( ) ;
87+ if show_history. is_some ( ) || filter_job_id. is_some ( ) {
88+ let limit = show_history. unwrap_or ( conf. history_limit ) ;
89+ for dir in & [ "queue/done" , "queue/failed" ] {
90+ if let Ok ( entries) = fs:: read_dir ( fbq_dir. join ( dir) ) {
91+ for entry in entries. filter_map ( |e| e. ok ( ) ) {
92+ if let Ok ( j) = job:: parse_job_file ( & entry. path ( ) ) {
93+ if let Some ( ref fid) = filter_job_id {
94+ let jid_norm = j. id . split ( '.' ) . next ( ) . unwrap_or ( & j. id ) ;
95+ if jid_norm != fid { continue ; }
96+ }
97+ if let Some ( ref u) = filter_user { if & j. user != u { continue ; } }
98+ history_jobs. push ( j) ;
99+ }
100+ }
101+ }
102+ }
103+ history_jobs. sort_by ( |a, b| b. id . parse :: < usize > ( ) . unwrap_or ( 0 ) . cmp ( & a. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ) ;
104+ if show_history. is_some ( ) {
105+ history_jobs. truncate ( limit) ;
106+ }
52107 }
53108
54109 let has_pending = !pending_jobs. is_empty ( ) ;
55110 let has_running = !running_entries. is_empty ( ) ;
56111
57112 if style == "pbs" {
58- print_pbs_style ( running_jobs, pending_jobs) ;
113+ print_pbs_style ( running_jobs, pending_jobs, history_jobs , show_history . is_some ( ) || filter_job_id . is_some ( ) ) ;
59114 } else {
60115 if let Some ( limit) = show_history {
61116 println ! ( "Recent Job History (Last {}):" , limit) ;
62- let mut history = Vec :: new ( ) ;
63- for dir in & [ "queue/done" , "queue/failed" ] {
64- if let Ok ( entries) = fs:: read_dir ( fbq_dir. join ( dir) ) {
65- for entry in entries. filter_map ( |e| e. ok ( ) ) {
66- if let Ok ( j) = job:: parse_job_file ( & entry. path ( ) ) { history. push ( j) ; }
67- }
68- }
69- }
70- history. sort_by ( |a, b| b. id . parse :: < usize > ( ) . unwrap_or ( 0 ) . cmp ( & a. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ) ;
71- for j in history. iter ( ) . take ( limit) {
117+ for j in history_jobs {
72118 let status = j. status . as_deref ( ) . unwrap_or ( "DONE" ) ;
73119 println ! ( " ID: {:>4} | NAME: {:<15} | USER: {:<10} | QUEUE: {:<10} | STATUS: {}" , j. id, j. name, j. user, j. queue, status) ;
74120 }
@@ -108,21 +154,32 @@ pub fn handle_stat(args: &[String], default_style: &str) {
108154 }
109155}
110156
111- fn print_pbs_style ( mut running : Vec < job:: Job > , mut pending : Vec < job:: Job > ) {
157+ fn print_pbs_style ( mut running : Vec < job:: Job > , mut pending : Vec < job:: Job > , history : Vec < job :: Job > , is_history_mode : bool ) {
112158 println ! ( "{:<16} {:<16} {:<16} {:<8} S {:<5}" , "Job id" , "Name" , "User" , "Time Use" , "Queue" ) ;
113159 println ! ( "{:-<16} {:-<16} {:-<16} {:-<8} - {:-<5}" , "" , "" , "" , "" , "" ) ;
114160
115161 let now = utils:: get_now ( ) ;
116- running. sort_by_key ( |j| j. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ;
117- pending. sort_by_key ( |j| j. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ;
118-
119- for j in running {
120- let elapsed = if let Some ( start) = j. start_at { now - start } else { 0 } ;
121- let time_use = format ! ( "{:02}:{:02}:{:02}" , elapsed / 3600 , ( elapsed % 3600 ) / 60 , elapsed % 60 ) ;
122- println ! ( "{:<16} {:<16} {:<16} {:<8} R {:<5}" , format!( "{}.master" , j. id) , truncate( & j. name, 16 ) , truncate( & j. user, 16 ) , time_use, j. queue) ;
123- }
124- for j in pending {
125- println ! ( "{:<16} {:<16} {:<16} {:<8} Q {:<5}" , format!( "{}.master" , j. id) , truncate( & j. name, 16 ) , truncate( & j. user, 16 ) , "0" , j. queue) ;
162+
163+ if !is_history_mode {
164+ running. sort_by_key ( |j| j. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ;
165+ for j in running {
166+ let elapsed = if let Some ( start) = j. start_at { now - start } else { 0 } ;
167+ let time_use = format ! ( "{:02}:{:02}:{:02}" , elapsed / 3600 , ( elapsed % 3600 ) / 60 , elapsed % 60 ) ;
168+ println ! ( "{:<16} {:<16} {:<16} {:<8} R {:<5}" , format!( "{}.master" , j. id) , truncate( & j. name, 16 ) , truncate( & j. user, 16 ) , time_use, j. queue) ;
169+ }
170+ pending. sort_by_key ( |j| j. id . parse :: < usize > ( ) . unwrap_or ( 0 ) ) ;
171+ for j in pending {
172+ println ! ( "{:<16} {:<16} {:<16} {:<8} Q {:<5}" , format!( "{}.master" , j. id) , truncate( & j. name, 16 ) , truncate( & j. user, 16 ) , "0" , j. queue) ;
173+ }
174+ } else {
175+ for j in history {
176+ let status_char = match j. status . as_deref ( ) {
177+ Some ( "DONE" ) => "F" ,
178+ Some ( "FAILED" ) | Some ( "CANCELLED" ) | Some ( "TIMEOUT" ) => "E" ,
179+ _ => "F" ,
180+ } ;
181+ println ! ( "{:<16} {:<16} {:<16} {:<8} {} {:<5}" , format!( "{}.master" , j. id) , truncate( & j. name, 16 ) , truncate( & j. user, 16 ) , "0" , status_char, j. queue) ;
182+ }
126183 }
127184}
128185
0 commit comments