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