@@ -44,44 +44,106 @@ struct LocationCache {
4444pub ( crate ) fn load_manifests ( load_from : & Path ) -> Result < ( Vec < MirrorFile > , Vec < String > ) , Error > {
4545 let mut result = Vec :: new ( ) ;
4646 let mut cache = LocationCache :: default ( ) ;
47+ let mut errors = Vec :: new ( ) ;
4748
4849 fn load_inner (
4950 load_from : & Path ,
5051 result : & mut Vec < MirrorFile > ,
5152 cache : & mut LocationCache ,
53+ errors : & mut Vec < String > ,
5254 ) -> anyhow:: Result < ( ) > {
5355 for entry in load_from. read_dir ( ) ? {
5456 let path = entry?. path ( ) ;
5557 if path. is_file ( ) && path. extension ( ) . and_then ( |s| s. to_str ( ) ) == Some ( "toml" ) {
56- let manifest = std:: fs:: read_to_string ( & path)
58+ let file_source = std:: fs:: read_to_string ( & path)
59+ . map_err ( Error :: from)
60+ . with_context ( || format ! ( "failed to read {}" , path. display( ) ) ) ?;
61+ let manifest = toml:: from_str :: < Manifest > ( & file_source)
5762 . map_err ( Error :: from)
58- . and_then ( |raw| toml:: from_str :: < Manifest > ( & raw ) . map_err ( Error :: from) )
5963 . with_context ( || format ! ( "failed to read {}" , path. display( ) ) ) ?;
6064 record_locations ( & path, & manifest, cache) ;
6165
6266 for file in manifest. files {
63- result . push ( match file. into_inner ( ) {
67+ let mirror_file = match file. into_inner ( ) {
6468 ManifestFile :: Legacy ( legacy) => MirrorFile {
6569 name : legacy. name ,
6670 sha256 : legacy. sha256 ,
6771 source : Source :: Legacy ,
72+ rename_from : None ,
6873 } ,
6974 ManifestFile :: Managed ( managed) => MirrorFile {
7075 name : managed. name ,
7176 sha256 : managed. sha256 ,
7277 source : Source :: Url ( managed. source ) ,
78+ rename_from : managed. rename_from ,
7379 } ,
74- } ) ;
80+ } ;
81+ if let Source :: Url ( ref source) = mirror_file. source {
82+ if let Some ( file_name) = source. path ( ) . split ( '/' ) . last ( )
83+ && let Some ( path_name) = mirror_file. name . split ( '/' ) . last ( )
84+ {
85+ match mirror_file. rename_from {
86+ Some ( ref rename_from) => {
87+ if path_name == file_name {
88+ let location = cache
89+ . seen_paths
90+ . get ( & mirror_file. name )
91+ . unwrap ( )
92+ . first ( )
93+ . unwrap ( ) ;
94+ let ( src_line, snippet) = span_info ( & file_source, location) ;
95+ errors. push ( format ! (
96+ "`rename-from` field isn't needed since `source` and `name` field have the same file name (`{file_name}`):\n \
97+ # {} (line {src_line})\n {snippet}\n ",
98+ location. file. display( )
99+ ) ) ;
100+ } else if path_name != file_name && rename_from != file_name {
101+ let location = cache
102+ . seen_paths
103+ . get ( & mirror_file. name )
104+ . unwrap ( )
105+ . first ( )
106+ . unwrap ( ) ;
107+ let ( src_line, snippet) = span_info ( & file_source, location) ;
108+ errors. push ( format ! (
109+ "`rename-from` field value doesn't match name from the URL `{source}` (`{file_name}` != `{rename_from}`):\n \
110+ # {} (line {src_line})\n {snippet}\n ",
111+ location. file. display( )
112+ ) ) ;
113+ }
114+ }
115+ None => {
116+ if path_name != file_name {
117+ let location = cache
118+ . seen_paths
119+ . get ( & mirror_file. name )
120+ . unwrap ( )
121+ . first ( )
122+ . unwrap ( ) ;
123+ let ( src_line, snippet) = span_info ( & file_source, location) ;
124+ errors. push ( format ! (
125+ "The name from the URL `{source}` doesn't match the `name` field (`{file_name}` != `{path_name}`). \
126+ Add `rename-from = {file_name:?}` to fix this error:\n \
127+ # {} (line {src_line})\n {snippet}\n ",
128+ location. file. display( )
129+ ) ) ;
130+ }
131+ }
132+ }
133+ }
134+ }
135+ result. push ( mirror_file) ;
75136 }
76137 } else if path. is_dir ( ) {
77- load_inner ( & path, result, cache) ?;
138+ load_inner ( & path, result, cache, errors ) ?;
78139 }
79140 }
80141 Ok ( ( ) )
81142 }
82143
83- load_inner ( load_from, & mut result, & mut cache) ?;
84- Ok ( ( result, find_errors ( cache) ) )
144+ load_inner ( load_from, & mut result, & mut cache, & mut errors) ?;
145+ find_errors ( cache, & mut errors) ;
146+ Ok ( ( result, errors) )
85147}
86148
87149fn record_locations ( toml_path : & Path , manifest : & Manifest , cache : & mut LocationCache ) {
@@ -114,12 +176,32 @@ fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationC
114176 . or_default ( )
115177 . insert ( location. clone ( ) ) ;
116178 if let Some ( url) = url {
117- cache. seen_urls . entry ( url) . or_default ( ) . insert ( location) ;
179+ cache
180+ . seen_urls
181+ . entry ( url)
182+ . or_default ( )
183+ . insert ( location. clone ( ) ) ;
184+ }
185+ }
186+ }
187+
188+ fn span_info < ' a > ( content : & ' a str , location : & Location ) -> ( usize , & ' a str ) {
189+ // Find the corresponding line number
190+ let mut accumulated_chars = 0 ;
191+ let mut src_line = 0 ;
192+ for ( index, line) in content. lines ( ) . enumerate ( ) {
193+ accumulated_chars += line. len ( ) + 1 ; // +1 for newline
194+ if accumulated_chars > location. span . 0 . start {
195+ src_line = index + 1 ;
196+ break ;
118197 }
119198 }
199+
200+ let snippet = & content[ location. span . 0 . start ..location. span . 0 . end ] ;
201+ ( src_line, snippet)
120202}
121203
122- fn find_errors ( cache : LocationCache ) -> Vec < String > {
204+ fn find_errors ( cache : LocationCache , errors : & mut Vec < String > ) {
123205 let mut file_cache: HashMap < PathBuf , String > = HashMap :: new ( ) ;
124206
125207 fn format_locations (
@@ -136,18 +218,7 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
136218 } )
137219 } ) ;
138220
139- // Find the corresponding line number
140- let mut accumulated_chars = 0 ;
141- let mut src_line = 0 ;
142- for ( index, line) in content. lines ( ) . enumerate ( ) {
143- accumulated_chars += line. len ( ) + 1 ; // +1 for newline
144- if accumulated_chars > location. span . 0 . start {
145- src_line = index + 1 ;
146- break ;
147- }
148- }
149-
150- let snippet = & content[ location. span . 0 . start ..location. span . 0 . end ] ;
221+ let ( src_line, snippet) = span_info ( & content, location) ;
151222 writeln ! (
152223 output,
153224 "# {} (line {src_line})\n {snippet}\n " ,
@@ -159,7 +230,6 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
159230 output
160231 }
161232
162- let mut errors = Vec :: new ( ) ;
163233 for ( path, locations) in cache. seen_paths {
164234 if locations. len ( ) > 1 {
165235 errors. push ( format ! (
@@ -184,13 +254,13 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
184254 ) ) ;
185255 }
186256 }
187- errors
188257}
189258
190259pub ( crate ) struct MirrorFile {
191260 pub ( crate ) name : String ,
192261 pub ( crate ) sha256 : String ,
193262 pub ( crate ) source : Source ,
263+ pub ( crate ) rename_from : Option < String > ,
194264}
195265
196266pub ( crate ) enum Source {
@@ -233,15 +303,24 @@ pub struct ManifestFileManaged {
233303 // This field is not considered at all by the automation, we just enforce its presence so that
234304 // people adding new entries think about the licensing implications.
235305 license : String ,
306+ #[ serde( default , rename = "rename-from" ) ]
307+ rename_from : Option < String > ,
236308}
237309
238310impl ManifestFileManaged {
239- pub fn new ( name : String , sha256 : String , source : Url , license : String ) -> Self {
311+ pub fn new (
312+ name : String ,
313+ sha256 : String ,
314+ source : Url ,
315+ license : String ,
316+ rename_from : Option < String > ,
317+ ) -> Self {
240318 Self {
241319 name,
242320 sha256,
243321 source,
244322 license,
323+ rename_from,
245324 }
246325 }
247326}
0 commit comments