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}