1 /**
2 * Copyright © DiamondMVC 2019
3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module diamond.core.filterservice;
7 
8 private
9 {
10   /// The entries that can be searched.
11   __gshared FilterEntry[] _entries;
12 
13   /// A filter entry.
14   final class FilterEntry
15   {
16     /// The title.
17     string title;
18     /// The url.
19     string url;
20     /// The keywords.
21     string[] keywords;
22     /// The keyword replacement.
23     string keywordReplacement;
24 
25     final:
26     /**
27     * Creates a new filter entry.
28     * Params:
29     *   title = The title.
30     *   url = The url.
31     *   keywords = The keywords.
32     *   keywordReplacement = The keyword replacement.
33     */
34     this(string title, string url, string[] keywords, string keywordReplacement)
35     {
36       this.title = title;
37       this.url = url;
38       this.keywords = keywords;
39       this.keywordReplacement = keywordReplacement;
40     }
41   }
42 }
43 
44 /// A filter result.
45 final class FilterResult
46 {
47   private:
48   /// The title.
49   string _title;
50   /// The url.
51   string _url;
52 
53   /**
54   * Creates a new filter result.
55   * Params:
56   *   title = The title of the result.
57   *   url = The url of the result.
58   */
59   this(string title, string url)
60   {
61     _title = title;
62     _url = url;
63   }
64 
65   public:
66   final:
67   /// Gets the title.
68   string title() { return _title; }
69 
70   /// Gets the url.
71   string url() { return _url; }
72 }
73 
74 /**
75 * Adds a search filter.
76 * Params:
77 *   title = The title of the result.
78 *   url = The url of the result.
79 *   keywords = The keywords to partially match when searching for the result.
80 *   keywordReplacement = A string to replace in the url with the keywords searched.
81 */
82 void addSearchFilter(string title, string url, string[] keywords, string keywordReplacement = null)
83 {
84   import diamond.errors.checks : enforce;
85 
86   enforce(title && title.length, "Missing title.");
87   enforce(url && url.length, "Missing url.");
88   enforce(keywords && keywords.length, "Missing keywords.");
89 
90   _entries ~= new FilterEntry(title, url, keywords, keywordReplacement);
91 }
92 
93 /**
94 * Gets all filtered results based on a set of keywords.
95 * Params:
96 *   searchKeywords = The keywords to search for.
97 * Returns:
98 *   An array of the filtered results that matches the keywords partially.
99 */
100 FilterResult[] search(string[] searchKeywords)
101 {
102   import std.algorithm : canFind;
103   import std.array : join, replace;
104   
105   FilterResult[] results;
106 
107   if (!_entries)
108   {
109     return results;
110   }
111 
112   foreach (entry; _entries)
113   {
114     foreach (searchKeyword; searchKeywords)
115     {
116       bool foundKeyword;
117 
118       foreach (keyword; entry.keywords)
119       {
120         if (keyword.canFind(searchKeyword))
121         {
122           string url = entry.url;
123 
124           if (entry.keywordReplacement && entry.keywordReplacement.length)
125           {
126             url = url.replace(entry.keywordReplacement, searchKeywords.join(","));
127           }
128 
129           results ~= new FilterResult(entry.title, url);
130           foundKeyword = true;
131           break;
132         }
133       }
134 
135       if (foundKeyword)
136       {
137         break;
138       }
139     }
140   }
141 
142   return results;
143 }