Skip to main content

provenant/output_schema/
license_match.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
7pub struct OutputMatch {
8    pub license_expression: String,
9    pub license_expression_spdx: String,
10    pub from_file: Option<String>,
11    pub start_line: u64,
12    pub end_line: u64,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub matcher: Option<String>,
15    pub score: f64,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub matched_length: Option<usize>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub match_coverage: Option<f64>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub rule_relevance: Option<u8>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub rule_identifier: Option<String>,
24    pub rule_url: Option<String>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub matched_text: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub matched_text_diagnostics: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none", default)]
30    pub referenced_filenames: Option<Vec<String>>,
31}
32
33impl From<&crate::models::Match> for OutputMatch {
34    fn from(value: &crate::models::Match) -> Self {
35        Self {
36            license_expression: value.license_expression.clone(),
37            license_expression_spdx: value.license_expression_spdx.clone(),
38            from_file: value.from_file.clone(),
39            start_line: value.start_line.get() as u64,
40            end_line: value.end_line.get() as u64,
41            matcher: Some(value.matcher.to_string()),
42            score: value.score.value(),
43            matched_length: value.matched_length,
44            match_coverage: value.match_coverage,
45            rule_relevance: value.rule_relevance,
46            rule_identifier: (!value.rule_identifier.is_empty())
47                .then(|| value.rule_identifier.clone()),
48            rule_url: value.rule_url.clone(),
49            matched_text: value.matched_text.clone(),
50            matched_text_diagnostics: value.matched_text_diagnostics.clone(),
51            referenced_filenames: value.referenced_filenames.clone(),
52        }
53    }
54}
55
56impl TryFrom<&OutputMatch> for crate::models::Match {
57    type Error = String;
58    fn try_from(value: &OutputMatch) -> Result<Self, Self::Error> {
59        use crate::models::LineNumber;
60        let start_line = LineNumber::new(value.start_line as usize)
61            .ok_or_else(|| format!("invalid start_line: {}", value.start_line))?;
62        let end_line = LineNumber::new(value.end_line as usize)
63            .ok_or_else(|| format!("invalid end_line: {}", value.end_line))?;
64        Ok(Self {
65            license_expression: value.license_expression.clone(),
66            license_expression_spdx: value.license_expression_spdx.clone(),
67            from_file: value.from_file.clone(),
68            start_line,
69            end_line,
70            matcher: value
71                .matcher
72                .as_deref()
73                .and_then(|s| s.parse().ok())
74                .unwrap_or(crate::license_detection::MatcherKind::Unknown),
75            score: crate::models::MatchScore::from_percentage(value.score),
76            matched_length: value.matched_length,
77            match_coverage: value.match_coverage,
78            rule_relevance: value.rule_relevance,
79            rule_identifier: value.rule_identifier.clone().unwrap_or_default(),
80            rule_url: value.rule_url.clone(),
81            matched_text: value.matched_text.clone(),
82            matched_text_diagnostics: value.matched_text_diagnostics.clone(),
83            referenced_filenames: value.referenced_filenames.clone(),
84        })
85    }
86}