1
- use std:: collections:: { BTreeSet , HashMap } ;
1
+ use std:: collections:: BTreeMap ;
2
+ use std:: collections:: BTreeSet ;
3
+ use std:: collections:: HashMap ;
2
4
use std:: fs:: { self , File } ;
3
5
use std:: io:: prelude:: * ;
4
6
use std:: io:: SeekFrom ;
@@ -32,6 +34,7 @@ use crate::util::HumanBytes;
32
34
use crate :: { drop_println, ops} ;
33
35
use anyhow:: { bail, Context as _} ;
34
36
use cargo_util:: paths;
37
+ use cargo_util_schemas:: messages;
35
38
use flate2:: { Compression , GzBuilder } ;
36
39
use tar:: { Builder , EntryType , Header , HeaderMode } ;
37
40
use tracing:: debug;
@@ -40,10 +43,38 @@ use unicase::Ascii as UncasedAscii;
40
43
mod vcs;
41
44
mod verify;
42
45
46
+ /// Message format for `cargo package`.
47
+ ///
48
+ /// Currently only affect the output of the `--list` flag.
49
+ #[ derive( Debug , Clone ) ]
50
+ pub enum PackageMessageFormat {
51
+ Human ,
52
+ Json ,
53
+ }
54
+
55
+ impl PackageMessageFormat {
56
+ pub const POSSIBLE_VALUES : [ & str ; 2 ] = [ "human" , "json" ] ;
57
+
58
+ pub const DEFAULT : & str = "human" ;
59
+ }
60
+
61
+ impl std:: str:: FromStr for PackageMessageFormat {
62
+ type Err = anyhow:: Error ;
63
+
64
+ fn from_str ( s : & str ) -> Result < PackageMessageFormat , anyhow:: Error > {
65
+ match s {
66
+ "human" => Ok ( PackageMessageFormat :: Human ) ,
67
+ "json" => Ok ( PackageMessageFormat :: Json ) ,
68
+ f => bail ! ( "unknown message format `{f}`" ) ,
69
+ }
70
+ }
71
+ }
72
+
43
73
#[ derive( Clone ) ]
44
74
pub struct PackageOpts < ' gctx > {
45
75
pub gctx : & ' gctx GlobalContext ,
46
76
pub list : bool ,
77
+ pub fmt : PackageMessageFormat ,
47
78
pub check_metadata : bool ,
48
79
pub allow_dirty : bool ,
49
80
pub include_lockfile : bool ,
@@ -78,9 +109,13 @@ enum FileContents {
78
109
79
110
enum GeneratedFile {
80
111
/// Generates `Cargo.toml` by rewriting the original.
81
- Manifest ,
82
- /// Generates `Cargo.lock` in some cases (like if there is a binary).
83
- Lockfile ,
112
+ ///
113
+ /// Associated path is the original manifest path.
114
+ Manifest ( PathBuf ) ,
115
+ /// Generates `Cargo.lock`.
116
+ ///
117
+ /// Associated path is the path to the original lock file, if existing.
118
+ Lockfile ( Option < PathBuf > ) ,
84
119
/// Adds a `.cargo_vcs_info.json` file if in a git repo.
85
120
VcsInfo ( vcs:: VcsInfo ) ,
86
121
}
@@ -236,8 +271,33 @@ fn do_package<'a>(
236
271
let ar_files = prepare_archive ( ws, & pkg, & opts) ?;
237
272
238
273
if opts. list {
239
- for ar_file in & ar_files {
240
- drop_println ! ( ws. gctx( ) , "{}" , ar_file. rel_str) ;
274
+ match opts. fmt {
275
+ PackageMessageFormat :: Human => {
276
+ // While this form is called "human",
277
+ // it keeps the old file-per-line format for compatibility.
278
+ for ar_file in & ar_files {
279
+ drop_println ! ( ws. gctx( ) , "{}" , ar_file. rel_str) ;
280
+ }
281
+ }
282
+ PackageMessageFormat :: Json => {
283
+ let message = messages:: PackageList {
284
+ id : pkg. package_id ( ) . to_spec ( ) ,
285
+ files : BTreeMap :: from_iter ( ar_files. into_iter ( ) . map ( |f| {
286
+ let file = match f. contents {
287
+ FileContents :: OnDisk ( path) => messages:: PackageFile :: Copy { path } ,
288
+ FileContents :: Generated (
289
+ GeneratedFile :: Manifest ( path)
290
+ | GeneratedFile :: Lockfile ( Some ( path) ) ,
291
+ ) => messages:: PackageFile :: Generate { path : Some ( path) } ,
292
+ FileContents :: Generated (
293
+ GeneratedFile :: VcsInfo ( _) | GeneratedFile :: Lockfile ( None ) ,
294
+ ) => messages:: PackageFile :: Generate { path : None } ,
295
+ } ;
296
+ ( f. rel_path , file)
297
+ } ) ) ,
298
+ } ;
299
+ let _ = ws. gctx ( ) . shell ( ) . print_json ( & message) ;
300
+ }
241
301
}
242
302
} else {
243
303
let tarball = create_package ( ws, & pkg, ar_files, local_reg. as_ref ( ) ) ?;
@@ -444,7 +504,9 @@ fn build_ar_list(
444
504
. push ( ArchiveFile {
445
505
rel_path : PathBuf :: from ( "Cargo.toml" ) ,
446
506
rel_str : "Cargo.toml" . to_string ( ) ,
447
- contents : FileContents :: Generated ( GeneratedFile :: Manifest ) ,
507
+ contents : FileContents :: Generated ( GeneratedFile :: Manifest (
508
+ pkg. manifest_path ( ) . to_owned ( ) ,
509
+ ) ) ,
448
510
} ) ;
449
511
} else {
450
512
ws. gctx ( ) . shell ( ) . warn ( & format ! (
@@ -454,14 +516,16 @@ fn build_ar_list(
454
516
}
455
517
456
518
if include_lockfile {
519
+ let lockfile_path = ws. lock_root ( ) . as_path_unlocked ( ) . join ( LOCKFILE_NAME ) ;
520
+ let lockfile_path = lockfile_path. exists ( ) . then_some ( lockfile_path) ;
457
521
let rel_str = "Cargo.lock" ;
458
522
result
459
523
. entry ( UncasedAscii :: new ( rel_str) )
460
524
. or_insert_with ( Vec :: new)
461
525
. push ( ArchiveFile {
462
526
rel_path : PathBuf :: from ( rel_str) ,
463
527
rel_str : rel_str. to_string ( ) ,
464
- contents : FileContents :: Generated ( GeneratedFile :: Lockfile ) ,
528
+ contents : FileContents :: Generated ( GeneratedFile :: Lockfile ( lockfile_path ) ) ,
465
529
} ) ;
466
530
}
467
531
@@ -780,8 +844,10 @@ fn tar(
780
844
}
781
845
FileContents :: Generated ( generated_kind) => {
782
846
let contents = match generated_kind {
783
- GeneratedFile :: Manifest => publish_pkg. manifest ( ) . to_normalized_contents ( ) ?,
784
- GeneratedFile :: Lockfile => build_lock ( ws, & publish_pkg, local_reg) ?,
847
+ GeneratedFile :: Manifest ( _) => {
848
+ publish_pkg. manifest ( ) . to_normalized_contents ( ) ?
849
+ }
850
+ GeneratedFile :: Lockfile ( _) => build_lock ( ws, & publish_pkg, local_reg) ?,
785
851
GeneratedFile :: VcsInfo ( ref s) => serde_json:: to_string_pretty ( s) ?,
786
852
} ;
787
853
header. set_entry_type ( EntryType :: file ( ) ) ;
0 commit comments