@@ -53,6 +53,14 @@ impl TryFrom<&str> for OutputFormat {
53
53
}
54
54
}
55
55
56
+ /// Either an input crate, markdown file, or nothing (--merge=finalize).
57
+ pub ( crate ) enum InputMode {
58
+ /// The `--merge=finalize` step does not need an input crate to rustdoc.
59
+ NoInputMergeFinalize ,
60
+ /// A crate or markdown file.
61
+ HasFile ( Input ) ,
62
+ }
63
+
56
64
/// Configuration options for rustdoc.
57
65
#[ derive( Clone ) ]
58
66
pub ( crate ) struct Options {
@@ -286,6 +294,12 @@ pub(crate) struct RenderOptions {
286
294
/// This field is only used for the JSON output. If it's set to true, no file will be created
287
295
/// and content will be displayed in stdout directly.
288
296
pub ( crate ) output_to_stdout : bool ,
297
+ /// Whether we should read or write rendered cross-crate info in the doc root.
298
+ pub ( crate ) should_merge : ShouldMerge ,
299
+ /// Path to crate-info for external crates.
300
+ pub ( crate ) include_parts_dir : Vec < PathToParts > ,
301
+ /// Where to write crate-info
302
+ pub ( crate ) parts_out_dir : Option < PathToParts > ,
289
303
}
290
304
291
305
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -345,7 +359,7 @@ impl Options {
345
359
early_dcx : & mut EarlyDiagCtxt ,
346
360
matches : & getopts:: Matches ,
347
361
args : Vec < String > ,
348
- ) -> Option < ( Input , Options , RenderOptions ) > {
362
+ ) -> Option < ( InputMode , Options , RenderOptions ) > {
349
363
// Check for unstable options.
350
364
nightly_options:: check_nightly_options ( early_dcx, matches, & opts ( ) ) ;
351
365
@@ -475,22 +489,34 @@ impl Options {
475
489
let ( lint_opts, describe_lints, lint_cap) = get_cmd_lint_options ( early_dcx, matches) ;
476
490
477
491
let input = if describe_lints {
478
- "" // dummy, this won't be used
492
+ InputMode :: HasFile ( make_input ( early_dcx , "" ) )
479
493
} else {
480
494
match matches. free . as_slice ( ) {
495
+ [ ] if matches. opt_str ( "merge" ) . as_deref ( ) == Some ( "finalize" ) => {
496
+ InputMode :: NoInputMergeFinalize
497
+ }
481
498
[ ] => dcx. fatal ( "missing file operand" ) ,
482
- [ input] => input,
499
+ [ input] => InputMode :: HasFile ( make_input ( early_dcx , input) ) ,
483
500
_ => dcx. fatal ( "too many file operands" ) ,
484
501
}
485
502
} ;
486
- let input = make_input ( early_dcx, input) ;
487
503
488
504
let externs = parse_externs ( early_dcx, matches, & unstable_opts) ;
489
505
let extern_html_root_urls = match parse_extern_html_roots ( matches) {
490
506
Ok ( ex) => ex,
491
507
Err ( err) => dcx. fatal ( err) ,
492
508
} ;
493
509
510
+ let parts_out_dir =
511
+ match matches. opt_str ( "parts-out-dir" ) . map ( |p| PathToParts :: from_flag ( p) ) . transpose ( ) {
512
+ Ok ( parts_out_dir) => parts_out_dir,
513
+ Err ( e) => dcx. fatal ( e) ,
514
+ } ;
515
+ let include_parts_dir = match parse_include_parts_dir ( matches) {
516
+ Ok ( include_parts_dir) => include_parts_dir,
517
+ Err ( e) => dcx. fatal ( e) ,
518
+ } ;
519
+
494
520
let default_settings: Vec < Vec < ( String , String ) > > = vec ! [
495
521
matches
496
522
. opt_str( "default-theme" )
@@ -732,6 +758,10 @@ impl Options {
732
758
let extern_html_root_takes_precedence =
733
759
matches. opt_present ( "extern-html-root-takes-precedence" ) ;
734
760
let html_no_source = matches. opt_present ( "html-no-source" ) ;
761
+ let should_merge = match parse_merge ( matches) {
762
+ Ok ( result) => result,
763
+ Err ( e) => dcx. fatal ( format ! ( "--merge option error: {e}" ) ) ,
764
+ } ;
735
765
736
766
if generate_link_to_definition && ( show_coverage || output_format != OutputFormat :: Html ) {
737
767
dcx. struct_warn (
@@ -819,6 +849,9 @@ impl Options {
819
849
no_emit_shared : false ,
820
850
html_no_source,
821
851
output_to_stdout,
852
+ should_merge,
853
+ include_parts_dir,
854
+ parts_out_dir,
822
855
} ;
823
856
Some ( ( input, options, render_options) )
824
857
}
@@ -894,3 +927,71 @@ fn parse_extern_html_roots(
894
927
}
895
928
Ok ( externs)
896
929
}
930
+
931
+ /// Path directly to crate-info file.
932
+ ///
933
+ /// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
934
+ #[ derive( Clone , Debug ) ]
935
+ pub ( crate ) struct PathToParts ( pub ( crate ) PathBuf ) ;
936
+
937
+ impl PathToParts {
938
+ fn from_flag ( path : String ) -> Result < PathToParts , String > {
939
+ let mut path = PathBuf :: from ( path) ;
940
+ // check here is for diagnostics
941
+ if path. exists ( ) && !path. is_dir ( ) {
942
+ Err ( format ! (
943
+ "--parts-out-dir and --include-parts-dir expect directories, found: {}" ,
944
+ path. display( ) ,
945
+ ) )
946
+ } else {
947
+ // if it doesn't exist, we'll create it. worry about that in write_shared
948
+ path. push ( "crate-info" ) ;
949
+ Ok ( PathToParts ( path) )
950
+ }
951
+ }
952
+ }
953
+
954
+ /// Reports error if --include-parts-dir / crate-info is not a file
955
+ fn parse_include_parts_dir ( m : & getopts:: Matches ) -> Result < Vec < PathToParts > , String > {
956
+ let mut ret = Vec :: new ( ) ;
957
+ for p in m. opt_strs ( "include-parts-dir" ) {
958
+ let p = PathToParts :: from_flag ( p) ?;
959
+ // this is just for diagnostic
960
+ if !p. 0 . is_file ( ) {
961
+ return Err ( format ! ( "--include-parts-dir expected {} to be a file" , p. 0 . display( ) ) ) ;
962
+ }
963
+ ret. push ( p) ;
964
+ }
965
+ Ok ( ret)
966
+ }
967
+
968
+ /// Controls merging of cross-crate information
969
+ #[ derive( Debug , Clone ) ]
970
+ pub ( crate ) struct ShouldMerge {
971
+ /// Should we append to existing cci in the doc root
972
+ pub ( crate ) read_rendered_cci : bool ,
973
+ /// Should we write cci to the doc root
974
+ pub ( crate ) write_rendered_cci : bool ,
975
+ }
976
+
977
+ /// Extracts read_rendered_cci and write_rendered_cci from command line arguments, or
978
+ /// reports an error if an invalid option was provided
979
+ fn parse_merge ( m : & getopts:: Matches ) -> Result < ShouldMerge , & ' static str > {
980
+ match m. opt_str ( "merge" ) . as_deref ( ) {
981
+ // default = read-write
982
+ None => Ok ( ShouldMerge { read_rendered_cci : true , write_rendered_cci : true } ) ,
983
+ Some ( "none" ) if m. opt_present ( "include-parts-dir" ) => {
984
+ Err ( "--include-parts-dir not allowed if --merge=none" )
985
+ }
986
+ Some ( "none" ) => Ok ( ShouldMerge { read_rendered_cci : false , write_rendered_cci : false } ) ,
987
+ Some ( "shared" ) if m. opt_present ( "parts-out-dir" ) || m. opt_present ( "include-parts-dir" ) => {
988
+ Err ( "--parts-out-dir and --include-parts-dir not allowed if --merge=shared" )
989
+ }
990
+ Some ( "shared" ) => Ok ( ShouldMerge { read_rendered_cci : true , write_rendered_cci : true } ) ,
991
+ Some ( "finalize" ) if m. opt_present ( "parts-out-dir" ) => {
992
+ Err ( "--parts-out-dir not allowed if --merge=finalize" )
993
+ }
994
+ Some ( "finalize" ) => Ok ( ShouldMerge { read_rendered_cci : false , write_rendered_cci : true } ) ,
995
+ Some ( _) => Err ( "argument to --merge must be `none`, `shared`, or `finalize`" ) ,
996
+ }
997
+ }
0 commit comments