use serde::{Deserialize, Serialize};
use std::env;
use std::error::Error;
use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct License {
name: String,
license_id: String,
license_text: String,
standard_license_header: Option<String>,
license_comments: Option<String>,
#[serde(default)]
see_also: Vec<String>,
#[serde(default)]
is_osi_approved: bool,
#[serde(default)]
is_fsf_libre: bool,
is_deprecated_license_id: bool,
}
impl License {
fn ident(&self) -> String {
match self.license_id.as_str() {
"0BSD" => "Bsd0".to_string(),
"3D-Slicer-1.0" => "ThreeDSlicer1_0".to_string(),
id => {
let ident = id.replace(['-', '.'], "_").replace('+', "_plus");
reword::pascal_case(ident)
}
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Exception {
name: String,
license_exception_id: String,
license_exception_text: String,
license_comments: Option<String>,
is_deprecated_license_id: bool,
#[serde(default)]
see_also: Vec<String>,
}
impl Exception {
fn ident(&self) -> String {
match self.license_exception_id.as_str() {
"389-exception" => "Exception389".to_string(),
id => {
let ident = id.replace(['-', '.'], "_");
reword::pascal_case(ident)
}
}
}
}
fn build_licenses_from_json(input: &Path, output: &Path) -> Result<(), Box<dyn Error>> {
let inner = File::create(output)?;
let mut f = BufWriter::with_capacity(4_194_304, inner);
let mut licenses = Vec::with_capacity(512);
f.write_all(b"pub(crate) fn parse_id(id: &str) -> Option<&'static dyn crate::License> {\n")?;
f.write_all(b" match id {\n")?;
for entry in fs::read_dir(input)? {
let entry = entry?;
let rdr = File::open(entry.path())?;
let license: License = serde_json::from_reader(BufReader::with_capacity(131_072, rdr))?;
writeln!(
f,
" {:?} => Some(&{}),",
license.license_id,
license.ident()
)?;
licenses.push(license);
}
f.write_all(b" _ => None,\n")?;
f.write_all(b" }\n")?;
f.write_all(b"}\n\n")?;
for license in licenses {
writeln!(
f,
include_str!("TEMPLATE-FOR-LICENSE.txt"),
ident = license.ident(),
id = license.license_id,
name = license.name,
text = license.license_text,
header = license.standard_license_header,
osi = license.is_osi_approved,
fsf = license.is_fsf_libre,
deprecated = license.is_deprecated_license_id,
comments = license.license_comments,
see_also = license.see_also,
)?;
}
Ok(())
}
fn build_exceptions_from_json(input: &Path, output: &Path) -> Result<(), Box<dyn Error>> {
let inner = File::create(output)?;
let mut f = BufWriter::with_capacity(524_288, inner);
let mut exceptions = Vec::with_capacity(64);
f.write_all(b"pub(crate) fn parse_id(id: &str) -> Option<&'static dyn crate::Exception> {\n")?;
f.write_all(b" match id {\n")?;
for entry in fs::read_dir(input)? {
let entry = entry?;
let rdr = File::open(entry.path())?;
let exception: Exception = serde_json::from_reader(BufReader::with_capacity(131_072, rdr))?;
writeln!(
f,
" {:?} => Some(&{}),",
exception.license_exception_id,
exception.ident()
)?;
exceptions.push(exception);
}
f.write_all(b" _ => None,\n")?;
f.write_all(b" }\n")?;
f.write_all(b"}\n\n")?;
for exception in exceptions {
writeln!(
f,
include_str!("TEMPLATE-FOR-EXCEPTION.txt"),
ident = exception.ident(),
id = exception.license_exception_id,
name = exception.name,
text = exception.license_exception_text,
deprecated = exception.is_deprecated_license_id,
comments = exception.license_comments,
see_also = exception.see_also,
)?;
}
Ok(())
}
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = PathBuf::from(out_dir);
build_licenses_from_json(
Path::new("license-list-data/json/details"),
&out_dir.join("licenses.rs"),
)
.expect("failed to build licenses.rs");
build_exceptions_from_json(
Path::new("license-list-data/json/exceptions"),
&out_dir.join("exceptions.rs"),
)
.expect("failed to build exceptions.rs");
}