Skip to content

Commit

Permalink
feat(free): first pass at license detection
Browse files Browse the repository at this point in the history
  • Loading branch information
fosskers committed Jul 6, 2024
1 parent 02f7854 commit 7027839
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 0 deletions.
1 change: 1 addition & 0 deletions rust/aura-pm/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) mod cache;
pub(crate) mod check;
pub(crate) mod conf;
pub(crate) mod deps;
pub(crate) mod free;
pub(crate) mod logs;
pub(crate) mod misc;
pub(crate) mod open;
Expand Down
158 changes: 158 additions & 0 deletions rust/aura-pm/src/command/free.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//! License detection.

use applying::Apply;
use r2d2_alpm::Alpm;
use std::collections::HashSet;
use std::ops::Not;

const COPYLEFT: &[&str] = &[
"GPL", "AGPL", "LGPL", "MPL", "EUPL", "OSL", "EPL", "CPL", "IPL",
];

// As found here: https://opensource.org/licenses
const FOSS_LICENSES: &[&str] = &[
"0BSD",
"AAL",
"AFL",
"AGPL",
"APSL",
"Apache",
"Artistic",
"BSD",
"BSL",
"BlueOak",
"CAL",
"CECILL",
"CPAL",
"CPL",
"ECL",
"EFL",
"EPL",
"EUPL",
"GPL",
"IPA",
"ISC",
"IPL",
"LGPL",
"LPPL",
"LiLiQ",
"MIT",
"MPL",
"MirOS",
"Motosoto",
"MulanPSL",
"Multics",
"NCSA",
"NPOSL",
"NTP",
"OFL",
"OLDAP",
"OSET",
"OSL",
"PHP",
"PostgreSQL",
"RPSL",
"RSCPL",
"SPL",
"SimPL",
"Sleepycat",
"UCL",
"UPL",
"Unlicense",
"VSL",
"Xnet",
"Zlib",
"eCos",
"QPL",
"PSF",
"NGPL",
"NOKIA",
"OGTSL",
];

pub fn free(alpm: &Alpm) {
let lics: HashSet<_> = FOSS_LICENSES.into_iter().map(|s| *s).collect();

println!("Not using FOSS licenses:");

for p in alpm.as_ref().localdb().pkgs() {
let ls: HashSet<_> = p.licenses().iter().flat_map(parse_licenses).collect();

if ls.iter().any(|l| lics.contains(l)).not() {
println!("{}: {:?}", p.name(), ls);
}
}
}

pub fn copyleft(alpm: &Alpm) {
let lics: HashSet<_> = COPYLEFT.into_iter().map(|s| *s).collect();

println!("Not using Copyleft licenses:");

for p in alpm.as_ref().localdb().pkgs() {
let ls: HashSet<_> = p.licenses().iter().flat_map(parse_licenses).collect();

if ls.iter().any(|l| lics.contains(l)).not() {
println!("{}: {:?}", p.name(), ls);
}
}
}

// /// All licenses currently associated with installed packages.
// fn licenses(alpm: &Alpm) -> HashSet<&str> {
// alpm.as_ref()
// .localdb()
// .pkgs()
// .iter()
// .flat_map(|p| p.licenses())
// .collect()
// }

fn parse_licenses(raw: &str) -> HashSet<&str> {
raw.split("AND")
.flat_map(|s| s.split("OR"))
.map(parse_prefix)
.collect()
}

fn parse_prefix(lic: &str) -> &str {
lic.split_once('-')
.map(|(l, _)| l)
.unwrap_or(lic)
.apply(|s| s.trim())
.apply(trim_numbers)
}

fn trim_numbers(lic: &str) -> &str {
lic.trim_end_matches(|c: char| c == '.' || c.is_ascii_digit())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn prefix() {
assert_eq!("MPL", parse_prefix("MPL-1.1"));
assert_eq!("MPL", parse_prefix("MPL"));
assert_eq!("GPL", parse_prefix("GPL2.1"));
}

#[test]
fn parsing() {
let mut set = HashSet::new();
set.insert("MPL");
set.insert("LGPL");
assert_eq!(set, parse_licenses("MPL-1.1 OR LGPL-2.1-only"));

let mut set = HashSet::new();
set.insert("MIT");
assert_eq!(set, parse_licenses("MIT"));

let mut set = HashSet::new();
set.insert("MIT");
set.insert("BSD");
set.insert("SGI");
assert_eq!(set, parse_licenses("MIT AND BSD-3-Clause AND SGI-B-2.0"));
}
}
10 changes: 10 additions & 0 deletions rust/aura-pm/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ pub enum SubCmd {
Stats(Stats),
/// The people behind Aura.
Thanks,
/// State of Free Software installed on the system.
Free(Free),
}

/// Synchronize official packages.
Expand Down Expand Up @@ -1100,6 +1102,14 @@ pub struct Stats {
pub heavy: bool,
}

/// State of Free Software installed on the system.
#[derive(Parser, Debug)]
pub struct Free {
/// Consider only Copyleft licenses.
#[clap(long, display_order = 1)]
pub copyleft: bool,
}

/// Synchronize AUR packages.
#[derive(Parser, Debug)]
#[clap(short_flag = 'A', long_flag = "aursync")]
Expand Down
4 changes: 4 additions & 0 deletions rust/aura-pm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ use aura_pm::flags::AURA_GLOBALS;
use aura_pm::ENGLISH;
use clap::Parser;
use colored::Colorize;
use command::free;
use env::Env;
use i18n_embed::fluent::FluentLanguageLoader;
use i18n_embed::LanguageLoader;
Expand Down Expand Up @@ -233,6 +234,9 @@ fn work(args: Args, env: Env, fll: &FluentLanguageLoader) -> Result<(), Error> {
SubCmd::Check(_) => check::check(fll, &env)?,
// --- Credits --- //
SubCmd::Thanks => thanks::thanks(fll),
// --- Free Software --- //
SubCmd::Free(f) if f.copyleft => free::copyleft(&env.alpm()?),
SubCmd::Free(_) => free::free(&env.alpm()?),
}

Ok(())
Expand Down

0 comments on commit 7027839

Please sign in to comment.