@@ -10,6 +10,10 @@ use walkdir::{DirEntry, WalkDir};
1010
1111use crate :: { Crate , LINTCHECK_DOWNLOADS , LINTCHECK_SOURCES } ;
1212
13+ const DEFAULT_DOCS_LINK : & str = "https://docs.rs/{krate}/{version}/src/{krate}/{file}.html#{line}" ;
14+ const DEFAULT_GITHUB_LINK : & str = "{url}/blob/{hash}/src/{file}#L{line}" ;
15+ const DEFAULT_PATH_LINK : & str = "{path}/src/{file}:{line}" ;
16+
1317/// List of sources to check, loaded from a .toml file
1418#[ derive( Debug , Deserialize ) ]
1519pub struct SourceList {
@@ -33,32 +37,60 @@ struct TomlCrate {
3337 git_hash : Option < String > ,
3438 path : Option < String > ,
3539 options : Option < Vec < String > > ,
40+ /// Magic values:
41+ /// * `{krate}` will be replaced by `self.name`
42+ /// * `{version}` will be replaced by `self.version`
43+ /// * `{url}` will be replaced with `self.git_url`
44+ /// * `{hash}` will be replaced with `self.git_hash`
45+ /// * `{path}` will be replaced with `self.path`
46+ /// * `{file}` will be replaced by the path after `src/`
47+ /// * `{line}` will be replaced by the line
48+ ///
49+ /// If unset, this will be filled by [`read_crates`] since it depends on
50+ /// the source.
51+ online_link : Option < String > ,
52+ }
53+
54+ impl TomlCrate {
55+ fn file_link ( & self , default : & str ) -> String {
56+ let mut link = self . online_link . clone ( ) . unwrap_or_else ( || default. to_string ( ) ) ;
57+ link = link. replace ( "{krate}" , & self . name ) ;
58+
59+ if let Some ( version) = & self . version {
60+ link = link. replace ( "{version}" , version) ;
61+ }
62+ if let Some ( url) = & self . git_url {
63+ link = link. replace ( "{url}" , url) ;
64+ }
65+ if let Some ( hash) = & self . git_hash {
66+ link = link. replace ( "{hash}" , hash) ;
67+ }
68+ if let Some ( path) = & self . path {
69+ link = link. replace ( "{path}" , path) ;
70+ }
71+ link
72+ }
3673}
3774
3875/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
3976/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
77+ #[ derive( Debug , Deserialize , Eq , Hash , PartialEq , Ord , PartialOrd ) ]
78+ pub struct CrateWithSource {
79+ pub name : String ,
80+ pub source : CrateSource ,
81+ pub file_link : String ,
82+ pub options : Option < Vec < String > > ,
83+ }
84+
4085#[ derive( Debug , Deserialize , Eq , Hash , PartialEq , Ord , PartialOrd ) ]
4186pub enum CrateSource {
42- CratesIo {
43- name : String ,
44- version : String ,
45- options : Option < Vec < String > > ,
46- } ,
47- Git {
48- name : String ,
49- url : String ,
50- commit : String ,
51- options : Option < Vec < String > > ,
52- } ,
53- Path {
54- name : String ,
55- path : PathBuf ,
56- options : Option < Vec < String > > ,
57- } ,
87+ CratesIo { version : String } ,
88+ Git { url : String , commit : String } ,
89+ Path { path : PathBuf } ,
5890}
5991
6092/// Read a `lintcheck_crates.toml` file
61- pub fn read_crates ( toml_path : & Path ) -> ( Vec < CrateSource > , RecursiveOptions ) {
93+ pub fn read_crates ( toml_path : & Path ) -> ( Vec < CrateWithSource > , RecursiveOptions ) {
6294 let toml_content: String =
6395 fs:: read_to_string ( toml_path) . unwrap_or_else ( |_| panic ! ( "Failed to read {}" , toml_path. display( ) ) ) ;
6496 let crate_list: SourceList =
@@ -71,23 +103,32 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
71103 let mut crate_sources = Vec :: new ( ) ;
72104 for tk in tomlcrates {
73105 if let Some ( ref path) = tk. path {
74- crate_sources. push ( CrateSource :: Path {
106+ crate_sources. push ( CrateWithSource {
75107 name : tk. name . clone ( ) ,
76- path : PathBuf :: from ( path) ,
108+ source : CrateSource :: Path {
109+ path : PathBuf :: from ( path) ,
110+ } ,
111+ file_link : tk. file_link ( DEFAULT_PATH_LINK ) ,
77112 options : tk. options . clone ( ) ,
78113 } ) ;
79114 } else if let Some ( ref version) = tk. version {
80- crate_sources. push ( CrateSource :: CratesIo {
115+ crate_sources. push ( CrateWithSource {
81116 name : tk. name . clone ( ) ,
82- version : version. to_string ( ) ,
117+ source : CrateSource :: CratesIo {
118+ version : version. to_string ( ) ,
119+ } ,
120+ file_link : tk. file_link ( DEFAULT_DOCS_LINK ) ,
83121 options : tk. options . clone ( ) ,
84122 } ) ;
85123 } else if tk. git_url . is_some ( ) && tk. git_hash . is_some ( ) {
86124 // otherwise, we should have a git source
87- crate_sources. push ( CrateSource :: Git {
125+ crate_sources. push ( CrateWithSource {
88126 name : tk. name . clone ( ) ,
89- url : tk. git_url . clone ( ) . unwrap ( ) ,
90- commit : tk. git_hash . clone ( ) . unwrap ( ) ,
127+ source : CrateSource :: Git {
128+ url : tk. git_url . clone ( ) . unwrap ( ) ,
129+ commit : tk. git_hash . clone ( ) . unwrap ( ) ,
130+ } ,
131+ file_link : tk. file_link ( DEFAULT_GITHUB_LINK ) ,
91132 options : tk. options . clone ( ) ,
92133 } ) ;
93134 } else {
@@ -117,7 +158,7 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
117158 ( crate_sources, crate_list. recursive )
118159}
119160
120- impl CrateSource {
161+ impl CrateWithSource {
121162 /// Makes the sources available on the disk for clippy to check.
122163 /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
123164 /// copies a local folder
@@ -139,8 +180,11 @@ impl CrateSource {
139180 retries += 1 ;
140181 }
141182 }
142- match self {
143- CrateSource :: CratesIo { name, version, options } => {
183+ let name = & self . name ;
184+ let options = & self . options ;
185+ let file_link = & self . file_link ;
186+ match & self . source {
187+ CrateSource :: CratesIo { version } => {
144188 let extract_dir = PathBuf :: from ( LINTCHECK_SOURCES ) ;
145189 let krate_download_dir = PathBuf :: from ( LINTCHECK_DOWNLOADS ) ;
146190
@@ -171,14 +215,10 @@ impl CrateSource {
171215 name : name. clone ( ) ,
172216 path : extract_dir. join ( format ! ( "{name}-{version}/" ) ) ,
173217 options : options. clone ( ) ,
218+ base_url : file_link. clone ( ) ,
174219 }
175220 } ,
176- CrateSource :: Git {
177- name,
178- url,
179- commit,
180- options,
181- } => {
221+ CrateSource :: Git { url, commit } => {
182222 let repo_path = {
183223 let mut repo_path = PathBuf :: from ( LINTCHECK_SOURCES ) ;
184224 // add a -git suffix in case we have the same crate from crates.io and a git repo
@@ -217,9 +257,10 @@ impl CrateSource {
217257 name : name. clone ( ) ,
218258 path : repo_path,
219259 options : options. clone ( ) ,
260+ base_url : file_link. clone ( ) ,
220261 }
221262 } ,
222- CrateSource :: Path { name , path, options } => {
263+ CrateSource :: Path { path } => {
223264 fn is_cache_dir ( entry : & DirEntry ) -> bool {
224265 fs:: read ( entry. path ( ) . join ( "CACHEDIR.TAG" ) )
225266 . map ( |x| x. starts_with ( b"Signature: 8a477f597d28d172789f06886806bc55" ) )
@@ -256,6 +297,7 @@ impl CrateSource {
256297 name : name. clone ( ) ,
257298 path : dest_crate_root,
258299 options : options. clone ( ) ,
300+ base_url : file_link. clone ( ) ,
259301 }
260302 } ,
261303 }
0 commit comments