1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use std::collections::HashMap;
5 
6 use failure::{format_err, Error};
7 use serde::{Deserialize, Serialize};
8 
9 use crate::{license::LicenseType, license::TextData};
10 
11 #[derive(Serialize, Deserialize)]
12 pub(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 askalono::{Store, TextData};
32 ///
33 /// # fn main() -> Result<(), Box<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)]
40 pub struct Store {
41     pub(crate) licenses: HashMap<String, LicenseEntry>,
42 }
43 
44 impl LicenseEntry {
new(original: TextData) -> LicenseEntry45     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 
55 impl Store {
56     /// Create a new `Store`.
57     ///
58     /// More often, you probably want to use `from_cache` instead of creating
59     /// an empty store.
new() -> Store60     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.
len(&self) -> usize70     pub fn len(&self) -> usize {
71         self.licenses.len()
72     }
73 
74     /// Check if the store is empty.
is_empty(&self) -> bool75     pub fn is_empty(&self) -> bool {
76         self.licenses.is_empty()
77     }
78 
79     /// Get all licenses by name via iterator.
licenses<'a>(&'a self) -> impl Iterator<Item = &String> + 'a80     pub fn licenses<'a>(&'a self) -> impl Iterator<Item = &String> + 'a {
81         self.licenses.keys()
82     }
83 
84     /// Get a license's standard TextData by name.
get_original(&self, name: &str) -> Option<&TextData>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.
add_license(&mut self, name: String, data: TextData)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.
add_variant( &mut self, name: &str, variant: LicenseType, data: TextData, ) -> Result<(), Error>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.
aliases(&self, name: &str) -> Result<&Vec<String>, Error>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.
set_aliases(&mut self, name: &str, aliases: Vec<String>) -> Result<(), Error>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 }
146