@@ -86,55 +86,61 @@ macro_rules! locator_regex {
86
86
} ;
87
87
}
88
88
89
- /// Core, and most services that interact with Core,
90
- /// refer to open source packages via the `Locator` type.
89
+ /// `Locator` identifies a package, optionally at a specific revision, in a code host.
91
90
///
92
- /// This type is nearly universally rendered to a string
93
- /// before being serialized to the database or sent over the network.
91
+ /// If the `revision` component is not specified, FOSSA services interpret this to mean
92
+ /// that the "latest" version of the package should be used if the requested operation
93
+ /// requires a concrete version of the package.
94
94
///
95
- /// This type represents a _validly-constructed_ `Locator`, but does not
96
- /// validate whether a `Locator` is actually valid. This means that a
97
- /// given `Locator` is guaranteed to be correctly formatted data,
98
- /// but that the actual repository or revision to which the `Locator`
99
- /// refers is _not_ guaranteed to exist or be accessible.
100
- /// Currently the canonical method for validating whether a given `Locator` is
101
- /// accessible is to run it through the Core fetcher system.
95
+ /// ## Guarantees
102
96
///
103
- /// For more information on the background of `Locator` and fetchers generally,
104
- /// FOSSA employees may refer to
105
- /// [Fetchers and Locators](https://go/fetchers-doc) .
97
+ /// This type represents a _validly-constructed_ `Locator`, but does not
98
+ /// guarantee whether a package or revision actually exists or is accessible
99
+ /// in the code host .
106
100
///
107
101
/// ## Ordering
108
102
///
109
- /// Locators order by:
103
+ /// `Locator` orders by:
110
104
/// 1. Fetcher, alphanumerically.
111
105
/// 2. Organization ID, alphanumerically; missing organizations are sorted higher.
112
106
/// 3. The package field, alphanumerically.
113
107
/// 4. The revision field:
114
- /// If both comparing locators use semver, these are compared using semver rules;
115
- /// otherwise these are compared alphanumerically.
116
- /// Missing revisions are sorted higher.
108
+ /// - If both comparing locators use semver, these are compared using semver rules.
109
+ /// - Otherwise these are compared alphanumerically.
110
+ /// - Missing revisions are sorted higher.
117
111
///
118
- /// Importantly, there may be other metrics for ordering using the actual code host
119
- /// which contains the package (for example, ordering by release date).
120
- /// This library does not perform such ordering.
112
+ /// **Important:** there may be other metrics for ordering using the actual code host
113
+ /// which contains the package- for example ordering by release date, or code hosts
114
+ /// such as `git` which have non-linear history (making flat ordering a lossy operation).
115
+ /// `Locator` does not take such edge cases into account in any way.
121
116
///
122
117
/// ## Parsing
123
118
///
124
- /// The input string must be in one of the following forms:
125
- /// - `{fetcher}+{package}`
126
- /// - `{fetcher}+{package}$`
127
- /// - `{fetcher}+{package}${revision}`
119
+ /// This type is canonically rendered to a string before being serialized
120
+ /// to the database or sent over the network according to the rules in this section.
121
+ ///
122
+ /// The input string must be in one of the following formats:
123
+ /// ```ignore
124
+ /// {fetcher}+{package}${revision}
125
+ /// {fetcher}+{package}
126
+ /// ```
128
127
///
129
128
/// Packages may also be namespaced to a specific organization;
130
129
/// in such cases the organization ID is at the start of the `{package}` field
131
130
/// separated by a slash. The ID can be any non-negative integer.
132
- /// This yields the following formats:
133
- /// - `{fetcher}+{org_id}/{package}`
134
- /// - `{fetcher}+{org_id}/{package}$`
135
- /// - `{fetcher}+{org_id}/{package}${revision}`
131
+ /// This yields the following optional formats:
132
+ /// ```ignore
133
+ /// {fetcher}+{org_id}/{package}${revision}
134
+ /// {fetcher}+{org_id}/{package}
135
+ /// ```
136
136
///
137
- /// This parse function is based on the function used in FOSSA Core for maximal compatibility.
137
+ /// Note that locators do not feature escaping: instead the _first_ instance
138
+ /// of each delimiter (`+`, `/`, `$`) is used to split the fields. However,
139
+ /// as a special case organization IDs are only extracted if the field content
140
+ /// fully consists of a non-negative integer.
141
+ //
142
+ // For more information on the background of `Locator` and fetchers generally,
143
+ // FOSSA employees may refer to the "fetchers and locators" doc: https://go/fetchers-doc.
138
144
#[ derive(
139
145
Clone , Eq , PartialEq , Ord , PartialOrd , Hash , Debug , Builder , Getters , CopyGetters , Documented ,
140
146
) ]
@@ -189,55 +195,43 @@ impl Locator {
189
195
190
196
/// Parse a `Locator`.
191
197
/// For details, see the parsing section on [`Locator`].
192
- pub fn parse ( locator : & str ) -> Result < Self , Error > {
198
+ pub fn parse ( input : & str ) -> Result < Self , Error > {
199
+ /// Convenience macro for fatal errors without needing to type out all the `.into()`s.
193
200
macro_rules! fatal {
194
- ( syntax; $inner: expr) => {
195
- ParseError :: Syntax {
196
- input: $inner. into( ) ,
197
- }
198
- } ;
199
- ( field => $name: expr; $inner: expr) => {
200
- ParseError :: Field {
201
- input: $inner. into( ) ,
202
- field: $name. into( ) ,
201
+ ( $type: ident => $input: expr) => {
202
+ ParseError :: $type {
203
+ input: $input. into( ) ,
203
204
}
204
205
} ;
205
- ( fetcher => $fetcher: expr, error => $err: expr; $inner: expr) => {
206
- ParseError :: Fetcher {
207
- input: $inner. into( ) ,
208
- fetcher: $fetcher. into( ) ,
209
- error: $err. into( ) ,
210
- }
211
- } ;
212
- ( package => $package: expr, error => $err: expr; $inner: expr) => {
213
- ParseError :: Package {
214
- input: $inner. into( ) ,
215
- package: $package. into( ) ,
216
- error: $err. into( ) ,
206
+ ( $type: ident => $input: expr, $( $key: ident: $value: expr) ,+) => {
207
+ ParseError :: $type {
208
+ input: $input. into( ) ,
209
+ $( $key: $value. into( ) ) ,* ,
217
210
}
218
211
} ;
219
212
}
220
213
214
+ /// Convenience macro for early returns.
221
215
macro_rules! bail {
222
216
( $( $tt: tt) * ) => {
223
217
return Err ( Error :: from( fatal!( $( $tt) * ) ) )
224
218
} ;
225
219
}
226
220
227
- let Some ( ( _, fetcher, package, revision) ) = locator_regex ! ( parse => locator ) else {
228
- bail ! ( syntax ; locator ) ;
221
+ let Some ( ( _, fetcher, package, revision) ) = locator_regex ! ( parse => input ) else {
222
+ bail ! ( Syntax => input ) ;
229
223
} ;
230
224
231
225
if fetcher. is_empty ( ) {
232
- bail ! ( field => "fetcher" ; locator ) ;
226
+ bail ! ( Field => input , field : "fetcher" ) ;
233
227
}
234
- let fetcher = Fetcher :: try_from ( fetcher)
235
- . map_err ( |err| fatal ! ( fetcher => fetcher, error => err; locator) ) ?;
236
-
237
228
if package. is_empty ( ) {
238
- bail ! ( field => "package" ; locator ) ;
229
+ bail ! ( Field => input , field : "package" ) ;
239
230
}
240
231
232
+ let fetcher = Fetcher :: try_from ( fetcher)
233
+ . map_err ( |err| fatal ! ( Fetcher => input, fetcher: fetcher, error: err) ) ?;
234
+
241
235
let revision = if revision. is_empty ( ) {
242
236
None
243
237
} else {
0 commit comments