scancode_rust/askalono/store/base.rs
1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::HashMap;
5
6use anyhow::{format_err, Error};
7use serde::{Deserialize, Serialize};
8
9use crate::askalono::{license::LicenseType, license::TextData};
10
11#[derive(Serialize, Deserialize)]
12pub(crate) struct LicenseEntry {
13 pub original: TextData,
14 pub aliases: Vec<String>,
15 pub headers: Vec<TextData>,
16 pub alternates: Vec<TextData>,
17}
18
19/// A representation of a collection of known licenses.
20///
21/// This struct is generally what you want to start with if you're looking to
22/// match text against a database of licenses. Load a cache from disk using
23/// `from_cache`, then use the `analyze` function to determine what a text most
24/// closely matches.
25///
26/// # Examples
27///
28/// ```rust,should_panic
29/// # use std::fs::File;
30/// # use std::error::Error;
31/// use scancode_rust::askalono::{Store, TextData};
32///
33/// # fn main() -> Result<(), Box<dyn Error>> {
34/// let store = Store::from_cache(File::open("askalono-cache.bin.zstd")?)?;
35/// let result = store.analyze(&TextData::from("what's this"));
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Default, Serialize, Deserialize)]
40pub struct Store {
41 pub(crate) licenses: HashMap<String, LicenseEntry>,
42}
43
44impl LicenseEntry {
45 pub fn new(original: TextData) -> LicenseEntry {
46 LicenseEntry {
47 original,
48 aliases: Vec::new(),
49 alternates: Vec::new(),
50 headers: Vec::new(),
51 }
52 }
53}
54
55impl Store {
56 /// Create a new `Store`.
57 ///
58 /// More often, you probably want to use `from_cache` instead of creating
59 /// an empty store.
60 pub fn new() -> Store {
61 Store {
62 licenses: HashMap::new(),
63 }
64 }
65
66 /// Get the number of licenses in the store.
67 ///
68 /// This only counts licenses by name -- headers, aliases, and alternates
69 /// aren't included in the count.
70 pub fn len(&self) -> usize {
71 self.licenses.len()
72 }
73
74 /// Check if the store is empty.
75 pub fn is_empty(&self) -> bool {
76 self.licenses.is_empty()
77 }
78
79 /// Get all licenses by name via iterator.
80 pub fn licenses(&self) -> impl Iterator<Item = &String> {
81 self.licenses.keys()
82 }
83
84 /// Get a license's standard TextData by name.
85 pub fn get_original(&self, name: &str) -> Option<&TextData> {
86 Some(&self.licenses.get(name)?.original)
87 }
88
89 /// Add a single license to the store.
90 ///
91 /// If the license with the given name already existed, it and all of its
92 /// variants will be replaced.
93 pub fn add_license(&mut self, name: String, data: TextData) {
94 let entry = LicenseEntry::new(data);
95 self.licenses.insert(name, entry);
96 }
97
98 /// Add a variant (a header or alternate formatting) of a given license to
99 /// the store.
100 ///
101 /// The license must already exist. This function cannot be used to replace
102 /// the original/canonical text of the license.
103 pub fn add_variant(
104 &mut self,
105 name: &str,
106 variant: LicenseType,
107 data: TextData,
108 ) -> Result<(), Error> {
109 let entry = self
110 .licenses
111 .get_mut(name)
112 .ok_or_else(|| format_err!("license {} not present in store", name))?;
113 match variant {
114 LicenseType::Alternate => {
115 entry.alternates.push(data);
116 }
117 LicenseType::Header => {
118 entry.headers.push(data);
119 }
120 _ => {
121 return Err(format_err!("variant type not applicable for add_variant"));
122 }
123 };
124 Ok(())
125 }
126
127 /// Get the list of aliases for a given license.
128 pub fn aliases(&self, name: &str) -> Result<&Vec<String>, Error> {
129 let entry = self
130 .licenses
131 .get(name)
132 .ok_or_else(|| format_err!("license {} not present in store", name))?;
133 Ok(&entry.aliases)
134 }
135
136 /// Set the list of aliases for a given license.
137 pub fn set_aliases(&mut self, name: &str, aliases: Vec<String>) -> Result<(), Error> {
138 let entry = self
139 .licenses
140 .get_mut(name)
141 .ok_or_else(|| format_err!("license {} not present in store", name))?;
142 entry.aliases = aliases;
143 Ok(())
144 }
145}