use crate::{
Krate,
diag::{CfgCoord, Diag, Diagnostic, Label, Severity},
};
#[derive(
strum::Display,
strum::EnumString,
strum::EnumIter,
strum::IntoStaticStr,
Copy,
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
)]
#[strum(serialize_all = "kebab-case")]
pub enum Code {
Accepted,
Rejected,
Unlicensed,
SkippedPrivateWorkspaceCrate,
LicenseNotEncountered,
LicenseExceptionNotEncountered,
MissingClarificationFile,
ParseError,
EmptyLicenseField,
NoLicenseField,
GatherFailure,
}
impl Code {
#[inline]
pub fn description(self) -> &'static str {
match self {
Self::Accepted => "A license was explicitly accepted",
Self::Rejected => "A license was not explicitly accepted",
Self::Unlicensed => "A license expression could not be determined for a crate",
Self::SkippedPrivateWorkspaceCrate => {
"A private workspace crate was skipped during the license check"
}
Self::LicenseNotEncountered => {
"An allowed license was not used by any crate in the graph"
}
Self::LicenseExceptionNotEncountered => {
"A license exception was not used by any crate in the graph"
}
Self::MissingClarificationFile => {
"A license clarification file was not found in the crate's source"
}
Self::ParseError => "Failed to parse an SPDX expression",
Self::EmptyLicenseField => "A crate declared an empty license field",
Self::NoLicenseField => "A crate did not have a license field",
Self::GatherFailure => {
"An error occurred gathering a license file from the crate's source"
}
}
}
}
impl From<Code> for String {
fn from(c: Code) -> Self {
c.to_string()
}
}
#[inline]
pub(crate) fn diag(diag: Diagnostic, code: Code) -> Diag {
Diag::new(diag, Some(crate::diag::DiagnosticCode::License(code)))
}
pub(crate) struct Unlicensed<'a> {
pub(crate) severity: Severity,
pub(crate) krate: &'a Krate,
}
impl<'a> From<Unlicensed<'a>> for Diag {
fn from(u: Unlicensed<'a>) -> Self {
diag(
Diagnostic::new(u.severity).with_message(format_args!("{} is unlicensed", u.krate)),
Code::Unlicensed,
)
}
}
pub(crate) struct SkippedPrivateWorkspaceCrate<'a> {
pub(crate) krate: &'a Krate,
}
impl<'a> From<SkippedPrivateWorkspaceCrate<'a>> for Diag {
fn from(spwc: SkippedPrivateWorkspaceCrate<'a>) -> Self {
diag(
Diagnostic::new(Severity::Note).with_message(format_args!(
"skipping private workspace crate '{}'",
spwc.krate
)),
Code::SkippedPrivateWorkspaceCrate,
)
}
}
pub(crate) struct UnmatchedLicenseAllowance {
pub(crate) severity: Severity,
pub(crate) allowed_license_cfg: CfgCoord,
}
impl From<UnmatchedLicenseAllowance> for Diag {
fn from(ula: UnmatchedLicenseAllowance) -> Self {
diag(
Diagnostic::new(ula.severity)
.with_message("license was not encountered")
.with_labels(vec![
ula.allowed_license_cfg
.into_label()
.with_message("unmatched license allowance"),
]),
Code::LicenseNotEncountered,
)
}
}
pub(crate) struct UnmatchedLicenseException {
pub(crate) severity: Severity,
pub(crate) license_exc_cfg: CfgCoord,
}
impl From<UnmatchedLicenseException> for Diag {
fn from(ule: UnmatchedLicenseException) -> Self {
diag(
Diagnostic::new(ule.severity)
.with_message("license exception was not encountered")
.with_labels(vec![
ule.license_exc_cfg
.into_label()
.with_message("unmatched license exception"),
]),
Code::LicenseExceptionNotEncountered,
)
}
}
pub(crate) struct MissingClarificationFile<'a> {
pub(crate) expected: &'a crate::cfg::Spanned<crate::PathBuf>,
pub(crate) cfg_file_id: crate::diag::FileId,
}
impl<'a> From<MissingClarificationFile<'a>> for Diag {
fn from(mcf: MissingClarificationFile<'a>) -> Self {
diag(
Diagnostic::new(Severity::Warning)
.with_message("unable to locate specified license file")
.with_labels(vec![Label::secondary(mcf.cfg_file_id, mcf.expected.span)]),
Code::MissingClarificationFile,
)
}
}
pub(crate) struct ParseError {
pub(crate) span: std::ops::Range<usize>,
pub(crate) file_id: crate::diag::FileId,
pub(crate) error: spdx::ParseError,
}
impl From<ParseError> for Diag {
fn from(pe: ParseError) -> Self {
let span = pe.span.start + pe.error.span.start..pe.span.start + pe.error.span.end;
diag(
Diagnostic::new(Severity::Warning)
.with_message("error parsing SPDX license expression")
.with_labels(vec![
Label::secondary(pe.file_id, span).with_message(pe.error.reason),
]),
Code::ParseError,
)
}
}
pub(crate) struct EmptyLicenseField {
pub(crate) span: std::ops::Range<usize>,
pub(crate) file_id: crate::diag::FileId,
}
impl From<EmptyLicenseField> for Diag {
fn from(value: EmptyLicenseField) -> Self {
diag(
Diagnostic::warning()
.with_message("license field was present but empty")
.with_label(
Label::secondary(value.file_id, value.span).with_message("empty field"),
),
Code::EmptyLicenseField,
)
}
}
pub(crate) struct NoLicenseField<'k>(pub(crate) &'k Krate);
impl From<NoLicenseField<'_>> for Diag {
fn from(value: NoLicenseField<'_>) -> Self {
diag(
Diagnostic::warning().with_message(format!(
"license expression was not specified in manifest for crate '{}'",
value.0
)),
Code::NoLicenseField,
)
}
}