Skip to main content

weavatrix_clone/output/
json.rs

1use super::encode::quoted;
2use crate::model::{
3    CloneEvidence, CloneFamily, CloneKind, CloneLocation, ClonePair, CloneReport, CloneStatistics,
4};
5use std::fmt::Write;
6
7pub const JSON_SCHEMA: &str = "https://weavatrix.com/schemas/clone-report/v1";
8pub const JSON_SCHEMA_DOCUMENT: &str = include_str!("../../schemas/clone-report-v1.schema.json");
9
10#[must_use]
11pub fn to_json(report: &CloneReport) -> String {
12    let mut output = String::new();
13    output.push_str("{\"schema\":");
14    quoted(&mut output, JSON_SCHEMA);
15    output.push_str(",\"version\":1,\"pairs\":[");
16    let mut pairs = report.pairs.iter().collect::<Vec<_>>();
17    pairs.sort_unstable_by(|left, right| left.id.cmp(&right.id));
18    join(&mut output, pairs, pair);
19    output.push_str("],\"families\":[");
20    let mut families = report.families.iter().collect::<Vec<_>>();
21    families.sort_unstable_by(|left, right| left.id.cmp(&right.id));
22    join(&mut output, families, family);
23    output.push_str("],\"statistics\":");
24    statistics(&mut output, &report.statistics);
25    output.push('}');
26    output
27}
28
29fn pair(output: &mut String, pair: &ClonePair) {
30    output.push_str("{\"id\":");
31    quoted(output, &pair.id);
32    output.push_str(",\"kind\":");
33    quoted(
34        output,
35        match pair.kind {
36            CloneKind::Type1 => "type1",
37            CloneKind::Type2 => "type2",
38            CloneKind::Type3 => "type3",
39        },
40    );
41    let _ = write!(
42        output,
43        ",\"similarityPermille\":{},\"left\":",
44        pair.similarity.permille()
45    );
46    location(output, &pair.left);
47    output.push_str(",\"right\":");
48    location(output, &pair.right);
49    output.push_str(",\"evidence\":");
50    evidence(output, &pair.evidence);
51    output.push('}');
52}
53
54fn location(output: &mut String, location: &CloneLocation) {
55    output.push_str("{\"fragmentId\":");
56    quoted(output, &location.fragment_id);
57    output.push_str(",\"path\":");
58    quoted(output, &location.path);
59    let span = location.span;
60    let _ = write!(
61        output,
62        ",\"span\":{{\"startByte\":{},\"endByte\":{},\"startLine\":{},\"endLine\":{}}}}}",
63        span.start_byte, span.end_byte, span.start_line, span.end_line
64    );
65}
66
67fn evidence(output: &mut String, value: &CloneEvidence) {
68    let _ = write!(
69        output,
70        concat!(
71            "{{\"strictEqual\":{},\"renamedEqual\":{},",
72            "\"sharedFingerprints\":{},\"fingerprintJaccardPermille\":{},",
73            "\"fingerprintContainmentPermille\":{},\"editDistance\":{},",
74            "\"comparedTokens\":{}}}"
75        ),
76        value.strict_equal,
77        value.renamed_equal,
78        value.shared_fingerprints,
79        value.fingerprint_jaccard.permille(),
80        value.fingerprint_containment.permille(),
81        value.edit_distance,
82        value.compared_tokens
83    );
84}
85
86fn family(output: &mut String, family: &CloneFamily) {
87    output.push_str("{\"id\":");
88    quoted(output, &family.id);
89    output.push_str(",\"members\":[");
90    let mut members = family.members.iter().collect::<Vec<_>>();
91    members.sort_unstable();
92    join(output, members, location);
93    output.push_str("],\"pairIds\":[");
94    let mut pair_ids = family.pair_ids.iter().collect::<Vec<_>>();
95    pair_ids.sort_unstable();
96    join(output, pair_ids, |output, id| quoted(output, id));
97    output.push_str("]}");
98}
99
100fn statistics(output: &mut String, value: &CloneStatistics) {
101    let _ = write!(
102        output,
103        concat!(
104            "{{\"sourceFiles\":{},\"sourceTokens\":{},\"inputFragments\":{},",
105            "\"analyzedFragments\":{},\"skippedSmallFragments\":{},\"tokens\":{},",
106            "\"fingerprints\":{},\"candidatePairs\":{},\"exactBlockCandidates\":{},",
107            "\"verifiedPairs\":{},\"suppressedBuckets\":{},",
108            "\"suppressedExactBuckets\":{}}}"
109        ),
110        value.source_files,
111        value.source_tokens,
112        value.input_fragments,
113        value.analyzed_fragments,
114        value.skipped_small_fragments,
115        value.tokens,
116        value.fingerprints,
117        value.candidate_pairs,
118        value.exact_block_candidates,
119        value.verified_pairs,
120        value.suppressed_buckets,
121        value.suppressed_exact_buckets
122    );
123}
124
125fn join<T>(
126    output: &mut String,
127    values: impl IntoIterator<Item = T>,
128    mut render: impl FnMut(&mut String, T),
129) {
130    let mut first = true;
131    for value in values {
132        if !first {
133            output.push(',');
134        }
135        first = false;
136        render(output, value);
137    }
138}