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