├── resources ├── licenses │ ├── ISC.txt.gz │ ├── MIT.txt.gz │ ├── Zlib.txt.gz │ ├── AGPL-3.0.txt.gz │ ├── CC0-1.0.txt.gz │ ├── CDDL-1.1.txt.gz │ ├── EPL-2.0.txt.gz │ ├── GPL-2.0.txt.gz │ ├── GPL-3.0.txt.gz │ ├── LGPL-2.1.txt.gz │ ├── LGPL-3.0.txt.gz │ ├── MPL-2.0.txt.gz │ ├── Unlicense.txt.gz │ ├── Apache-2.0.txt.gz │ ├── Artistic-2.0.txt.gz │ ├── BSD-2-Clause.txt.gz │ └── BSD-3-Clause.txt.gz ├── exceptions │ ├── LLVM-exception.txt.gz │ ├── GCC-exception-2.0.txt.gz │ ├── GCC-exception-3.1.txt.gz │ └── Linux-syscall-note.txt.gz ├── exceptions.json ├── exceptions-schema.json ├── licenses-schema.json ├── licenses.json └── preview.svg ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ └── check.yml ├── licensor_codegen ├── Cargo.toml └── src │ └── main.rs ├── licensor_common ├── Cargo.toml └── src │ └── lib.rs ├── licensor_fetch ├── Cargo.toml └── src │ └── main.rs ├── src ├── types.rs ├── main.rs └── codegen.rs ├── LIST.md ├── LICENSE ├── Cargo.toml ├── README.md └── tests └── integration.rs /resources/licenses/ISC.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/ISC.txt.gz -------------------------------------------------------------------------------- /resources/licenses/MIT.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/MIT.txt.gz -------------------------------------------------------------------------------- /resources/licenses/Zlib.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/Zlib.txt.gz -------------------------------------------------------------------------------- /resources/licenses/AGPL-3.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/AGPL-3.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/CC0-1.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/CC0-1.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/CDDL-1.1.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/CDDL-1.1.txt.gz -------------------------------------------------------------------------------- /resources/licenses/EPL-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/EPL-2.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/GPL-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/GPL-2.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/GPL-3.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/GPL-3.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/LGPL-2.1.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/LGPL-2.1.txt.gz -------------------------------------------------------------------------------- /resources/licenses/LGPL-3.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/LGPL-3.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/MPL-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/MPL-2.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/Unlicense.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/Unlicense.txt.gz -------------------------------------------------------------------------------- /resources/licenses/Apache-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/Apache-2.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/Artistic-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/Artistic-2.0.txt.gz -------------------------------------------------------------------------------- /resources/licenses/BSD-2-Clause.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/BSD-2-Clause.txt.gz -------------------------------------------------------------------------------- /resources/licenses/BSD-3-Clause.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/licenses/BSD-3-Clause.txt.gz -------------------------------------------------------------------------------- /resources/exceptions/LLVM-exception.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/exceptions/LLVM-exception.txt.gz -------------------------------------------------------------------------------- /resources/exceptions/GCC-exception-2.0.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/exceptions/GCC-exception-2.0.txt.gz -------------------------------------------------------------------------------- /resources/exceptions/GCC-exception-3.1.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/exceptions/GCC-exception-3.1.txt.gz -------------------------------------------------------------------------------- /resources/exceptions/Linux-syscall-note.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raftario/licensor/HEAD/resources/exceptions/Linux-syscall-note.txt.gz -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target/ 2 | **/*.rs.bk 3 | .idea/ 4 | resources/licenses/*.txt 5 | resources/exceptions/*.txt 6 | resources/license-list-data-*.tar.gz 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /licensor_codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "licensor_codegen" 3 | version = "0.1.0" 4 | authors = ["Raphaël Thériault "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | licensor_common = { path = "../licensor_common" } 9 | phf_codegen = "0.7.24" 10 | -------------------------------------------------------------------------------- /licensor_common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "licensor_common" 3 | version = "0.1.0" 4 | authors = ["Raphaël Thériault "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | serde_json = "1.0.41" 9 | [dependencies.serde] 10 | version = "1.0.102" 11 | features = ["derive"] 12 | -------------------------------------------------------------------------------- /licensor_fetch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "licensor_fetch" 3 | version = "0.2.0" 4 | authors = ["Raphaël Thériault "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | flate2 = "1.0.13" 9 | licensor_common = { path = "../licensor_common" } 10 | reqwest = "0.9.22" 11 | sha3 = "0.8.2" 12 | tar = "0.4.26" 13 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | pub struct LicenseReplace { 2 | pub year: Option<&'static str>, 3 | pub name: Option<&'static str>, 4 | } 5 | 6 | pub struct License { 7 | pub id: &'static str, 8 | pub replace: Option, 9 | pub copyright: Option<&'static str>, 10 | pub optional: Option<&'static [&'static str]>, 11 | } 12 | 13 | pub struct Exception { 14 | pub id: &'static str, 15 | pub with: Option<&'static [&'static str]>, 16 | pub optional: Option<&'static [&'static str]>, 17 | } 18 | -------------------------------------------------------------------------------- /LIST.md: -------------------------------------------------------------------------------- 1 | # Available licenses and exceptions 2 | 3 | [//]: # (This is an automatically generated file, do not edit it.) 4 | 5 | ## Licenses 6 | 7 | * AGPL-3.0 8 | * Apache-2.0 9 | * Artistic-2.0 10 | * BSD-2-Clause 11 | * BSD-3-Clause 12 | * CC0-1.0 13 | * CDDL-1.1 14 | * EPL-2.0 15 | * GPL-2.0 16 | * GPL-3.0 17 | * ISC 18 | * LGPL-2.1 19 | * LGPL-3.0 20 | * MIT 21 | * MPL-2.0 22 | * Unlicense 23 | * Zlib 24 | 25 | ## Exceptions 26 | 27 | * GCC-exception-2.0 28 | * GCC-exception-3.1 29 | * LLVM-exception 30 | * Linux-syscall-note 31 | -------------------------------------------------------------------------------- /resources/exceptions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "GCC-exception-2.0", 4 | "with": [ 5 | "GPL-2.0", 6 | "GPL-3.0" 7 | ] 8 | }, 9 | { 10 | "id": "GCC-exception-3.1", 11 | "with": [ 12 | "GPL-3.0" 13 | ], 14 | "optional": [ 15 | "GCC RUNTIME LIBRARY EXCEPTION\n\nVersion 3.1, 31 March 2009" 16 | ] 17 | }, 18 | { 19 | "id": "LLVM-exception", 20 | "with": [ 21 | "Apache-2.0" 22 | ], 23 | "optional": [ 24 | "LLVM Exceptions to the Apache 2.0 License " 25 | ] 26 | }, 27 | { 28 | "id": "Linux-syscall-note", 29 | "with": [ 30 | "GPL-2.0" 31 | ] 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /resources/exceptions-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "type": "array", 4 | "description": "Exception descriptor", 5 | "items": { 6 | "type": "object", 7 | "properties": { 8 | "id": { 9 | "type": "string", 10 | "description": "SPDX ID of the exception" 11 | }, 12 | "with": { 13 | "type": "array", 14 | "description": "IDs of the licenses this exception is meant to be used with", 15 | "items": { 16 | "type": "string" 17 | }, 18 | "uniqueItems": true 19 | }, 20 | "optional": { 21 | "type": "array", 22 | "description": "Strings to remove when skipping the optional contents of the license", 23 | "items": { 24 | "type": "string", 25 | "description": "Optional string" 26 | }, 27 | "uniqueItems": true 28 | } 29 | }, 30 | "required": [ 31 | "id" 32 | ] 33 | }, 34 | "uniqueItems": true 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Raphaël Thériault 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (including the next 11 | paragraph) shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "licensor" 3 | version = "2.1.0" 4 | authors = ["Raphaël Thériault "] 5 | license = "MIT" 6 | description = "write licenses to stdout" 7 | homepage = "https://github.com/raftario/licensor" 8 | repository = "https://github.com/raftario/licensor.git" 9 | readme = "README.md" 10 | keywords = [ 11 | "license", 12 | "cli", 13 | "spdx", 14 | "licensing", 15 | ] 16 | categories = ["command-line-utilities"] 17 | edition = "2018" 18 | 19 | [workspace] 20 | members = [ 21 | "licensor_codegen", 22 | "licensor_common", 23 | "licensor_fetch" 24 | ] 25 | 26 | [dependencies] 27 | calm_io = "0.1.1" 28 | chrono = "0.4.9" 29 | phf = "0.7.24" 30 | structopt = "0.3.5" 31 | textwrap = "0.11.0" 32 | [dependencies.flate2] 33 | version = "1.0.13" 34 | default-features = false 35 | features = ["rust_backend"] 36 | 37 | [dev-dependencies] 38 | assert_cmd = "0.11.1" 39 | predicates = "1.0.2" 40 | [dev-dependencies.cargo-husky] 41 | version = "1.4.0" 42 | default-features = false 43 | features = [ 44 | "run-for-all", 45 | "precommit-hook", 46 | "run-cargo-test", 47 | "run-cargo-clippy", 48 | "run-cargo-fmt", 49 | ] 50 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | matrix: 8 | os: [ubuntu-latest, windows-latest, macOS-latest] 9 | steps: 10 | - uses: actions/checkout@v1 11 | with: 12 | fetch-depth: 1 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | override: true 17 | - name: Build project 18 | uses: actions-rs/cargo@v1 19 | with: 20 | command: build 21 | args: --release 22 | - name: Strip binary (*nix) 23 | if: matrix.os != 'windows-latest' 24 | run: strip target/release/licensor 25 | - name: Archive binary (Linux) 26 | if: matrix.os == 'ubuntu-latest' 27 | uses: actions/upload-artifact@v1 28 | with: 29 | name: Linux 30 | path: target/release/licensor 31 | - name: Archive binary (Windows) 32 | if: matrix.os == 'windows-latest' 33 | uses: actions/upload-artifact@v1 34 | with: 35 | name: Windows 36 | path: target/release/licensor.exe 37 | - name: Archive binary (macOS) 38 | if: matrix.os == 'macOS-latest' 39 | uses: actions/upload-artifact@v1 40 | with: 41 | name: macOS 42 | path: target/release/licensor -------------------------------------------------------------------------------- /resources/licenses-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "type": "array", 4 | "items": { 5 | "type": "object", 6 | "description": "License descriptor", 7 | "properties": { 8 | "id": { 9 | "type": "string", 10 | "description": "SPDX ID of the license" 11 | }, 12 | "replace": { 13 | "type": "object", 14 | "description": "Keywords that need to be replaced in the license", 15 | "properties": { 16 | "year": { 17 | "type": "string", 18 | "description": "Copyright year" 19 | }, 20 | "name": { 21 | "type": "string", 22 | "description": "Copyright holder" 23 | } 24 | } 25 | }, 26 | "copyright": { 27 | "type": "string", 28 | "description": "String to remove when skipping the copyright notice in the license" 29 | }, 30 | "optional": { 31 | "type": "array", 32 | "description": "Strings to remove when skipping the optional contents of the license", 33 | "items": { 34 | "type": "string", 35 | "description": "Optional string" 36 | }, 37 | "uniqueItems": true 38 | } 39 | }, 40 | "required": [ 41 | "id" 42 | ] 43 | }, 44 | "uniqueItems": true 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Checks 2 | on: [pull_request] 3 | jobs: 4 | checks: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | with: 9 | fetch-depth: 1 10 | - uses: actions-rs/toolchain@v1 11 | with: 12 | toolchain: stable 13 | override: true 14 | - run: rustup component add rustfmt 15 | - run: rustup component add clippy 16 | - name: Run tests 17 | uses: actions-rs/cargo@v1 18 | with: 19 | command: test 20 | args: --all 21 | - name: Check for clippy warnings 22 | uses: actions-rs/cargo@v1 23 | with: 24 | command: clippy 25 | args: --all -- -D warnings 26 | - name: Check formatting 27 | uses: actions-rs/cargo@v1 28 | with: 29 | command: fmt 30 | args: --all -- --check 31 | - name: Fetch and parse licenses 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: run 35 | args: -p licensor_fetch 36 | - name: Generate code 37 | uses: actions-rs/cargo@v1 38 | with: 39 | command: run 40 | args: -p licensor_codegen 41 | - name: Format newly generated code 42 | uses: actions-rs/cargo@v1 43 | with: 44 | command: fmt 45 | args: --all 46 | - name: Check for changed files 47 | run: git diff --exit-code -------------------------------------------------------------------------------- /licensor_codegen/src/main.rs: -------------------------------------------------------------------------------- 1 | use phf_codegen::OrderedMap; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::PathBuf; 5 | 6 | fn get_path() -> PathBuf { 7 | let mut path = licensor_common::get_root_path(); 8 | path.push("src"); 9 | path.push("codegen.rs"); 10 | path 11 | } 12 | 13 | fn main() { 14 | eprintln!("Writing codegen.rs..."); 15 | 16 | let path = get_path(); 17 | let mut file = File::create(&path).expect("Can't create codegen.rs."); 18 | 19 | write!( 20 | &mut file, 21 | "// This is an automatically generated file, do not edit it.\n\n" 22 | ) 23 | .expect("Can't write to codegen.rs."); 24 | write!(&mut file, "#![allow(clippy::unreadable_literal)]\n\n") 25 | .expect("Can't write to codegen.rs."); 26 | write!( 27 | &mut file, 28 | "use crate::types::{{License, Exception, LicenseReplace}};\nuse phf::OrderedMap;\n\n" 29 | ) 30 | .expect("Can't write to codegen.rs."); 31 | 32 | let licenses = licensor_common::parse_licenses(); 33 | let exceptions = licensor_common::parse_exceptions(); 34 | 35 | let mut licenses_builder = OrderedMap::new(); 36 | let mut licenses_info_builder = OrderedMap::new(); 37 | for license in &licenses { 38 | licenses_builder.entry( 39 | license.id.as_str(), 40 | &format!( 41 | "include_bytes!(\"../resources/licenses/{}.txt.gz\")", 42 | license.id 43 | ), 44 | ); 45 | licenses_info_builder.entry(license.id.as_str(), &format!("{:?}", license)); 46 | } 47 | 48 | write!( 49 | &mut file, 50 | "pub static LICENSES: OrderedMap<&'static str, &'static [u8]> = " 51 | ) 52 | .expect("Can't write to codegen.rs."); 53 | licenses_builder 54 | .build(&mut file) 55 | .expect("Can't write to codegen.rs."); 56 | writeln!(&mut file, ";").expect("Can't write to codegen.rs."); 57 | write!( 58 | &mut file, 59 | "pub static LICENSES_INFO: OrderedMap<&'static str, License> = " 60 | ) 61 | .expect("Can't write to codegen.rs."); 62 | licenses_info_builder 63 | .build(&mut file) 64 | .expect("Can't write to codegen.rs."); 65 | writeln!(&mut file, ";").expect("Can't write to codegen.rs."); 66 | 67 | let mut exceptions_builder = OrderedMap::new(); 68 | let mut exceptions_info_builder = OrderedMap::new(); 69 | for exception in &exceptions { 70 | exceptions_builder.entry( 71 | exception.id.as_str(), 72 | &format!( 73 | "include_bytes!(\"../resources/exceptions/{}.txt.gz\")", 74 | exception.id 75 | ), 76 | ); 77 | exceptions_info_builder.entry(exception.id.as_str(), &format!("{:?}", exception)); 78 | } 79 | 80 | write!( 81 | &mut file, 82 | "pub static EXCEPTIONS: OrderedMap<&'static str, &'static [u8]> = " 83 | ) 84 | .expect("Can't write to codegen.rs."); 85 | exceptions_builder 86 | .build(&mut file) 87 | .expect("Can't write to codegen.rs."); 88 | writeln!(&mut file, ";").expect("Can't write to codegen.rs."); 89 | write!( 90 | &mut file, 91 | "pub static EXCEPTIONS_INFO: OrderedMap<&'static str, Exception> = " 92 | ) 93 | .expect("Can't write to codegen.rs."); 94 | exceptions_info_builder 95 | .build(&mut file) 96 | .expect("Can't write to codegen.rs."); 97 | writeln!(&mut file, ";").expect("Can't write to codegen.rs."); 98 | } 99 | -------------------------------------------------------------------------------- /licensor_common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde; 3 | 4 | use std::env; 5 | use std::fmt; 6 | use std::fs::File; 7 | use std::path::PathBuf; 8 | 9 | #[derive(Debug, Deserialize)] 10 | pub struct LicenseReplace { 11 | pub year: Option, 12 | pub name: Option, 13 | } 14 | 15 | #[derive(Deserialize)] 16 | pub struct License { 17 | pub id: String, 18 | pub replace: Option, 19 | pub copyright: Option, 20 | pub optional: Option>, 21 | } 22 | 23 | #[derive(Deserialize)] 24 | pub struct Exception { 25 | pub id: String, 26 | pub with: Option>, 27 | pub optional: Option>, 28 | } 29 | 30 | impl fmt::Debug for License { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | let optional = if let Some(optional) = &self.optional { 33 | format!("Some(&{:?})", optional) 34 | } else { 35 | "None".to_owned() 36 | }; 37 | write!( 38 | f, 39 | "License {{ id: {:?}, replace: {:?}, copyright: {:?}, optional: {} }}", 40 | &self.id, &self.replace, &self.copyright, optional 41 | ) 42 | } 43 | } 44 | 45 | impl fmt::Debug for Exception { 46 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 47 | let with = if let Some(with) = &self.with { 48 | format!("Some(&{:?})", with) 49 | } else { 50 | "None".to_owned() 51 | }; 52 | let optional = if let Some(optional) = &self.optional { 53 | format!("Some(&{:?})", optional) 54 | } else { 55 | "None".to_owned() 56 | }; 57 | write!( 58 | f, 59 | "Exception {{ id: {:?}, with: {}, optional: {} }}", 60 | &self.id, with, optional 61 | ) 62 | } 63 | } 64 | 65 | pub fn get_root_path() -> PathBuf { 66 | let cargo_manifest_dir = 67 | env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not defined"); 68 | let mut root_path = PathBuf::from(cargo_manifest_dir) 69 | .canonicalize() 70 | .expect("Invalid Cargo manifest directory"); 71 | if !root_path.pop() { 72 | panic!("Can't find root path."); 73 | } 74 | root_path 75 | } 76 | 77 | pub fn get_resources_path() -> PathBuf { 78 | let mut resources_path = get_root_path(); 79 | resources_path.push("resources"); 80 | resources_path 81 | } 82 | 83 | pub fn parse_licenses() -> Vec { 84 | eprintln!("Parsing licenses.json..."); 85 | let mut licenses_json_path = get_resources_path(); 86 | licenses_json_path.push("licenses.json"); 87 | let licenses_json = File::open(&licenses_json_path).expect("Can't read licenses.json"); 88 | serde_json::from_reader(licenses_json).expect("Can't parse licenses.json") 89 | } 90 | 91 | pub fn parse_exceptions() -> Vec { 92 | eprintln!("Parsing exceptions.json..."); 93 | let mut exceptions_json_path = get_resources_path(); 94 | exceptions_json_path.push("exceptions.json"); 95 | let exceptions_json = File::open(&exceptions_json_path).expect("Can't read exceptions.json"); 96 | serde_json::from_reader(exceptions_json).expect("Can't parse exceptions.json") 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | mod parse { 102 | use crate::{parse_exceptions, parse_licenses}; 103 | 104 | #[test] 105 | fn works() { 106 | let licenses = parse_licenses(); 107 | let exceptions = parse_exceptions(); 108 | 109 | assert!(!licenses.is_empty()); 110 | assert!(!exceptions.is_empty()); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # licensor 2 | 3 | ![Preview](./resources/preview.svg) 4 | 5 | write licenses to stdout 6 | 7 | [![GitHub Actions](https://img.shields.io/github/workflow/status/raftario/licensor/Build)](https://github.com/raftario/licensor/actions?workflowID=Build) [![crates.io](https://img.shields.io/crates/v/licensor?color=orange)](https://crates.io/crates/licensor) 8 | 9 | ## About 10 | 11 | Write a license to standard output given its SPDX ID. A name for the copyright holder can optionally be provided for licenses where it is included. If the provided ID isn't found, similar ones will be suggested. Licenses are all compiled into the binary. 12 | 13 | ## Features 14 | 15 | * Simple 16 | * Licenses are taken directly from SPDX, no slightly edited templates 17 | * Single binary, licenses are included into it at compile time 18 | * `WITH` exception expressions support 19 | * Ability to skip the copyright notice, which is allowed by the Berne convention 20 | * Adding new licenses just requires editing a JSON file 21 | 22 | ## Why 23 | 24 | I just got tired of losing time looking for license files for my new projects. 25 | 26 | ## Usage 27 | 28 | Here are a couple usage examples to get a general idea of how it all works. For detailed usage, just pass the `--help` flag. 29 | 30 | Write the MIT license with a copyright notice to `LICENSE`: 31 | 32 | ```sh 33 | $ licensor MIT "Raphaël Thériault" > LICENSE 34 | ``` 35 | 36 | Write the Apache 2.0 license with the LLVM exception to `LICENSE`, skipping optional parts: 37 | 38 | ```sh 39 | $ licensor "Apache-2.0 WITH LLVM-exception" --skip-optional > LICENSE 40 | ``` 41 | 42 | Print the BSD 3 Clause license without a copyright notice: 43 | 44 | ```sh 45 | $ licensor BSD-3-Clause 46 | ``` 47 | 48 | List available licenses 49 | ```sh 50 | $ licensor --licenses 51 | ``` 52 | 53 | ## Installation 54 | 55 | There are a few installation option available. 56 | 57 | You are welcome to distribute this software on other platforms, don't hesitate to open a PR to update this section if you do so! 58 | 59 | ### [Releases](https://github.com/raftario/licensor/releases/latest) 60 | 61 | Pre-compiled binaries of `licensor` can be downloaded from the release page. 62 | 63 | ### [Homebrew](https://formulae.brew.sh/formula/licensor) 64 | 65 | ```sh 66 | $ brew install licensor 67 | ``` 68 | 69 | ### [Crates.io](https://crates.io/crates/licensor) 70 | 71 | ```sh 72 | $ cargo install licensor 73 | ``` 74 | 75 | ## Available licenses and exceptions 76 | 77 | See [list](./LIST.md). 78 | 79 | ## Contributing 80 | 81 | Contributors are welcome. If you see anything that could be fixed/improved or have a new feature idea, just open an issue or a PR! 82 | 83 | However, try to keep the main CLI as simple and light as possible. Features such as adding licenses/exceptions at runtime or validating licenses are not planned and will not be added. 84 | 85 | ### Adding licenses 86 | 87 | If you'd like a license to be added to the list, you can either open an issue for it or add it yourself, which is fairly easy. 88 | 89 | To add a license, just add it to [`resources/licenses.json`](./resources/licenses.json) following the schema provided in [`resources/licenses-schema.json`](./resources/licenses-schema.json). To apply the changes to the main CLI, you'll also need to run both `cargo run -p licensor_fetch` and `cargo run -p licensor_codegen`, in that order. To get the information you need, just refer to the [SPDX License List](https://github.com/spdx/license-list-data). 90 | 91 | The same goes for exceptions. 92 | 93 | ## How it works 94 | 95 | First, licenses and exceptions specified in the resources files are parsed from the [SPDX License List](https://github.com/spdx/license-list-data), then gzipped, using the [licensor_fetch](./licensor_fetch) subcrate. Then the [codegen.rs](./src/codegen.rs) file is automatically generated based on the parsed licenses, using the [licensor_codegen](./licensor_codegen) subcrate, and included in the CLI. 96 | 97 | Finally, the main CLI is built on its own, which makes the build time relatively fast for end users and only requires dependencies of the main CLI and not ones required by helper subcrates. 98 | 99 | ## Credits 100 | 101 | Thanks to the amazing people on the [/r/rust](https://reddit.com/r/rust) subreddit who provided great feedback and hints, and showed way more enthusiasm than initially expected. 102 | 103 | ## Licensing 104 | 105 | `licensor` is licensed under the [MIT License](./LICENSE). 106 | -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use std::process::Command; 3 | 4 | #[test] 5 | fn list_licenses() { 6 | Command::cargo_bin("licensor") 7 | .unwrap() 8 | .args(&["-l"]) 9 | .assert() 10 | .success() 11 | .stdout(predicates::str::contains("\n")) 12 | .stderr(predicates::str::is_empty()); 13 | 14 | Command::cargo_bin("licensor") 15 | .unwrap() 16 | .args(&["--licenses"]) 17 | .assert() 18 | .success() 19 | .stdout(predicates::str::contains("\n")) 20 | .stderr(predicates::str::is_empty()); 21 | } 22 | 23 | #[test] 24 | fn list_exceptions() { 25 | Command::cargo_bin("licensor") 26 | .unwrap() 27 | .args(&["-e"]) 28 | .assert() 29 | .success() 30 | .stdout(predicates::str::contains("\n")) 31 | .stderr(predicates::str::is_empty()); 32 | 33 | Command::cargo_bin("licensor") 34 | .unwrap() 35 | .args(&["--exceptions"]) 36 | .assert() 37 | .success() 38 | .stdout(predicates::str::contains("\n")) 39 | .stderr(predicates::str::is_empty()); 40 | } 41 | 42 | #[test] 43 | fn license() { 44 | Command::cargo_bin("licensor") 45 | .unwrap() 46 | .args(&["MIT"]) 47 | .assert() 48 | .success() 49 | .stdout(predicates::str::starts_with("MIT License \n")) 50 | .stderr(predicates::str::is_empty()); 51 | } 52 | 53 | #[test] 54 | fn license_invalid() { 55 | Command::cargo_bin("licensor") 56 | .unwrap() 57 | .args(&["mit"]) 58 | .assert() 59 | .failure() 60 | .stdout(predicates::str::is_empty()) 61 | .stderr(predicates::str::starts_with("Invalid license ID.\n")); 62 | } 63 | 64 | #[test] 65 | fn license_and_name() { 66 | Command::cargo_bin("licensor") 67 | .unwrap() 68 | .args(&["MIT", "Raphaël Thériault"]) 69 | .assert() 70 | .success() 71 | .stdout(predicates::str::starts_with( 72 | "MIT License Copyright (c) 2022 Raphaël Thériault\n", 73 | )) 74 | .stderr(predicates::str::is_empty()); 75 | } 76 | 77 | #[test] 78 | fn license_with_exception() { 79 | Command::cargo_bin("licensor") 80 | .unwrap() 81 | .args(&["Apache-2.0 WITH LLVM-exception"]) 82 | .assert() 83 | .success() 84 | .stdout(predicates::str::contains( 85 | "limitations under the License.\n\n\nLLVM Exceptions to the Apache 2.0 License", 86 | )) 87 | .stderr(predicates::str::is_empty()); 88 | } 89 | 90 | #[test] 91 | fn license_with_exception_invalid() { 92 | Command::cargo_bin("licensor") 93 | .unwrap() 94 | .args(&["Apache-2.0 WITH llvm"]) 95 | .assert() 96 | .failure() 97 | .stdout(predicates::str::is_empty()) 98 | .stderr(predicates::str::starts_with("Invalid exception ID.\n")); 99 | } 100 | 101 | #[test] 102 | fn expr_invalid() { 103 | Command::cargo_bin("licensor") 104 | .unwrap() 105 | .args(&["Apache-2.0 with LLVM-exception"]) 106 | .assert() 107 | .failure() 108 | .stdout(predicates::str::is_empty()) 109 | .stderr("Invalid SPDX expression. Did you mean \"Apache-2.0 WITH LLVM-exception\"?\n"); 110 | } 111 | 112 | #[test] 113 | fn license_keep_placeholder() { 114 | Command::cargo_bin("licensor") 115 | .unwrap() 116 | .args(&["MIT", "-p"]) 117 | .assert() 118 | .success() 119 | .stdout(predicates::str::starts_with( 120 | "MIT License Copyright (c) \n", 121 | )) 122 | .stderr(predicates::str::is_empty()); 123 | 124 | Command::cargo_bin("licensor") 125 | .unwrap() 126 | .args(&["MIT", "--keep-placeholder"]) 127 | .assert() 128 | .success() 129 | .stdout(predicates::str::starts_with( 130 | "MIT License Copyright (c) \n", 131 | )) 132 | .stderr(predicates::str::is_empty()); 133 | } 134 | 135 | #[test] 136 | fn license_skip_optional() { 137 | Command::cargo_bin("licensor") 138 | .unwrap() 139 | .args(&["MIT", "-o"]) 140 | .assert() 141 | .success() 142 | .stdout(predicates::str::starts_with( 143 | "Permission is hereby granted, free of charge", 144 | )) 145 | .stderr(predicates::str::is_empty()); 146 | 147 | Command::cargo_bin("licensor") 148 | .unwrap() 149 | .args(&["MIT", "--skip-optional"]) 150 | .assert() 151 | .success() 152 | .stdout(predicates::str::starts_with( 153 | "Permission is hereby granted, free of charge", 154 | )) 155 | .stderr(predicates::str::is_empty()); 156 | } 157 | 158 | #[test] 159 | fn invalid() { 160 | Command::cargo_bin("licensor") 161 | .unwrap() 162 | .assert() 163 | .failure() 164 | .stdout(predicates::str::is_empty()) 165 | .stderr(predicates::str::contains("USAGE")); 166 | } 167 | -------------------------------------------------------------------------------- /licensor_fetch/src/main.rs: -------------------------------------------------------------------------------- 1 | use flate2::read::GzDecoder; 2 | use flate2::write::GzEncoder; 3 | use flate2::Compression; 4 | use sha3::{Digest, Sha3_256}; 5 | use std::ffi::OsStr; 6 | use std::fmt::Debug; 7 | use std::fs::File; 8 | use std::io::Write; 9 | use std::path::{Component, Path, PathBuf}; 10 | use std::{fs, io, process}; 11 | use tar::Archive; 12 | 13 | static LLD_ARCHIVE_URL: &str = "https://github.com/spdx/license-list-data/archive/v3.7.tar.gz"; 14 | static LLD_ARCHIVE_HASH: &[u8] = &[ 15 | 0xd6, 0xf5, 0x8a, 0x37, 0x00, 0x15, 0xe5, 0xa4, 0x7d, 0xc6, 0xd7, 0xf9, 0x7c, 0x5e, 0x4a, 0x92, 16 | 0xc1, 0x4b, 0x30, 0xf4, 0x6a, 0xca, 0x93, 0x43, 0xbb, 0xb6, 0xc5, 0xbd, 0xbd, 0x40, 0x44, 0x52, 17 | ]; 18 | 19 | fn hash_file + Debug>(src: P) -> Vec { 20 | eprintln!("Hashing {:?}...", src); 21 | let mut file = File::open(src).expect("Can't read file for hashing"); 22 | let mut hasher = Sha3_256::new(); 23 | io::copy(&mut file, &mut hasher).expect("Can't hash file"); 24 | hasher.result().to_vec() 25 | } 26 | 27 | fn download_file + Debug>(url: &str, dest: P) { 28 | eprintln!("Downloading {} to {:?}...", url, dest); 29 | let mut file = File::create(dest).expect("Can't create destination file"); 30 | let mut response = reqwest::get(url).expect("Can't download file"); 31 | io::copy(&mut response, &mut file).expect("Can't write response to file"); 32 | } 33 | 34 | fn decode_gz_file + Debug, W: Write>(src: P, dest: &mut W) { 35 | eprintln!("Decoding {:?}...", src); 36 | let file = File::open(src).expect("Can't read file for decoding"); 37 | let mut decoder = GzDecoder::new(file); 38 | io::copy(&mut decoder, dest).expect("Can't decode file"); 39 | } 40 | 41 | fn encode_file_gz + Debug, W: Write>(src: P, dest: &mut W, level: Compression) { 42 | eprintln!("Encoding {:?}...", src); 43 | let mut file = File::open(src).expect("Can't read file for encoding"); 44 | let mut encoder = GzEncoder::new(dest, level); 45 | io::copy(&mut file, &mut encoder).expect("Can't encode file"); 46 | } 47 | 48 | fn get_lld_archive_path() -> PathBuf { 49 | let mut lld_archive_path = licensor_common::get_resources_path(); 50 | lld_archive_path.push("license-list-data-3.7.tar.gz"); 51 | lld_archive_path 52 | } 53 | 54 | fn main() { 55 | let resources_path = licensor_common::get_resources_path(); 56 | 57 | let lld_archive_path = get_lld_archive_path(); 58 | let mut lld_archive_ok = false; 59 | if lld_archive_path.is_file() { 60 | eprintln!("Found license list archive. Checking hash..."); 61 | let hash = hash_file(&lld_archive_path); 62 | if hash.as_slice() == LLD_ARCHIVE_HASH { 63 | lld_archive_ok = true; 64 | } else { 65 | eprintln!("License list archive hash doesn't match expected one."); 66 | } 67 | } 68 | 69 | if !lld_archive_ok { 70 | download_file(LLD_ARCHIVE_URL, &lld_archive_path); 71 | let hash = hash_file(&lld_archive_path); 72 | if hash.as_slice() != LLD_ARCHIVE_HASH { 73 | eprintln!("Downloaded license list archive hash doesn't match expected one. Please try again and report the issue if it reappears."); 74 | process::exit(1); 75 | } 76 | } 77 | 78 | let mut decoded_archive: Vec = Vec::new(); 79 | decode_gz_file(&lld_archive_path, &mut decoded_archive); 80 | 81 | let licenses = licensor_common::parse_licenses(); 82 | let exceptions = licensor_common::parse_exceptions(); 83 | 84 | let mut parsed_licenses: Vec = Vec::new(); 85 | let mut parsed_exceptions: Vec = Vec::new(); 86 | 87 | let mut licenses_path = resources_path.clone(); 88 | licenses_path.push("licenses"); 89 | let mut exceptions_path = resources_path; 90 | exceptions_path.push("exceptions"); 91 | 92 | let mut lld_archive = Archive::new(decoded_archive.as_slice()); 93 | for file in lld_archive.entries().expect("Can't read archive") { 94 | let mut file = file.expect("Can't read archive file"); 95 | 96 | let filepath = file 97 | .header() 98 | .path() 99 | .expect("Can't read path from archive file") 100 | .to_path_buf(); 101 | let components: Vec = filepath.components().collect(); 102 | if components.len() == 3 && components[1].as_os_str() == OsStr::new("text") { 103 | let filename = components[2] 104 | .as_os_str() 105 | .to_str() 106 | .expect("Can't convert archive file filename to string"); 107 | 108 | for license in &licenses { 109 | if format!("{}.txt", license.id) == filename 110 | || format!("deprecated_{}.txt", license.id) == filename 111 | { 112 | eprintln!("Parsing license {}", license.id); 113 | 114 | let mut license_path = licenses_path.clone(); 115 | license_path.push(format!("{}.txt", license.id)); 116 | let mut license_file = 117 | File::create(&license_path).expect("Can't create license file"); 118 | io::copy(&mut file, &mut license_file).expect("Can't write license to file"); 119 | 120 | let mut encoded_license_path = licenses_path.clone(); 121 | encoded_license_path.push(format!("{}.txt.gz", license.id)); 122 | let mut encoded_license_file = File::create(&encoded_license_path) 123 | .expect("Can't create encoded license file"); 124 | encode_file_gz( 125 | &license_path, 126 | &mut encoded_license_file, 127 | Compression::best(), 128 | ); 129 | 130 | parsed_licenses.push(license.id.clone()); 131 | } 132 | } 133 | 134 | for exception in &exceptions { 135 | if format!("{}.txt", exception.id) == filename 136 | || format!("deprecated_{}.txt", exception.id) == filename 137 | { 138 | eprintln!("Parsing exception {}", exception.id); 139 | 140 | let mut exception_path = exceptions_path.clone(); 141 | exception_path.push(format!("{}.txt", exception.id)); 142 | let mut exception_file = 143 | File::create(&exception_path).expect("Can't create exception file"); 144 | io::copy(&mut file, &mut exception_file) 145 | .expect("Can't write exception to file"); 146 | 147 | let mut encoded_exception_path = exceptions_path.clone(); 148 | encoded_exception_path.push(format!("{}.txt.gz", exception.id)); 149 | let mut encoded_exception_file = File::create(&encoded_exception_path) 150 | .expect("Can't create encoded exception file"); 151 | encode_file_gz( 152 | &exception_path, 153 | &mut encoded_exception_file, 154 | Compression::best(), 155 | ); 156 | 157 | parsed_exceptions.push(exception.id.clone()); 158 | } 159 | } 160 | } 161 | } 162 | 163 | if licenses.len() != parsed_licenses.len() { 164 | eprintln!("Some licenses couldn't be parsed. Check for error in licenses.json."); 165 | process::exit(1); 166 | } 167 | if exceptions.len() != parsed_exceptions.len() { 168 | eprintln!("Some exceptions couldn't be parsed. Check for error in exceptions.json."); 169 | process::exit(1); 170 | } 171 | 172 | eprintln!("Writing list file..."); 173 | 174 | let mut list_path = licensor_common::get_root_path(); 175 | list_path.push("LIST.md"); 176 | 177 | let mut list_contents = "# Available licenses and exceptions\n\n".to_owned(); 178 | list_contents 179 | .push_str("[//]: # (This is an automatically generated file, do not edit it.)\n\n"); 180 | list_contents.push_str("## Licenses\n\n"); 181 | for license in &licenses { 182 | list_contents.push_str(&format!("* {}\n", license.id)); 183 | } 184 | list_contents.push_str("\n## Exceptions\n\n"); 185 | for exception in &exceptions { 186 | list_contents.push_str(&format!("* {}\n", exception.id)); 187 | } 188 | 189 | fs::write(&list_path, list_contents.as_bytes()).expect("Couldn't write to list file."); 190 | } 191 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod codegen; 2 | mod types; 3 | 4 | #[macro_use] 5 | extern crate calm_io; 6 | 7 | use crate::codegen::{EXCEPTIONS, EXCEPTIONS_INFO, LICENSES, LICENSES_INFO}; 8 | use chrono::{Datelike, Utc}; 9 | use flate2::read::GzDecoder; 10 | use phf::OrderedMap; 11 | use std::io; 12 | use std::io::Read; 13 | use std::process; 14 | use structopt::clap::ArgGroup; 15 | use structopt::StructOpt; 16 | 17 | fn actions_arg_group() -> ArgGroup<'static> { 18 | ArgGroup::with_name("actions").required(true) 19 | } 20 | 21 | #[derive(StructOpt)] 22 | #[structopt(group = actions_arg_group())] 23 | /// write licenses to stdout 24 | /// 25 | /// Write a license to standard output given a SPDX expression. 26 | /// A name for the copyright holder can optionally be provided for licenses where a notice is included. 27 | /// If the provided ID isn't found, similar ones will be suggested. 28 | /// Licenses are all compiled into the binary. 29 | struct Opt { 30 | /// Lists available licenses 31 | #[structopt(short = "l", long = "licenses", group = "actions")] 32 | list_licenses: bool, 33 | 34 | /// Lists available exceptions 35 | #[structopt(short = "e", long = "exceptions", group = "actions")] 36 | list_exceptions: bool, 37 | 38 | /// Keeps the copyright notice even if no name is specified 39 | #[structopt(short = "p", long = "keep-placeholder")] 40 | keep_placeholder: bool, 41 | 42 | /// Skips optional license contents 43 | #[structopt(short = "o", long = "skip-optional")] 44 | skip_optional: bool, 45 | 46 | /// SPDX license ID or expression 47 | /// 48 | /// WITH expressions are supported, i.e. "Apache-2.0 WITH LLVM-exception", just make sure to surround them with quotes. 49 | #[structopt(name = "LICENSE", group = "actions")] 50 | spdx_expr: Option, 51 | 52 | /// Name of the copyright holder 53 | /// 54 | /// Will be used in the copyright notice in licenses where it is present. 55 | /// Has no effect if the license has no copyright notice. 56 | #[structopt(name = "NAME")] 57 | copyright_holder: Option, 58 | } 59 | 60 | #[cfg_attr(test, derive(Debug, PartialEq))] 61 | struct SPDXExpr { 62 | license: String, 63 | exception: Option, 64 | } 65 | 66 | fn parse_spdx_expr(expr: String) -> io::Result { 67 | let expr: Vec<&str> = expr.split(' ').collect(); 68 | let len = expr.len(); 69 | if len == 1 { 70 | Ok(SPDXExpr { 71 | license: expr[0].to_owned(), 72 | exception: None, 73 | }) 74 | } else if len == 3 { 75 | if expr[1] == "WITH" { 76 | Ok(SPDXExpr { 77 | license: expr[0].to_owned(), 78 | exception: Some(expr[2].to_owned()), 79 | }) 80 | } else { 81 | stderrln!( 82 | "Invalid SPDX expression. Did you mean \"{} WITH {}\"?", 83 | expr[0], 84 | expr[2] 85 | )?; 86 | process::exit(1) 87 | } 88 | } else { 89 | stderrln!("Invalid SPDX expression.")?; 90 | process::exit(1) 91 | } 92 | } 93 | 94 | fn gz_decode_bytes(src: &[u8]) -> io::Result { 95 | let mut decoder = GzDecoder::new(src); 96 | let mut result = String::new(); 97 | decoder.read_to_string(&mut result)?; 98 | Ok(result) 99 | } 100 | 101 | fn clean_newlines(str: &mut String) { 102 | while str.starts_with('\n') { 103 | str.remove(0); 104 | } 105 | while str.ends_with('\n') { 106 | str.pop(); 107 | } 108 | } 109 | 110 | fn is_similar(l: &str, r: &str) -> bool { 111 | let ll = l.to_lowercase(); 112 | let lr = r.to_lowercase(); 113 | ll.contains(&lr) || lr.contains(&ll) 114 | } 115 | 116 | fn get_similar_keys(id: &str, iter: &OrderedMap<&'static str, &'static [u8]>) -> Vec { 117 | iter.keys() 118 | .filter_map(|key| { 119 | let key = key.to_string(); 120 | if is_similar(id, &key) { 121 | Some(key) 122 | } else { 123 | None 124 | } 125 | }) 126 | .collect() 127 | } 128 | 129 | fn parse_license(id: &str) -> io::Result { 130 | if let Some(gz_contents) = LICENSES.get(id) { 131 | if let Ok(contents) = gz_decode_bytes(gz_contents) { 132 | Ok(contents) 133 | } else { 134 | stdoutln!("Can't decode license.")?; 135 | unexpected()?; 136 | Err(io::Error::new( 137 | io::ErrorKind::InvalidInput, 138 | "Can't decode license.", 139 | )) 140 | } 141 | } else { 142 | stderrln!("Invalid license ID.")?; 143 | 144 | let similar = get_similar_keys(&id, &LICENSES); 145 | if !similar.is_empty() { 146 | stderrln!("Similar IDs: {}.", similar.join(", "))?; 147 | } 148 | 149 | process::exit(1); 150 | } 151 | } 152 | 153 | fn parse_exception(id: &str) -> io::Result { 154 | if let Some(gz_contents) = EXCEPTIONS.get(id) { 155 | if let Ok(contents) = gz_decode_bytes(gz_contents) { 156 | Ok(contents) 157 | } else { 158 | stdoutln!("Can't decode exception.")?; 159 | unexpected()?; 160 | Err(io::Error::new( 161 | io::ErrorKind::InvalidInput, 162 | "Can't decode exception.", 163 | )) 164 | } 165 | } else { 166 | stderrln!("Invalid exception ID.")?; 167 | 168 | let similar = get_similar_keys(&id, &EXCEPTIONS); 169 | if !similar.is_empty() { 170 | stderrln!("Similar IDs: {}.", similar.join(", "))?; 171 | } 172 | 173 | process::exit(1); 174 | } 175 | } 176 | 177 | fn list_licenses() -> io::Result<()> { 178 | for id in LICENSES.keys() { 179 | stdoutln!("{}", id)?; 180 | } 181 | Ok(()) 182 | } 183 | 184 | fn list_exceptions() -> io::Result<()> { 185 | for id in EXCEPTIONS.keys() { 186 | stdoutln!("{}", id)?; 187 | } 188 | Ok(()) 189 | } 190 | 191 | fn licensor_main(args: Opt) -> io::Result<()> { 192 | let expr = parse_spdx_expr(args.spdx_expr.unwrap())?; 193 | let mut valid_exception = true; 194 | 195 | let mut license = parse_license(&expr.license)?; 196 | license = license.replace('\r', ""); 197 | 198 | if let Some(license_info) = LICENSES_INFO.get(expr.license.as_str()) { 199 | if let Some(name) = args.copyright_holder { 200 | if let Some(replace) = &license_info.replace { 201 | if let Some(replace_year) = replace.year { 202 | let year = Utc::today().year().to_string(); 203 | license = license.replace(replace_year, &year); 204 | } 205 | if let Some(replace_name) = replace.name { 206 | license = license.replace(replace_name, &name); 207 | } 208 | } 209 | } else if let Some(copyright) = license_info.copyright { 210 | if !args.keep_placeholder { 211 | license = license.replace(copyright, ""); 212 | } 213 | } 214 | 215 | if let Some(optional) = license_info.optional { 216 | if args.skip_optional { 217 | for s in optional { 218 | license = license.replace(s, ""); 219 | } 220 | } 221 | } 222 | } 223 | 224 | clean_newlines(&mut license); 225 | 226 | if let Some(exception_id) = &expr.exception { 227 | let mut exception = parse_exception(exception_id)?; 228 | exception = exception.replace('\r', ""); 229 | 230 | if let Some(exception_info) = EXCEPTIONS_INFO.get(exception_id.as_str()) { 231 | if let Some(with) = &exception_info.with { 232 | if !with.iter().any(|l| l == &expr.license) { 233 | valid_exception = false; 234 | } 235 | } 236 | 237 | if let Some(optional) = exception_info.optional { 238 | if args.skip_optional { 239 | for s in optional { 240 | exception = exception.replace(s, ""); 241 | } 242 | } 243 | } 244 | } 245 | 246 | let max_length = { 247 | let license_lines: Vec<&str> = license.split('\n').collect(); 248 | let mut max_length = 40; 249 | for line in license_lines { 250 | let length = line.len(); 251 | if length > max_length { 252 | max_length = length; 253 | } 254 | } 255 | max_length 256 | }; 257 | exception = textwrap::fill(&exception, max_length); 258 | 259 | clean_newlines(&mut exception); 260 | 261 | license.push_str("\n\n\n"); 262 | license.push_str(&exception); 263 | } 264 | 265 | license.push('\n'); 266 | 267 | stdout!("{}", license)?; 268 | 269 | if !valid_exception { 270 | stderrln!("This exception wasn't designed to be used with this license. Please consider using another license or removing it.")?; 271 | } 272 | 273 | Ok(()) 274 | } 275 | 276 | fn unexpected() -> io::Result<()> { 277 | stderrln!("This shouldn't have happened. Please open an issue with the command you entered: .")?; 278 | process::exit(1); 279 | } 280 | 281 | #[pipefail] 282 | fn main() -> io::Result<()> { 283 | let args = Opt::from_args(); 284 | 285 | if args.list_licenses { 286 | list_licenses() 287 | } else if args.list_exceptions { 288 | list_exceptions() 289 | } else if args.spdx_expr.is_some() { 290 | licensor_main(args) 291 | } else { 292 | stderrln!("Invalid arguments.")?; 293 | unexpected() 294 | } 295 | } 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | mod parse_spdx_expr { 300 | use crate::{parse_spdx_expr, SPDXExpr}; 301 | 302 | #[test] 303 | fn simple_expr() { 304 | let result = parse_spdx_expr("MIT".to_owned()).unwrap(); 305 | let expected = SPDXExpr { 306 | license: "MIT".to_owned(), 307 | exception: None, 308 | }; 309 | 310 | assert_eq!(result, expected); 311 | } 312 | 313 | #[test] 314 | fn complex_expr() { 315 | let result = parse_spdx_expr("Apache-2.0 WITH LLVM-exception".to_owned()).unwrap(); 316 | let expected = SPDXExpr { 317 | license: "Apache-2.0".to_owned(), 318 | exception: Some("LLVM-exception".to_owned()), 319 | }; 320 | 321 | assert_eq!(result, expected); 322 | } 323 | } 324 | 325 | mod gz_decode_bytes { 326 | use crate::gz_decode_bytes; 327 | use flate2::write::GzEncoder; 328 | use flate2::Compression; 329 | use std::io::Write; 330 | 331 | #[test] 332 | fn works() { 333 | let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); 334 | encoder 335 | .write_all(b"MIT License Copyright (c) ") 336 | .unwrap(); 337 | let encoded = encoder.finish().unwrap(); 338 | 339 | let result = gz_decode_bytes(&encoded).unwrap(); 340 | let expected = "MIT License Copyright (c) ".to_owned(); 341 | 342 | assert_eq!(result, expected); 343 | } 344 | } 345 | 346 | mod clean_newlines { 347 | use crate::clean_newlines; 348 | 349 | #[test] 350 | fn works() { 351 | let mut result = "\n\nMIT\n\n".to_owned(); 352 | clean_newlines(&mut result); 353 | let expected = "MIT".to_owned(); 354 | 355 | assert_eq!(result, expected); 356 | } 357 | } 358 | 359 | mod is_similar { 360 | use crate::is_similar; 361 | 362 | #[test] 363 | fn yes_contains() { 364 | let l = "GPL-3.0"; 365 | let r = "gpl"; 366 | 367 | let result = is_similar(l, r); 368 | let expected = true; 369 | 370 | assert_eq!(result, expected); 371 | } 372 | 373 | #[test] 374 | fn yes_contained() { 375 | let l = "gpl"; 376 | let r = "GPL-3.0"; 377 | 378 | let result = is_similar(l, r); 379 | let expected = true; 380 | 381 | assert_eq!(result, expected); 382 | } 383 | 384 | #[test] 385 | fn no() { 386 | let l = "mit"; 387 | let r = "Apache-2.0"; 388 | 389 | let result = is_similar(l, r); 390 | let expected = false; 391 | 392 | assert_eq!(result, expected); 393 | } 394 | } 395 | 396 | mod get_similar_keys { 397 | use crate::{get_similar_keys, LICENSES}; 398 | 399 | #[test] 400 | fn works() { 401 | let result = get_similar_keys("gpl", &LICENSES); 402 | let expected = vec![ 403 | "AGPL-3.0".to_owned(), 404 | "GPL-2.0".to_owned(), 405 | "GPL-3.0".to_owned(), 406 | "LGPL-2.1".to_owned(), 407 | "LGPL-3.0".to_owned(), 408 | ]; 409 | 410 | assert_eq!(result, expected); 411 | } 412 | } 413 | 414 | mod parse_license { 415 | use crate::parse_license; 416 | 417 | #[test] 418 | fn works() { 419 | let result = parse_license("MIT").unwrap(); 420 | assert!(result.starts_with("MIT License Copyright (c) ")); 421 | } 422 | } 423 | 424 | mod parse_exception { 425 | use crate::parse_exception; 426 | 427 | #[test] 428 | fn works() { 429 | let result = parse_exception("LLVM-exception").unwrap(); 430 | assert!(result.starts_with("LLVM Exceptions to the Apache 2.0 License")); 431 | } 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /resources/licenses.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "AGPL-3.0", 4 | "optional": [ 5 | "GNU AFFERO GENERAL PUBLIC LICENSE\n\nVersion 3, 19 November 2007", 6 | " END OF\nTERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) \n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option)\nany later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Affero General Public License along\nwith this program. If not, see .\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf your software can interact with users remotely through a computer network,\nyou should also make sure that it provides a way for users to get its source.\nFor example, if your program is a web application, its interface could display\na \"Source\" link that leads users to an archive of the code. There are many\nways you could offer source, and different solutions will be better for different\nprograms; see section 13 for the specific requirements.\n\nYou should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. For\nmore information on this, and how to apply and follow the GNU AGPL, see ." 7 | ] 8 | }, 9 | { 10 | "id": "Apache-2.0", 11 | "optional": [ 12 | "Apache License\n\nVersion 2.0, January 2004\n\nhttp://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,\nAND DISTRIBUTION", 13 | " END OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own identifying\ninformation. (Don't include the brackets!) The text should be enclosed in\nthe appropriate comment syntax for the file format. We also recommend that\na file or class name and description of purpose be included on the same \"printed\npage\" as the copyright notice for easier identification within third-party\narchives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\n\nyou may not use this file except in compliance with the License.\n\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\n\ndistributed under the License is distributed on an \"AS IS\" BASIS,\n\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\nSee the License for the specific language governing permissions and\n\nlimitations under the License." 14 | ] 15 | }, 16 | { 17 | "id": "Artistic-2.0", 18 | "optional": [ 19 | "The Artistic License 2.0" 20 | ] 21 | }, 22 | { 23 | "id": "BSD-2-Clause", 24 | "replace": { 25 | "year": "", 26 | "name": "" 27 | }, 28 | "copyright": "Copyright (c) . All rights reserved." 29 | }, 30 | { 31 | "id": "BSD-3-Clause", 32 | "replace": { 33 | "year": "", 34 | "name": "" 35 | }, 36 | "copyright": "Copyright (c) . All rights reserved." 37 | }, 38 | { 39 | "id": "CC0-1.0", 40 | "optional": [ 41 | "Creative Commons Legal Code\n\nCC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES\nNOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE\nAN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION\nON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE\nOF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS\nLIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION\nOR WORKS PROVIDED HEREUNDER.\n\n" 42 | ] 43 | }, 44 | { 45 | "id": "CDDL-1.1", 46 | "optional": [ 47 | "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)\n\nVersion 1.1" 48 | ] 49 | }, 50 | { 51 | "id": "EPL-2.0", 52 | "optional": [ 53 | "Eclipse Public License - v 2.0" 54 | ] 55 | }, 56 | { 57 | "id": "GPL-2.0", 58 | "optional": [ 59 | "GNU GENERAL PUBLIC LICENSE\n\nVersion 2, June 1991", 60 | "END OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively convey the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) < yyyy> \n\nThis program is free software; you can redistribute it and/or modify it under\nthe terms of the GNU General Public License as published by the Free Software\nFoundation; either version 2 of the License, or (at your option) any later\nversion.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with\nthis program; if not, write to the Free Software Foundation, Inc., 51 Franklin\nStreet, Fifth Floor, Boston, MA 02110-1301, USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this when\nit starts in an interactive mode:\n\nGnomovision version 69, Copyright (C) year name of author Gnomovision comes\nwith ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software,\nand you are welcome to redistribute it under certain conditions; type `show\nc' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License. Of course, the commands you use may be\ncalled something other than `show w' and `show c'; they could even be mouse-clicks\nor menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. Here\nis a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision'\n(which makes passes at compilers) written by James Hacker.\n\n, 1 April 1989 Ty Coon, President of Vice This General\nPublic License does not permit incorporating your program into proprietary\nprograms. If your program is a subroutine library, you may consider it more\nuseful to permit linking proprietary applications with the library. If this\nis what you want to do, use the GNU Lesser General Public License instead\nof this License." 61 | ] 62 | }, 63 | { 64 | "id": "GPL-3.0", 65 | "optional": [ 66 | "GNU GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007", 67 | "END OF\nTERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) \n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU General Public License as published by the Free Software\nFoundation, either version 3 of the License, or (at your option) any later\nversion.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with\nthis program. If not, see .\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program does terminal interaction, make it output a short notice like\nthis when it starts in an interactive mode:\n\n Copyright (C) \n\nThis program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n\nThis is free software, and you are welcome to redistribute it under certain\nconditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License. Of course, your program's commands might\nbe different; for a GUI interface, you would use an \"about box\".\n\nYou should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. For\nmore information on this, and how to apply and follow the GNU GPL, see .\n\nThe GNU General Public License does not permit incorporating your program\ninto proprietary programs. If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary. If this is what you want to do, use the GNU Lesser General Public\nLicense instead of this License. But first, please read ." 68 | ] 69 | }, 70 | { 71 | "id": "ISC", 72 | "replace": { 73 | "year": "1995-2003", 74 | "name": "by Internet Software Consortium" 75 | }, 76 | "copyright": "Copyright (c) 1995-2003 by Internet Software Consortium", 77 | "optional": [ 78 | "ISC License Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. (\"ISC\")" 79 | ] 80 | }, 81 | { 82 | "id": "LGPL-2.1", 83 | "optional": [ 84 | "GNU LESSER GENERAL PUBLIC LICENSE\n\nVersion 2.1, February 1999", 85 | "END OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Libraries\n\nIf you develop a new library, and you want it to be of the greatest possible\nuse to the public, we recommend making it free software that everyone can\nredistribute and change. You can do so by permitting redistribution under\nthese terms (or, alternatively, under the terms of the ordinary General Public\nLicense).\n\nTo apply these terms, attach the following notices to the library. It is safest\nto attach them to the start of each source file to most effectively convey\nthe exclusion of warranty; and each file should have at least the \"copyright\"\nline and a pointer to where the full notice is found.\n\n< one line to give the library's name and an idea of what it does. >\n\nCopyright (C) < year > < name of author >\n\nThis library is free software; you can redistribute it and/or modify it under\nthe terms of the GNU Lesser General Public License as published by the Free\nSoftware Foundation; either version 2.1 of the License, or (at your option)\nany later version.\n\nThis library is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Lesser General Public License along\nwith this library; if not, write to the Free Software Foundation, Inc., 51\nFranklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information\non how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your school,\nif any, to sign a \"copyright disclaimer\" for the library, if necessary. Here\nis a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright interest in\n\nthe library `Frob' (a library for tweaking knobs) written\n\nby James Random Hacker.\n\n< signature of Ty Coon > , 1 April 1990\n\nTy Coon, President of Vice\n\nThat's all there is to it!" 86 | ] 87 | }, 88 | { 89 | "id": "LGPL-3.0", 90 | "optional": [ 91 | "GNU LESSER GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007" 92 | ] 93 | }, 94 | { 95 | "id": "MIT", 96 | "replace": { 97 | "year": "", 98 | "name": "" 99 | }, 100 | "copyright": "Copyright (c) ", 101 | "optional": [ 102 | "MIT License " 103 | ] 104 | }, 105 | { 106 | "id": "MPL-2.0", 107 | "optional": [ 108 | "Mozilla Public License Version 2.0", 109 | " Exhibit A - Source Code Form\nLicense Notice\n\nThis Source Code Form is subject to the terms of the Mozilla Public License,\nv. 2.0. If a copy of the MPL was not distributed with this file, You can obtain\none at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined\nby the Mozilla Public License, v. 2.0." 110 | ] 111 | }, 112 | { 113 | "id": "Unlicense", 114 | "optional": [ 115 | " For more information,\nplease refer to " 116 | ] 117 | }, 118 | { 119 | "id": "Zlib", 120 | "replace": { 121 | "year": "", 122 | "name": "" 123 | }, 124 | "copyright": "Copyright (c) ", 125 | "optional": [ 126 | "zlib License " 127 | ] 128 | } 129 | ] 130 | -------------------------------------------------------------------------------- /src/codegen.rs: -------------------------------------------------------------------------------- 1 | // This is an automatically generated file, do not edit it. 2 | 3 | #![allow(clippy::unreadable_literal)] 4 | 5 | use crate::types::{Exception, License, LicenseReplace}; 6 | use phf::OrderedMap; 7 | 8 | pub static LICENSES: OrderedMap<&'static str, &'static [u8]> = ::phf::OrderedMap { 9 | key: 732231254413039614, 10 | disps: ::phf::Slice::Static(&[(3, 0), (1, 0), (0, 3), (4, 15)]), 11 | idxs: ::phf::Slice::Static(&[1, 13, 15, 8, 7, 16, 9, 6, 5, 2, 14, 10, 11, 4, 3, 0, 12]), 12 | entries: ::phf::Slice::Static(&[ 13 | ( 14 | "AGPL-3.0", 15 | include_bytes!("../resources/licenses/AGPL-3.0.txt.gz"), 16 | ), 17 | ( 18 | "Apache-2.0", 19 | include_bytes!("../resources/licenses/Apache-2.0.txt.gz"), 20 | ), 21 | ( 22 | "Artistic-2.0", 23 | include_bytes!("../resources/licenses/Artistic-2.0.txt.gz"), 24 | ), 25 | ( 26 | "BSD-2-Clause", 27 | include_bytes!("../resources/licenses/BSD-2-Clause.txt.gz"), 28 | ), 29 | ( 30 | "BSD-3-Clause", 31 | include_bytes!("../resources/licenses/BSD-3-Clause.txt.gz"), 32 | ), 33 | ( 34 | "CC0-1.0", 35 | include_bytes!("../resources/licenses/CC0-1.0.txt.gz"), 36 | ), 37 | ( 38 | "CDDL-1.1", 39 | include_bytes!("../resources/licenses/CDDL-1.1.txt.gz"), 40 | ), 41 | ( 42 | "EPL-2.0", 43 | include_bytes!("../resources/licenses/EPL-2.0.txt.gz"), 44 | ), 45 | ( 46 | "GPL-2.0", 47 | include_bytes!("../resources/licenses/GPL-2.0.txt.gz"), 48 | ), 49 | ( 50 | "GPL-3.0", 51 | include_bytes!("../resources/licenses/GPL-3.0.txt.gz"), 52 | ), 53 | ("ISC", include_bytes!("../resources/licenses/ISC.txt.gz")), 54 | ( 55 | "LGPL-2.1", 56 | include_bytes!("../resources/licenses/LGPL-2.1.txt.gz"), 57 | ), 58 | ( 59 | "LGPL-3.0", 60 | include_bytes!("../resources/licenses/LGPL-3.0.txt.gz"), 61 | ), 62 | ("MIT", include_bytes!("../resources/licenses/MIT.txt.gz")), 63 | ( 64 | "MPL-2.0", 65 | include_bytes!("../resources/licenses/MPL-2.0.txt.gz"), 66 | ), 67 | ( 68 | "Unlicense", 69 | include_bytes!("../resources/licenses/Unlicense.txt.gz"), 70 | ), 71 | ("Zlib", include_bytes!("../resources/licenses/Zlib.txt.gz")), 72 | ]), 73 | }; 74 | pub static LICENSES_INFO: OrderedMap<&'static str, License> = ::phf::OrderedMap { 75 | key: 732231254413039614, 76 | disps: ::phf::Slice::Static(&[ 77 | (3, 0), 78 | (1, 0), 79 | (0, 3), 80 | (4, 15), 81 | ]), 82 | idxs: ::phf::Slice::Static(&[ 83 | 1, 84 | 13, 85 | 15, 86 | 8, 87 | 7, 88 | 16, 89 | 9, 90 | 6, 91 | 5, 92 | 2, 93 | 14, 94 | 10, 95 | 11, 96 | 4, 97 | 3, 98 | 0, 99 | 12, 100 | ]), 101 | entries: ::phf::Slice::Static(&[ 102 | ("AGPL-3.0", License { id: "AGPL-3.0", replace: None, copyright: None, optional: Some(&["GNU AFFERO GENERAL PUBLIC LICENSE\n\nVersion 3, 19 November 2007", " END OF\nTERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) \n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License as published by the Free\nSoftware Foundation, either version 3 of the License, or (at your option)\nany later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Affero General Public License along\nwith this program. If not, see .\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf your software can interact with users remotely through a computer network,\nyou should also make sure that it provides a way for users to get its source.\nFor example, if your program is a web application, its interface could display\na \"Source\" link that leads users to an archive of the code. There are many\nways you could offer source, and different solutions will be better for different\nprograms; see section 13 for the specific requirements.\n\nYou should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. For\nmore information on this, and how to apply and follow the GNU AGPL, see ."]) }), 103 | ("Apache-2.0", License { id: "Apache-2.0", replace: None, copyright: None, optional: Some(&["Apache License\n\nVersion 2.0, January 2004\n\nhttp://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,\nAND DISTRIBUTION", " END OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own identifying\ninformation. (Don\'t include the brackets!) The text should be enclosed in\nthe appropriate comment syntax for the file format. We also recommend that\na file or class name and description of purpose be included on the same \"printed\npage\" as the copyright notice for easier identification within third-party\narchives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\n\nyou may not use this file except in compliance with the License.\n\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\n\ndistributed under the License is distributed on an \"AS IS\" BASIS,\n\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\nSee the License for the specific language governing permissions and\n\nlimitations under the License."]) }), 104 | ("Artistic-2.0", License { id: "Artistic-2.0", replace: None, copyright: None, optional: Some(&["The Artistic License 2.0"]) }), 105 | ("BSD-2-Clause", License { id: "BSD-2-Clause", replace: Some(LicenseReplace { year: Some(""), name: Some("") }), copyright: Some("Copyright (c) . All rights reserved."), optional: None }), 106 | ("BSD-3-Clause", License { id: "BSD-3-Clause", replace: Some(LicenseReplace { year: Some(""), name: Some("") }), copyright: Some("Copyright (c) . All rights reserved."), optional: None }), 107 | ("CC0-1.0", License { id: "CC0-1.0", replace: None, copyright: None, optional: Some(&["Creative Commons Legal Code\n\nCC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES\nNOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE\nAN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION\nON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE\nOF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS\nLIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION\nOR WORKS PROVIDED HEREUNDER.\n\n"]) }), 108 | ("CDDL-1.1", License { id: "CDDL-1.1", replace: None, copyright: None, optional: Some(&["COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)\n\nVersion 1.1"]) }), 109 | ("EPL-2.0", License { id: "EPL-2.0", replace: None, copyright: None, optional: Some(&["Eclipse Public License - v 2.0"]) }), 110 | ("GPL-2.0", License { id: "GPL-2.0", replace: None, copyright: None, optional: Some(&["GNU GENERAL PUBLIC LICENSE\n\nVersion 2, June 1991", "END OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively convey the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) < yyyy> \n\nThis program is free software; you can redistribute it and/or modify it under\nthe terms of the GNU General Public License as published by the Free Software\nFoundation; either version 2 of the License, or (at your option) any later\nversion.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with\nthis program; if not, write to the Free Software Foundation, Inc., 51 Franklin\nStreet, Fifth Floor, Boston, MA 02110-1301, USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this when\nit starts in an interactive mode:\n\nGnomovision version 69, Copyright (C) year name of author Gnomovision comes\nwith ABSOLUTELY NO WARRANTY; for details type `show w\'. This is free software,\nand you are welcome to redistribute it under certain conditions; type `show\nc\' for details.\n\nThe hypothetical commands `show w\' and `show c\' should show the appropriate\nparts of the General Public License. Of course, the commands you use may be\ncalled something other than `show w\' and `show c\'; they could even be mouse-clicks\nor menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. Here\nis a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision\'\n(which makes passes at compilers) written by James Hacker.\n\n, 1 April 1989 Ty Coon, President of Vice This General\nPublic License does not permit incorporating your program into proprietary\nprograms. If your program is a subroutine library, you may consider it more\nuseful to permit linking proprietary applications with the library. If this\nis what you want to do, use the GNU Lesser General Public License instead\nof this License."]) }), 111 | ("GPL-3.0", License { id: "GPL-3.0", replace: None, copyright: None, optional: Some(&["GNU GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007", "END OF\nTERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible\nuse to the public, the best way to achieve this is to make it free software\nwhich everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion\nof warranty; and each file should have at least the \"copyright\" line and a\npointer to where the full notice is found.\n\n\n\nCopyright (C) \n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU General Public License as published by the Free Software\nFoundation, either version 3 of the License, or (at your option) any later\nversion.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with\nthis program. If not, see .\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program does terminal interaction, make it output a short notice like\nthis when it starts in an interactive mode:\n\n Copyright (C) \n\nThis program comes with ABSOLUTELY NO WARRANTY; for details type `show w\'.\n\nThis is free software, and you are welcome to redistribute it under certain\nconditions; type `show c\' for details.\n\nThe hypothetical commands `show w\' and `show c\' should show the appropriate\nparts of the General Public License. Of course, your program\'s commands might\nbe different; for a GUI interface, you would use an \"about box\".\n\nYou should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary. For\nmore information on this, and how to apply and follow the GNU GPL, see .\n\nThe GNU General Public License does not permit incorporating your program\ninto proprietary programs. If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary. If this is what you want to do, use the GNU Lesser General Public\nLicense instead of this License. But first, please read ."]) }), 112 | ("ISC", License { id: "ISC", replace: Some(LicenseReplace { year: Some("1995-2003"), name: Some("by Internet Software Consortium") }), copyright: Some("Copyright (c) 1995-2003 by Internet Software Consortium"), optional: Some(&["ISC License Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. (\"ISC\")"]) }), 113 | ("LGPL-2.1", License { id: "LGPL-2.1", replace: None, copyright: None, optional: Some(&["GNU LESSER GENERAL PUBLIC LICENSE\n\nVersion 2.1, February 1999", "END OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Libraries\n\nIf you develop a new library, and you want it to be of the greatest possible\nuse to the public, we recommend making it free software that everyone can\nredistribute and change. You can do so by permitting redistribution under\nthese terms (or, alternatively, under the terms of the ordinary General Public\nLicense).\n\nTo apply these terms, attach the following notices to the library. It is safest\nto attach them to the start of each source file to most effectively convey\nthe exclusion of warranty; and each file should have at least the \"copyright\"\nline and a pointer to where the full notice is found.\n\n< one line to give the library\'s name and an idea of what it does. >\n\nCopyright (C) < year > < name of author >\n\nThis library is free software; you can redistribute it and/or modify it under\nthe terms of the GNU Lesser General Public License as published by the Free\nSoftware Foundation; either version 2.1 of the License, or (at your option)\nany later version.\n\nThis library is distributed in the hope that it will be useful, but WITHOUT\nANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\nFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more\ndetails.\n\nYou should have received a copy of the GNU Lesser General Public License along\nwith this library; if not, write to the Free Software Foundation, Inc., 51\nFranklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information\non how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your school,\nif any, to sign a \"copyright disclaimer\" for the library, if necessary. Here\nis a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright interest in\n\nthe library `Frob\' (a library for tweaking knobs) written\n\nby James Random Hacker.\n\n< signature of Ty Coon > , 1 April 1990\n\nTy Coon, President of Vice\n\nThat\'s all there is to it!"]) }), 114 | ("LGPL-3.0", License { id: "LGPL-3.0", replace: None, copyright: None, optional: Some(&["GNU LESSER GENERAL PUBLIC LICENSE\n\nVersion 3, 29 June 2007"]) }), 115 | ("MIT", License { id: "MIT", replace: Some(LicenseReplace { year: Some(""), name: Some("") }), copyright: Some("Copyright (c) "), optional: Some(&["MIT License "]) }), 116 | ("MPL-2.0", License { id: "MPL-2.0", replace: None, copyright: None, optional: Some(&["Mozilla Public License Version 2.0", " Exhibit A - Source Code Form\nLicense Notice\n\nThis Source Code Form is subject to the terms of the Mozilla Public License,\nv. 2.0. If a copy of the MPL was not distributed with this file, You can obtain\none at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined\nby the Mozilla Public License, v. 2.0."]) }), 117 | ("Unlicense", License { id: "Unlicense", replace: None, copyright: None, optional: Some(&[" For more information,\nplease refer to "]) }), 118 | ("Zlib", License { id: "Zlib", replace: Some(LicenseReplace { year: Some(""), name: Some("") }), copyright: Some("Copyright (c) "), optional: Some(&["zlib License "]) }), 119 | ]), 120 | }; 121 | pub static EXCEPTIONS: OrderedMap<&'static str, &'static [u8]> = ::phf::OrderedMap { 122 | key: 6925680744564340301, 123 | disps: ::phf::Slice::Static(&[(3, 0)]), 124 | idxs: ::phf::Slice::Static(&[2, 3, 0, 1]), 125 | entries: ::phf::Slice::Static(&[ 126 | ( 127 | "GCC-exception-2.0", 128 | include_bytes!("../resources/exceptions/GCC-exception-2.0.txt.gz"), 129 | ), 130 | ( 131 | "GCC-exception-3.1", 132 | include_bytes!("../resources/exceptions/GCC-exception-3.1.txt.gz"), 133 | ), 134 | ( 135 | "LLVM-exception", 136 | include_bytes!("../resources/exceptions/LLVM-exception.txt.gz"), 137 | ), 138 | ( 139 | "Linux-syscall-note", 140 | include_bytes!("../resources/exceptions/Linux-syscall-note.txt.gz"), 141 | ), 142 | ]), 143 | }; 144 | pub static EXCEPTIONS_INFO: OrderedMap<&'static str, Exception> = ::phf::OrderedMap { 145 | key: 6925680744564340301, 146 | disps: ::phf::Slice::Static(&[(3, 0)]), 147 | idxs: ::phf::Slice::Static(&[2, 3, 0, 1]), 148 | entries: ::phf::Slice::Static(&[ 149 | ( 150 | "GCC-exception-2.0", 151 | Exception { 152 | id: "GCC-exception-2.0", 153 | with: Some(&["GPL-2.0", "GPL-3.0"]), 154 | optional: None, 155 | }, 156 | ), 157 | ( 158 | "GCC-exception-3.1", 159 | Exception { 160 | id: "GCC-exception-3.1", 161 | with: Some(&["GPL-3.0"]), 162 | optional: Some(&["GCC RUNTIME LIBRARY EXCEPTION\n\nVersion 3.1, 31 March 2009"]), 163 | }, 164 | ), 165 | ( 166 | "LLVM-exception", 167 | Exception { 168 | id: "LLVM-exception", 169 | with: Some(&["Apache-2.0"]), 170 | optional: Some(&["LLVM Exceptions to the Apache 2.0 License "]), 171 | }, 172 | ), 173 | ( 174 | "Linux-syscall-note", 175 | Exception { 176 | id: "Linux-syscall-note", 177 | with: Some(&["GPL-2.0"]), 178 | optional: None, 179 | }, 180 | ), 181 | ]), 182 | }; 183 | -------------------------------------------------------------------------------- /resources/preview.svg: -------------------------------------------------------------------------------- 1 | $$l$li$lic$lice$licen$licens$licenso$licensor$licensorGPLInvalidlicenseID.SimilarIDs:AGPL-3.0,GPL-2.0,GPL-3.0,LGPL-2.1,LGPL-3.0.$licensorMIT$licensorMIT"Raphaël$licensorMIT"RaphaëlThériault"$licensorMIT"RaphaëlThériault">$licensorMIT"RaphaëlThériault">LICENSE$cat$catLICENSEMITLicenseCopyright(c)2019RaphaëlThériaultPermissionisherebygranted,freeofcharge,toanypersonobtainingacopyofthissoftwareandassociateddocumentationfiles(the"Software"),todealintheSoftwarewithoutrestriction,includingwithoutlimitationtherightstouse,copy,modify,merge,publish,distribute,sublicense,and/orsellcopiesoftheSoftware,andtopermitpersonstowhomtheSoftwareisfurnishedtodoso,subjecttothefollowingconditions:Theabovecopyrightnoticeandthispermissionnotice(includingthenextparagraph)shallbeincludedinallcopiesorsubstantialportionsoftheSoftware.THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANYKIND,EXPRESSORIMPLIED,INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY,FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT.INNOEVENTSHALLTHEAUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM,DAMAGESOROTHERLIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,ARISINGFROM,OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOROTHERDEALINGSINTHESOFTWARE.$licensorG$licensorGPSimilarIDs:SimilarIDs:AGPL-3.0,GPL-2.0,GPL-3.0,LGPL-2.1,LGPL-3.0$licensorM$licensorMI$licensorMIT"$licensorMIT"R$licensorMIT"Ra$licensorMIT"Rap$licensorMIT"Raph$licensorMIT"Rapha$licensorMIT"Raphaë$licensorMIT"RaphaëlT$licensorMIT"RaphaëlTh$licensorMIT"RaphaëlThé$licensorMIT"RaphaëlThér$licensorMIT"RaphaëlThéri$licensorMIT"RaphaëlThéria$licensorMIT"RaphaëlThériau$licensorMIT"RaphaëlThériaul$licensorMIT"RaphaëlThériault$licensorMIT"RaphaëlThériault">L$licensorMIT"RaphaëlThériault">LI$licensorMIT"RaphaëlThériault">LIC$licensorMIT"RaphaëlThériault">LICE$licensorMIT"RaphaëlThériault">LICEN$licensorMIT"RaphaëlThériault">LICENS$c$ca$catL$catLI$catLIC$catLICE$catLICEN$catLICENS --------------------------------------------------------------------------------