11use std:: collections:: BTreeMap ;
2- use std:: io:: Write ;
32use std:: path:: { Path , PathBuf } ;
43
54use anyhow:: Error ;
5+ use rinja:: Template ;
66
77mod cargo_metadata;
88
9- static TOP_BOILERPLATE : & str = r##"
10- <!DOCTYPE html>
11- <html>
12- <head>
13- <meta charset="UTF-8">
14- <title>Copyright notices for The Rust Toolchain</title>
15- </head>
16- <body>
17-
18- <h1>Copyright notices for The Rust Toolchain</h1>
19-
20- <p>This file describes the copyright and licensing information for the source
21- code within The Rust Project git tree, and the third-party dependencies used
22- when building the Rust toolchain (including the Rust Standard Library).</p>
23-
24- <h2>Table of Contents</h2>
25- <ul>
26- <li><a href="#in-tree-files">In-tree files</a></li>
27- <li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li>
28- </ul>
29- "## ;
30-
31- static BOTTOM_BOILERPLATE : & str = r#"
32- </body>
33- </html>
34- "# ;
9+ #[ derive( Template ) ]
10+ #[ template( path = "COPYRIGHT.html" ) ]
11+ struct CopyrightTemplate {
12+ in_tree : Node ,
13+ dependencies : BTreeMap < cargo_metadata:: Package , cargo_metadata:: PackageMetadata > ,
14+ }
3515
3616/// The entry point to the binary.
3717///
@@ -53,150 +33,114 @@ fn main() -> Result<(), Error> {
5333 Path :: new ( "./src/tools/cargo/Cargo.toml" ) ,
5434 Path :: new ( "./library/std/Cargo.toml" ) ,
5535 ] ;
56- let collected_cargo_metadata =
36+ let mut collected_cargo_metadata =
5737 cargo_metadata:: get_metadata_and_notices ( & cargo, & out_dir, & root_path, & workspace_paths) ?;
5838
5939 let stdlib_set =
6040 cargo_metadata:: get_metadata ( & cargo, & root_path, & [ Path :: new ( "./library/std/Cargo.toml" ) ] ) ?;
6141
62- let mut buffer = Vec :: new ( ) ;
42+ for ( key, value) in collected_cargo_metadata. iter_mut ( ) {
43+ value. is_in_libstd = Some ( stdlib_set. contains_key ( key) ) ;
44+ }
6345
64- writeln ! ( buffer, "{}" , TOP_BOILERPLATE ) ?;
46+ let template = CopyrightTemplate {
47+ in_tree : collected_tree_metadata. files ,
48+ dependencies : collected_cargo_metadata,
49+ } ;
6550
66- writeln ! (
67- buffer,
68- r#"<h2 id="in-tree-files">In-tree files</h2><p>The following licenses cover the in-tree source files that were used in this release:</p>"#
69- ) ?;
70- render_tree_recursive ( & collected_tree_metadata. files , & mut buffer) ?;
51+ let output = template. render ( ) ?;
7152
72- writeln ! (
73- buffer,
74- r#"<h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2><p>The following licenses cover the out-of-tree crates that were used in this release:</p>"#
75- ) ?;
76- render_deps ( & collected_cargo_metadata, & stdlib_set, & mut buffer) ?;
53+ std:: fs:: write ( & dest_file, output) ?;
7754
78- writeln ! ( buffer, "{}" , BOTTOM_BOILERPLATE ) ?;
55+ Ok ( ( ) )
56+ }
7957
80- std:: fs:: write ( & dest_file, & buffer) ?;
58+ /// Describes a tree of metadata for our filesystem tree
59+ #[ derive( serde:: Deserialize ) ]
60+ struct Metadata {
61+ files : Node ,
62+ }
8163
64+ /// Describes one node in our metadata tree
65+ #[ derive( serde:: Deserialize ) ]
66+ #[ serde( rename_all = "kebab-case" , tag = "type" ) ]
67+ pub ( crate ) enum Node {
68+ Root { children : Vec < Node > } ,
69+ Directory { name : String , children : Vec < Node > , license : Option < License > } ,
70+ File { name : String , license : License } ,
71+ Group { files : Vec < String > , directories : Vec < String > , license : License } ,
72+ }
73+
74+ fn with_box < F > ( fmt : & mut std:: fmt:: Formatter < ' _ > , inner : F ) -> std:: fmt:: Result
75+ where
76+ F : FnOnce ( & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result ,
77+ {
78+ writeln ! ( fmt, r#"<div style="border:1px solid black; padding: 5px;">"# ) ?;
79+ inner ( fmt) ?;
80+ writeln ! ( fmt, "</div>" ) ?;
8281 Ok ( ( ) )
8382}
8483
85- /// Recursively draw the tree of files/folders we found on disk and their licenses, as
86- /// markdown, into the given Vec.
87- fn render_tree_recursive ( node : & Node , buffer : & mut Vec < u8 > ) -> Result < ( ) , Error > {
88- writeln ! ( buffer, r#"<div style="border:1px solid black; padding: 5px;">"# ) ?;
89- match node {
90- Node :: Root { children } => {
91- for child in children {
92- render_tree_recursive ( child, buffer) ?;
84+ impl std:: fmt:: Display for Node {
85+ fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
86+ match self {
87+ Node :: Root { children } => {
88+ if children. len ( ) > 1 {
89+ with_box ( fmt, |f| {
90+ for child in children {
91+ writeln ! ( f, "{child}" ) ?;
92+ }
93+ Ok ( ( ) )
94+ } )
95+ } else {
96+ for child in children {
97+ writeln ! ( fmt, "{child}" ) ?;
98+ }
99+ Ok ( ( ) )
100+ }
93101 }
94- }
95- Node :: Directory { name , children , license } => {
96- render_tree_license ( std :: iter :: once ( name ) , license . as_ref ( ) , buffer ) ? ;
97- if !children . is_empty ( ) {
98- writeln ! ( buffer , "<p><b>Exceptions:</b></p>" ) ? ;
99- for child in children {
100- render_tree_recursive ( child , buffer ) ? ;
102+ Node :: Directory { name , children , license } => with_box ( fmt , |f| {
103+ render_tree_license ( std :: iter :: once ( name ) , license . as_ref ( ) , f ) ? ;
104+ if !children . is_empty ( ) {
105+ writeln ! ( f , "<p><b>Exceptions:</b></p>" ) ? ;
106+ for child in children {
107+ writeln ! ( f , "{child}" ) ? ;
108+ }
101109 }
110+ Ok ( ( ) )
111+ } ) ,
112+ Node :: Group { files, directories, license } => with_box ( fmt, |f| {
113+ render_tree_license ( directories. iter ( ) . chain ( files. iter ( ) ) , Some ( license) , f)
114+ } ) ,
115+ Node :: File { name, license } => {
116+ with_box ( fmt, |f| render_tree_license ( std:: iter:: once ( name) , Some ( license) , f) )
102117 }
103118 }
104- Node :: Group { files, directories, license } => {
105- render_tree_license ( directories. iter ( ) . chain ( files. iter ( ) ) , Some ( license) , buffer) ?;
106- }
107- Node :: File { name, license } => {
108- render_tree_license ( std:: iter:: once ( name) , Some ( license) , buffer) ?;
109- }
110119 }
111- writeln ! ( buffer, "</div>" ) ?;
112-
113- Ok ( ( ) )
114120}
115121
116- /// Draw a series of sibling files/folders, as markdown , into the given Vec .
122+ /// Draw a series of sibling files/folders, as HTML , into the given formatter .
117123fn render_tree_license < ' a > (
118124 names : impl Iterator < Item = & ' a String > ,
119125 license : Option < & License > ,
120- buffer : & mut Vec < u8 > ,
121- ) -> Result < ( ) , Error > {
122- writeln ! ( buffer , "<p><b>File/Directory:</b> " ) ?;
126+ f : & mut std :: fmt :: Formatter < ' _ > ,
127+ ) -> std :: fmt :: Result {
128+ writeln ! ( f , "<p><b>File/Directory:</b> " ) ?;
123129 for name in names {
124- writeln ! ( buffer , "<code>{name }</code>" ) ?;
130+ writeln ! ( f , "<code>{}</code>" , html_escape :: encode_text ( & name ) ) ?;
125131 }
126- writeln ! ( buffer , "</p>" ) ?;
132+ writeln ! ( f , "</p>" ) ?;
127133
128134 if let Some ( license) = license {
129- writeln ! ( buffer , "<p><b>License:</b> {}</p>" , license. spdx) ?;
135+ writeln ! ( f , "<p><b>License:</b> {}</p>" , html_escape :: encode_text ( & license. spdx) ) ?;
130136 for copyright in license. copyright . iter ( ) {
131- writeln ! ( buffer , "<p><b>Copyright:</b> {copyright }</p>" ) ?;
137+ writeln ! ( f , "<p><b>Copyright:</b> {}</p>" , html_escape :: encode_text ( & copyright ) ) ?;
132138 }
133139 }
134140
135141 Ok ( ( ) )
136142}
137143
138- /// Render a list of out-of-tree dependencies as markdown into the given Vec.
139- fn render_deps (
140- all_deps : & BTreeMap < cargo_metadata:: Package , cargo_metadata:: PackageMetadata > ,
141- stdlib_set : & BTreeMap < cargo_metadata:: Package , cargo_metadata:: PackageMetadata > ,
142- buffer : & mut Vec < u8 > ,
143- ) -> Result < ( ) , Error > {
144- for ( package, metadata) in all_deps {
145- let authors_list = if metadata. authors . is_empty ( ) {
146- "None Specified" . to_owned ( )
147- } else {
148- metadata. authors . join ( ", " )
149- } ;
150- let url = format ! ( "https://crates.io/crates/{}/{}" , package. name, package. version) ;
151- writeln ! ( buffer) ?;
152- writeln ! (
153- buffer,
154- r#"<h3>📦 {name}-{version}</h3>"# ,
155- name = package. name,
156- version = package. version,
157- ) ?;
158- writeln ! ( buffer, r#"<p><b>URL:</b> <a href="{url}">{url}</a></p>"# , ) ?;
159- writeln ! (
160- buffer,
161- "<p><b>In libstd:</b> {}</p>" ,
162- if stdlib_set. contains_key( package) { "Yes" } else { "No" }
163- ) ?;
164- writeln ! ( buffer, "<p><b>Authors:</b> {}</p>" , escape_html( & authors_list) ) ?;
165- writeln ! ( buffer, "<p><b>License:</b> {}</p>" , escape_html( & metadata. license) ) ?;
166- writeln ! ( buffer, "<p><b>Notices:</b> " ) ?;
167- if metadata. notices . is_empty ( ) {
168- writeln ! ( buffer, "None" ) ?;
169- } else {
170- for ( name, contents) in & metadata. notices {
171- writeln ! (
172- buffer,
173- "<details><summary><code>{}</code></summary>" ,
174- name. to_string_lossy( )
175- ) ?;
176- writeln ! ( buffer, "<pre>\n {}\n </pre>" , contents) ?;
177- writeln ! ( buffer, "</details>" ) ?;
178- }
179- }
180- writeln ! ( buffer, "</p>" ) ?;
181- }
182- Ok ( ( ) )
183- }
184- /// Describes a tree of metadata for our filesystem tree
185- #[ derive( serde:: Deserialize ) ]
186- struct Metadata {
187- files : Node ,
188- }
189-
190- /// Describes one node in our metadata tree
191- #[ derive( serde:: Deserialize ) ]
192- #[ serde( rename_all = "kebab-case" , tag = "type" ) ]
193- pub ( crate ) enum Node {
194- Root { children : Vec < Node > } ,
195- Directory { name : String , children : Vec < Node > , license : Option < License > } ,
196- File { name : String , license : License } ,
197- Group { files : Vec < String > , directories : Vec < String > , license : License } ,
198- }
199-
200144/// A License has an SPDX license name and a list of copyright holders.
201145#[ derive( serde:: Deserialize ) ]
202146struct License {
@@ -212,13 +156,3 @@ fn env_path(var: &str) -> Result<PathBuf, Error> {
212156 anyhow:: bail!( "missing environment variable {var}" )
213157 }
214158}
215-
216- /// Escapes any invalid HTML characters
217- fn escape_html ( input : & str ) -> String {
218- static MAPPING : [ ( char , & str ) ; 3 ] = [ ( '&' , "&" ) , ( '<' , "<" ) , ( '>' , ">" ) ] ;
219- let mut output = input. to_owned ( ) ;
220- for ( ch, s) in & MAPPING {
221- output = output. replace ( * ch, s) ;
222- }
223- output
224- }
0 commit comments