xref: /aosp_15_r20/development/tools/external_crates/license_checker/src/file_name_checker.rs (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::{
16     collections::BTreeMap,
17     ffi::{OsStr, OsString},
18     path::Path,
19     sync::LazyLock,
20 };
21 
22 use spdx::{LicenseReq, Licensee};
23 
classify_license_file_name(file: impl AsRef<Path>) -> Option<LicenseReq>24 pub(crate) fn classify_license_file_name(file: impl AsRef<Path>) -> Option<LicenseReq> {
25     // File should be relative
26     let file = file.as_ref();
27     let mut basename = if file.extension() == Some(OsStr::new("txt"))
28         || file.extension() == Some(OsStr::new("md"))
29     {
30         file.with_extension("")
31     } else {
32         file.to_owned()
33     };
34     let uppercase_name = basename.as_mut_os_string();
35     uppercase_name.make_ascii_uppercase();
36     if let Some(req) = LICENSE_FILE_NAME_CLASSIFICATION.get(uppercase_name) {
37         return Some(req.clone());
38     }
39     None
40 }
41 
42 static LICENSE_FILE_NAME_CLASSIFICATION: LazyLock<BTreeMap<OsString, LicenseReq>> = LazyLock::new(
43     ||
44         // Filenames are case-insensitive.
45         vec![
46             ("LICENSE-MIT", "MIT"),
47             ("LICENSES/MIT", "MIT"),
48 
49             ("LICENSE-APACHE", "Apache-2.0"),
50             ("LICENSE-APACHE-2.0", "Apache-2.0"),
51             ("LICENSES/Apache-2.0", "Apache-2.0"),
52 
53             ("LICENSE-BSD-3-Clause", "BSD-3-Clause"),
54 
55             ("LICENSE-UNICODE", "Unicode-DFS-2016"),
56 
57             ("LICENSE-0BSD", "0BSD"),
58 
59             ("LICENSE-ZLIB", "Zlib"),
60 
61             ("UNLICENSE", "Unlicense"),
62         ]
63         .into_iter()
64         .map(|(file, req)| (OsString::from(file.to_uppercase()), Licensee::parse(req).unwrap().into_req()))
65         .collect(),
66 );
67 
68 #[cfg(test)]
69 mod tests {
70     use super::*;
71 
72     #[test]
test_classify()73     fn test_classify() {
74         assert!(classify_license_file_name(Path::new("LICENSE")).is_none(), "Generic license name");
75         assert_eq!(
76             classify_license_file_name(Path::new("UNLICENSE")),
77             Some(Licensee::parse("Unlicense").unwrap().into_req()),
78             "Unlicense"
79         );
80         assert_eq!(
81             classify_license_file_name(Path::new("LICENSE-MIT")),
82             Some(Licensee::parse("MIT").unwrap().into_req()),
83             "Standard name"
84         );
85         assert_eq!(
86             classify_license_file_name(Path::new("LICENSE-APACHE")),
87             Some(Licensee::parse("Apache-2.0").unwrap().into_req()),
88             "Standard name"
89         );
90         assert_eq!(
91             classify_license_file_name(Path::new("LICENSE-apache")),
92             Some(Licensee::parse("Apache-2.0").unwrap().into_req()),
93             "Case-insensitive"
94         );
95         assert_eq!(
96             classify_license_file_name(Path::new("LICENSE-APACHE.md")),
97             Some(Licensee::parse("Apache-2.0").unwrap().into_req()),
98             ".md extension ignored"
99         );
100         assert_eq!(
101             classify_license_file_name(Path::new("LICENSE-0BSD.txt")),
102             Some(Licensee::parse("0BSD").unwrap().into_req()),
103             ".txt extension ignored"
104         );
105         assert!(
106             classify_license_file_name(Path::new("LICENSE-0BSD.jpg")).is_none(),
107             "Random extension preserved"
108         );
109         assert_eq!(
110             classify_license_file_name(Path::new("LICENSES/MIT")),
111             Some(Licensee::parse("MIT").unwrap().into_req()),
112             "Subdirectory"
113         );
114         assert_eq!(
115             classify_license_file_name(Path::new("LICENSES/Apache-2.0")),
116             Some(Licensee::parse("Apache-2.0").unwrap().into_req()),
117             "Subdirectory"
118         );
119     }
120 }
121