@@ -2,7 +2,11 @@ use std::collections::HashSet;
22use std:: num:: NonZeroUsize ;
33use std:: path:: PathBuf ;
44
5- use clap:: Parser ;
5+ use clap:: { CommandFactory , Parser } ;
6+
7+ mod sam;
8+
9+ pub use sam:: { OutSamFormat , OutSamSortOrder , OutSamType , OutSamUnmapped } ;
610
711// ---------------------------------------------------------------------------
812// Run mode enum
@@ -102,108 +106,6 @@ impl std::str::FromStr for IntronStrandFilter {
102106 }
103107}
104108
105- // ---------------------------------------------------------------------------
106- // SAM output type enums
107- // ---------------------------------------------------------------------------
108-
109- /// STAR's `--outSAMtype` format component.
110- #[ derive( Debug , Clone , PartialEq , Eq , Default ) ]
111- pub enum OutSamFormat {
112- #[ default]
113- Sam ,
114- Bam ,
115- None ,
116- }
117-
118- /// STAR's `--outSAMtype` sort order component (only applies to BAM).
119- #[ derive( Debug , Clone , PartialEq , Eq ) ]
120- pub enum OutSamSortOrder {
121- Unsorted ,
122- SortedByCoordinate ,
123- }
124-
125- /// Combined `--outSAMtype` value.
126- #[ derive( Debug , Clone , PartialEq , Eq , Default ) ]
127- pub struct OutSamType {
128- pub format : OutSamFormat ,
129- pub sort_order : Option < OutSamSortOrder > ,
130- }
131-
132- impl std:: fmt:: Display for OutSamType {
133- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
134- match ( & self . format , & self . sort_order ) {
135- ( OutSamFormat :: Sam , _) => write ! ( f, "SAM" ) ,
136- ( OutSamFormat :: None , _) => write ! ( f, "None" ) ,
137- ( OutSamFormat :: Bam , Some ( OutSamSortOrder :: SortedByCoordinate ) ) => {
138- write ! ( f, "BAM SortedByCoordinate" )
139- }
140- ( OutSamFormat :: Bam , _) => write ! ( f, "BAM Unsorted" ) ,
141- }
142- }
143- }
144-
145- impl clap:: FromArgMatches for OutSamType {
146- fn from_arg_matches ( matches : & clap:: ArgMatches ) -> Result < Self , clap:: Error > {
147- let mut s = Self :: default ( ) ;
148- s. update_from_arg_matches ( matches) ?;
149- Ok ( s)
150- }
151-
152- fn update_from_arg_matches ( & mut self , matches : & clap:: ArgMatches ) -> Result < ( ) , clap:: Error > {
153- let Some ( values) = matches. get_many :: < String > ( "outSAMtype" ) else {
154- return Ok ( ( ) ) ;
155- } ;
156- let tokens: Vec < & str > = values. map ( String :: as_str) . collect ( ) ;
157- * self = match tokens. as_slice ( ) {
158- [ "SAM" ] => Self {
159- format : OutSamFormat :: Sam ,
160- sort_order : None ,
161- } ,
162- [ "None" ] => Self {
163- format : OutSamFormat :: None ,
164- sort_order : None ,
165- } ,
166- [ "BAM" , "Unsorted" ] => Self {
167- format : OutSamFormat :: Bam ,
168- sort_order : Some ( OutSamSortOrder :: Unsorted ) ,
169- } ,
170- [ "BAM" , "SortedByCoordinate" ] => Self {
171- format : OutSamFormat :: Bam ,
172- sort_order : Some ( OutSamSortOrder :: SortedByCoordinate ) ,
173- } ,
174- other => {
175- return Err ( clap:: Error :: raw (
176- clap:: error:: ErrorKind :: InvalidValue ,
177- format ! (
178- "invalid value '{}' for '--outSAMtype': expected 'SAM', 'None', 'BAM Unsorted', or 'BAM SortedByCoordinate'\n " ,
179- other. join( " " )
180- ) ,
181- ) ) ;
182- }
183- } ;
184- Ok ( ( ) )
185- }
186- }
187-
188- impl clap:: Args for OutSamType {
189- fn augment_args ( cmd : clap:: Command ) -> clap:: Command {
190- cmd. arg (
191- clap:: Arg :: new ( "outSAMtype" )
192- . long ( "outSAMtype" )
193- . num_args ( 1 ..=2 )
194- . default_values ( [ "SAM" ] )
195- . help (
196- "Output type: SAM, BAM Unsorted, BAM SortedByCoordinate, None. \
197- Provide as space-separated tokens, e.g. \" BAM SortedByCoordinate\" .",
198- ) ,
199- )
200- }
201-
202- fn augment_args_for_update ( cmd : clap:: Command ) -> clap:: Command {
203- Self :: augment_args ( cmd)
204- }
205- }
206-
207109// ---------------------------------------------------------------------------
208110// Standard output streaming
209111// ---------------------------------------------------------------------------
@@ -257,30 +159,6 @@ impl std::str::FromStr for OutReadsUnmapped {
257159 }
258160}
259161
260- // ---------------------------------------------------------------------------
261- // SAM unmapped output
262- // ---------------------------------------------------------------------------
263-
264- #[ derive( Debug , Clone , PartialEq , Eq , Default ) ]
265- pub enum OutSamUnmapped {
266- #[ default]
267- None ,
268- Within ,
269- WithinKeepPairs ,
270- }
271-
272- impl std:: str:: FromStr for OutSamUnmapped {
273- type Err = String ;
274- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
275- match s {
276- "None" => Ok ( Self :: None ) ,
277- "Within" => Ok ( Self :: Within ) ,
278- "Within KeepPairs" => Ok ( Self :: WithinKeepPairs ) ,
279- _ => Err ( format ! ( "unknown outSAMunmapped value: '{s}'" ) ) ,
280- }
281- }
282- }
283-
284162// ---------------------------------------------------------------------------
285163// Output filter type
286164// ---------------------------------------------------------------------------
@@ -435,8 +313,7 @@ pub struct Parameters {
435313 #[ arg( long = "outSAMattrRGline" , num_args = 1 .., default_values_t = vec![ "-" . to_string( ) ] ) ]
436314 pub out_sam_attr_rg_line : Vec < String > ,
437315
438- /// Unmapped reads in SAM output: None or Within
439- #[ arg( long = "outSAMunmapped" , default_value = "None" ) ]
316+ #[ command( flatten) ]
440317 pub out_sam_unmapped : OutSamUnmapped ,
441318
442319 /// Output unmapped reads to FASTQ file(s): None or Fastx
@@ -943,8 +820,13 @@ impl Parameters {
943820 pub fn parse_from < T : Into < std:: ffi:: OsString > + Clone > (
944821 args : impl IntoIterator < Item = T > ,
945822 ) -> Self {
946- Self :: try_parse_from ( args)
947- . unwrap_or_else ( |e| if cfg ! ( test) { panic ! ( "{e}" ) } else { e. exit ( ) } )
823+ Self :: try_parse_from ( args) . unwrap_or_else ( |e| {
824+ if cfg ! ( test) {
825+ panic ! ( "{e}" )
826+ } else {
827+ e. format ( & mut <Self as CommandFactory >:: command ( ) ) . exit ( )
828+ }
829+ } )
948830 }
949831
950832 /// Parse and validate parameter combinations.
@@ -1262,6 +1144,40 @@ mod tests {
12621144 assert ! ( try_parse( & [ "--readFilesIn" , "r.fq" , "--outSAMtype" , "BAM" ] ) . is_err( ) ) ;
12631145 }
12641146
1147+ #[ test]
1148+ fn out_sam_unmapped_parsing ( ) {
1149+ let p = try_parse ( & [ "--readFilesIn" , "r.fq" ] ) . unwrap ( ) ;
1150+ assert_eq ! ( p. out_sam_unmapped, OutSamUnmapped :: None ) ;
1151+
1152+ let p = try_parse ( & [ "--readFilesIn" , "r.fq" , "--outSAMunmapped" , "None" ] ) . unwrap ( ) ;
1153+ assert_eq ! ( p. out_sam_unmapped, OutSamUnmapped :: None ) ;
1154+
1155+ let p = try_parse ( & [ "--readFilesIn" , "r.fq" , "--outSAMunmapped" , "Within" ] ) . unwrap ( ) ;
1156+ assert_eq ! ( p. out_sam_unmapped, OutSamUnmapped :: Within ) ;
1157+
1158+ let p = try_parse ( & [
1159+ "--readFilesIn" ,
1160+ "r.fq" ,
1161+ "--outSAMunmapped" ,
1162+ "Within" ,
1163+ "KeepPairs" ,
1164+ ] )
1165+ . unwrap ( ) ;
1166+ assert_eq ! ( p. out_sam_unmapped, OutSamUnmapped :: WithinKeepPairs ) ;
1167+
1168+ assert ! ( try_parse( & [ "--readFilesIn" , "r.fq" , "--outSAMunmapped" , "Bogus" ] ) . is_err( ) ) ;
1169+ assert ! (
1170+ try_parse( & [
1171+ "--readFilesIn" ,
1172+ "r.fq" ,
1173+ "--outSAMunmapped" ,
1174+ "Within" ,
1175+ "Bogus"
1176+ ] )
1177+ . is_err( )
1178+ ) ;
1179+ }
1180+
12651181 #[ test]
12661182 fn chimeric_params ( ) {
12671183 let p = try_parse ( & [
0 commit comments