@@ -44,44 +44,118 @@ 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 ( ) ;
48+
49+ fn emit_error (
50+ error : String ,
51+ mirror_file : & MirrorFile ,
52+ file_source : & str ,
53+ cache : & LocationCache ,
54+ errors : & mut Vec < String > ,
55+ ) {
56+ let location = cache
57+ . seen_paths
58+ . get ( & mirror_file. name )
59+ . unwrap ( )
60+ . first ( )
61+ . unwrap ( ) ;
62+ let ( src_line, snippet) = span_info ( & file_source, location) ;
63+ errors. push ( format ! (
64+ "{error}:\n \
65+ # {} (line {src_line})\n {snippet}\n ",
66+ location. file. display( )
67+ ) ) ;
68+ }
4769
4870 fn load_inner (
4971 load_from : & Path ,
5072 result : & mut Vec < MirrorFile > ,
5173 cache : & mut LocationCache ,
74+ errors : & mut Vec < String > ,
5275 ) -> anyhow:: Result < ( ) > {
5376 for entry in load_from. read_dir ( ) ? {
5477 let path = entry?. path ( ) ;
5578 if path. is_file ( ) && path. extension ( ) . and_then ( |s| s. to_str ( ) ) == Some ( "toml" ) {
56- let manifest = std:: fs:: read_to_string ( & path)
79+ let file_source = std:: fs:: read_to_string ( & path)
80+ . map_err ( Error :: from)
81+ . with_context ( || format ! ( "failed to read {}" , path. display( ) ) ) ?;
82+ let manifest = toml:: from_str :: < Manifest > ( & file_source)
5783 . map_err ( Error :: from)
58- . and_then ( |raw| toml:: from_str :: < Manifest > ( & raw ) . map_err ( Error :: from) )
5984 . with_context ( || format ! ( "failed to read {}" , path. display( ) ) ) ?;
6085 record_locations ( & path, & manifest, cache) ;
6186
6287 for file in manifest. files {
63- result . push ( match file. into_inner ( ) {
88+ let mirror_file = match file. into_inner ( ) {
6489 ManifestFile :: Legacy ( legacy) => MirrorFile {
6590 name : legacy. name ,
6691 sha256 : legacy. sha256 ,
6792 source : Source :: Legacy ,
93+ rename_from : None ,
6894 } ,
6995 ManifestFile :: Managed ( managed) => MirrorFile {
7096 name : managed. name ,
7197 sha256 : managed. sha256 ,
7298 source : Source :: Url ( managed. source ) ,
99+ rename_from : managed. rename_from ,
73100 } ,
74- } ) ;
101+ } ;
102+ if let Source :: Url ( ref source) = mirror_file. source {
103+ if let Some ( file_name) = source. path ( ) . split ( '/' ) . last ( )
104+ && let Some ( path_name) = mirror_file. name . split ( '/' ) . last ( )
105+ {
106+ match mirror_file. rename_from {
107+ Some ( ref rename_from) => {
108+ if path_name == file_name {
109+ emit_error (
110+ format ! (
111+ "`rename-from` field isn't needed since `source` and `name` field have the same file name (`{file_name}`)"
112+ ) ,
113+ & mirror_file,
114+ & file_source,
115+ cache,
116+ errors,
117+ ) ;
118+ } else if rename_from != file_name {
119+ emit_error (
120+ format ! (
121+ "`rename-from` field value doesn't match name from the URL `{source}` (`{file_name}` != `{rename_from}`)"
122+ ) ,
123+ & mirror_file,
124+ & file_source,
125+ cache,
126+ errors,
127+ ) ;
128+ }
129+ }
130+ None => {
131+ if path_name != file_name {
132+ emit_error (
133+ format ! (
134+ "The name from the URL `{source}` doesn't match the `name` field (`{file_name}` != `{path_name}`). \
135+ Add `rename-from = {file_name:?}` to fix this error"
136+ ) ,
137+ & mirror_file,
138+ & file_source,
139+ cache,
140+ errors,
141+ ) ;
142+ }
143+ }
144+ }
145+ }
146+ }
147+ result. push ( mirror_file) ;
75148 }
76149 } else if path. is_dir ( ) {
77- load_inner ( & path, result, cache) ?;
150+ load_inner ( & path, result, cache, errors ) ?;
78151 }
79152 }
80153 Ok ( ( ) )
81154 }
82155
83- load_inner ( load_from, & mut result, & mut cache) ?;
84- Ok ( ( result, find_errors ( cache) ) )
156+ load_inner ( load_from, & mut result, & mut cache, & mut errors) ?;
157+ find_errors ( cache, & mut errors) ;
158+ Ok ( ( result, errors) )
85159}
86160
87161fn record_locations ( toml_path : & Path , manifest : & Manifest , cache : & mut LocationCache ) {
@@ -114,12 +188,32 @@ fn record_locations(toml_path: &Path, manifest: &Manifest, cache: &mut LocationC
114188 . or_default ( )
115189 . insert ( location. clone ( ) ) ;
116190 if let Some ( url) = url {
117- cache. seen_urls . entry ( url) . or_default ( ) . insert ( location) ;
191+ cache
192+ . seen_urls
193+ . entry ( url)
194+ . or_default ( )
195+ . insert ( location. clone ( ) ) ;
118196 }
119197 }
120198}
121199
122- fn find_errors ( cache : LocationCache ) -> Vec < String > {
200+ fn span_info < ' a > ( content : & ' a str , location : & Location ) -> ( usize , & ' a str ) {
201+ // Find the corresponding line number
202+ let mut accumulated_chars = 0 ;
203+ let mut src_line = 0 ;
204+ for ( index, line) in content. lines ( ) . enumerate ( ) {
205+ accumulated_chars += line. len ( ) + 1 ; // +1 for newline
206+ if accumulated_chars > location. span . 0 . start {
207+ src_line = index + 1 ;
208+ break ;
209+ }
210+ }
211+
212+ let snippet = & content[ location. span . 0 . start ..location. span . 0 . end ] ;
213+ ( src_line, snippet)
214+ }
215+
216+ fn find_errors ( cache : LocationCache , errors : & mut Vec < String > ) {
123217 let mut file_cache: HashMap < PathBuf , String > = HashMap :: new ( ) ;
124218
125219 fn format_locations (
@@ -136,18 +230,7 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
136230 } )
137231 } ) ;
138232
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 ] ;
233+ let ( src_line, snippet) = span_info ( & content, location) ;
151234 writeln ! (
152235 output,
153236 "# {} (line {src_line})\n {snippet}\n " ,
@@ -159,7 +242,6 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
159242 output
160243 }
161244
162- let mut errors = Vec :: new ( ) ;
163245 for ( path, locations) in cache. seen_paths {
164246 if locations. len ( ) > 1 {
165247 errors. push ( format ! (
@@ -184,13 +266,13 @@ fn find_errors(cache: LocationCache) -> Vec<String> {
184266 ) ) ;
185267 }
186268 }
187- errors
188269}
189270
190271pub ( crate ) struct MirrorFile {
191272 pub ( crate ) name : String ,
192273 pub ( crate ) sha256 : String ,
193274 pub ( crate ) source : Source ,
275+ pub ( crate ) rename_from : Option < String > ,
194276}
195277
196278pub ( crate ) enum Source {
@@ -233,15 +315,24 @@ pub struct ManifestFileManaged {
233315 // This field is not considered at all by the automation, we just enforce its presence so that
234316 // people adding new entries think about the licensing implications.
235317 license : String ,
318+ #[ serde( default , rename = "rename-from" ) ]
319+ rename_from : Option < String > ,
236320}
237321
238322impl ManifestFileManaged {
239- pub fn new ( name : String , sha256 : String , source : Url , license : String ) -> Self {
323+ pub fn new (
324+ name : String ,
325+ sha256 : String ,
326+ source : Url ,
327+ license : String ,
328+ rename_from : Option < String > ,
329+ ) -> Self {
240330 Self {
241331 name,
242332 sha256,
243333 source,
244334 license,
335+ rename_from,
245336 }
246337 }
247338}
0 commit comments