1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
use anyhow::Error;
use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf;
fn main() -> Result<(), Error> {
let dest = env_path("DEST")?;
let license_metadata = env_path("LICENSE_METADATA")?;
let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
let mut buffer = Vec::new();
render_recursive(&metadata.files, &mut buffer, 0)?;
std::fs::write(&dest, &buffer)?;
Ok(())
}
fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
match node {
Node::Root { children } => {
for child in children {
render_recursive(child, buffer, depth)?;
}
}
Node::Directory { name, children, license } => {
render_license(&prefix, std::iter::once(name), std::iter::once(license), buffer)?;
if !children.is_empty() {
writeln!(buffer, "{prefix}")?;
writeln!(buffer, "{prefix}*Exceptions:*")?;
for child in children {
writeln!(buffer, "{prefix}")?;
render_recursive(child, buffer, depth + 1)?;
}
}
}
Node::CondensedDirectory { name, licenses } => {
render_license(&prefix, std::iter::once(name), licenses.iter(), buffer)?;
}
Node::Group { files, directories, license } => {
render_license(
&prefix,
directories.iter().chain(files.iter()),
std::iter::once(license),
buffer,
)?;
}
Node::File { name, license } => {
render_license(&prefix, std::iter::once(name), std::iter::once(license), buffer)?;
}
}
Ok(())
}
fn render_license<'a>(
prefix: &str,
names: impl Iterator<Item = &'a String>,
licenses: impl Iterator<Item = &'a License>,
buffer: &mut Vec<u8>,
) -> Result<(), Error> {
let mut spdxs = BTreeSet::new();
let mut copyrights = BTreeSet::new();
for license in licenses {
spdxs.insert(&license.spdx);
for copyright in &license.copyright {
copyrights.insert(copyright);
}
}
for name in names {
writeln!(buffer, "{prefix}**`{name}`** ")?;
}
for spdx in spdxs.iter() {
writeln!(buffer, "{prefix}License: `{spdx}` ")?;
}
for (i, copyright) in copyrights.iter().enumerate() {
let suffix = if i == copyrights.len() - 1 { "" } else { " " };
writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
}
Ok(())
}
#[derive(serde::Deserialize)]
struct Metadata {
files: Node,
}
#[derive(serde::Deserialize)]
#[serde(rename_all = "kebab-case", tag = "type")]
pub(crate) enum Node {
Root { children: Vec<Node> },
Directory { name: String, children: Vec<Node>, license: License },
CondensedDirectory { name: String, licenses: Vec<License> },
File { name: String, license: License },
Group { files: Vec<String>, directories: Vec<String>, license: License },
}
#[derive(serde::Deserialize)]
struct License {
spdx: String,
copyright: Vec<String>,
}
fn env_path(var: &str) -> Result<PathBuf, Error> {
if let Some(var) = std::env::var_os(var) {
Ok(var.into())
} else {
anyhow::bail!("missing environment variable {var}")
}
}
|