1 /*
2  * Copyright 2022 XXIV
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 module dogapi;
17 
18 import std.format: format;
19 import std.string: strip;
20 import std.json: JSONValue, parseJSON, JSONException;
21 import std.net.curl : byLineAsync, CurlException;
22 
23 class DogAPIException : Exception
24 {
25     this(string message, string file = __FILE__, size_t line = __LINE__)
26     {
27         super(message, file, line);
28     }
29 }
30 
31 private string getRequest(string endpoint)
32 {
33     auto response = byLineAsync(format("http://dog.ceo/api/%s", endpoint));
34     string content = "";
35     foreach (line; response)
36         content ~= line;
37     return content;
38 }
39 
40 /** 
41  * DISPLAY SINGLE RANDOM IMAGE FROM ALL DOGS COLLECTION
42  *
43  * Returns: random dog image
44  * Throws: DogAPIException on failure
45  */
46 string randomImage()
47 {
48     try
49     {
50         auto response = getRequest("breeds/image/random");
51         JSONValue data = parseJSON(response);
52         if (data["status"].str != "success")
53         {
54             throw new Exception(data["message"].str);
55         }
56         else
57         {
58             return data["message"].str;
59         }
60     }
61     catch (CurlException ex)
62     {
63         throw new DogAPIException(ex.msg);
64     }
65     catch (JSONException ex)
66     {
67         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
68     }
69     catch (Exception ex)
70     {
71         throw new DogAPIException(ex.msg);
72     }
73 }
74 
75 /** 
76  * DISPLAY MULTIPLE RANDOM IMAGES FROM ALL DOGS COLLECTION
77  *
78  * Params:
79  *   imagesNumber = number of images
80  * Returns: multiple random dog image `NOTE` ~ Max number returned is 50
81  * Throws: DogAPIException on failure
82  */
83 string[] multipleRandomImages(byte imagesNumber)
84 {
85     try
86     {
87         auto response = getRequest(format("breeds/image/random/%d", imagesNumber));
88         JSONValue data = parseJSON(response);
89         if (data["status"].str != "success")
90         {
91             throw new Exception(data["message"].str);
92         }
93         else
94         {
95             string[] list = [];
96             foreach (i; data["message"].array)
97             {
98                 list ~= i.str;
99             }
100             return list;
101         }
102     }
103     catch (CurlException ex)
104     {
105         throw new DogAPIException(ex.msg);
106     }
107     catch (JSONException ex)
108     {
109         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
110     }
111     catch (Exception ex)
112     {
113         throw new DogAPIException(ex.msg);
114     }
115 }
116 
117 /** 
118  * RANDOM IMAGE FROM A BREED COLLECTION
119  *
120  * Params:
121  *   breed = breed name
122  * Returns: random dog image from a breed, e.g. hound
123  * Throws: DogAPIException on failure
124  */
125 string randomImageByBreed(string breed)
126 {
127     try
128     {
129         auto response = getRequest(format("breed/%s/images/random", strip(breed)));
130         JSONValue data = parseJSON(response);
131         if (data["status"].str != "success")
132         {
133             throw new Exception(data["message"].str);
134         }
135         else
136         {
137             return data["message"].str;
138         }
139     }
140     catch (CurlException ex)
141     {
142         throw new DogAPIException(ex.msg);
143     }
144     catch (JSONException ex)
145     {
146         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
147     }
148     catch (Exception ex)
149     {
150         throw new DogAPIException(ex.msg);
151     }
152 }
153 
154 /** 
155  * MULTIPLE IMAGES FROM A BREED COLLECTION
156  *
157  * Params:
158  *   breed = breed name
159  *   imagesNumber = number of images
160  * Returns: multiple random dog image from a breed, e.g. hound
161  * Throws: DogAPIException on failure
162  */
163 string[] multipleRandomImagesByBreed(string breed, long imagesNumber)
164 {
165     try
166     {
167         auto response = getRequest(format("breed/%s/images/random/%d", strip(breed), imagesNumber));
168         JSONValue data = parseJSON(response);
169         if (data["status"].str != "success")
170         {
171             throw new Exception(data["message"].str);
172         }
173         else
174         {
175             string[] list = [];
176             foreach (i; data["message"].array)
177             {
178                 list ~= i.str;
179             }
180             return list;
181         }
182     }
183     catch (CurlException ex)
184     {
185         throw new DogAPIException(ex.msg);
186     }
187     catch (JSONException ex)
188     {
189         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
190     }
191     catch (Exception ex)
192     {
193         throw new DogAPIException(ex.msg);
194     }
195 }
196 
197 /** 
198  * ALL IMAGES FROM A BREED COLLECTION
199  *
200  * Params:
201  *   breed = breed name
202  * Returns: an array of all the images from a breed, e.g. hound
203  * Throws: DogAPIException on failure
204  */
205 string[] imagesByBreed(string breed)
206 {
207     try
208     {
209         auto response = getRequest(format("breed/%s/images", strip(breed)));
210         JSONValue data = parseJSON(response);
211         if (data["status"].str != "success")
212         {
213             throw new Exception(data["message"].str);
214         }
215         else
216         {
217             string[] list = [];
218             foreach (i; data["message"].array)
219             {
220                 list ~= i.str;
221             }
222             return list;
223         }
224     }
225     catch (CurlException ex)
226     {
227         throw new DogAPIException(ex.msg);
228     }
229     catch (JSONException ex)
230     {
231         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
232     }
233     catch (Exception ex)
234     {
235         throw new DogAPIException(ex.msg);
236     }
237 }
238 
239 /** 
240  * SINGLE RANDOM IMAGE FROM A SUB BREED COLLECTION
241  *
242  * Params:
243  *   breed = breed name
244  *   subBreed = sub_breed name
245  * Returns: random dog image from a sub-breed, e.g. Afghan Hound
246  * Throws: DogAPIException on failure
247  */
248 string randomImageBySubBreed(string breed, string subBreed)
249 {
250     try
251     {
252         auto response = getRequest(format("breed/%s/%s/images/random", strip(breed), strip(subBreed)));
253         JSONValue data = parseJSON(response);
254         if (data["status"].str != "success")
255         {
256             throw new Exception(data["message"].str);
257         }
258         else
259         {
260             return data["message"].str;
261         }
262     }
263     catch (CurlException ex)
264     {
265         throw new DogAPIException(ex.msg);
266     }
267     catch (JSONException ex)
268     {
269         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
270     }
271     catch (Exception ex)
272     {
273         throw new DogAPIException(ex.msg);
274     }
275 }
276 
277 /** 
278  * MULTIPLE IMAGES FROM A SUB-BREED COLLECTION
279  *
280  * Params:
281  *   breed = breed name
282  *   subBreed = sub_breed name
283  *   imagesNumber = number of images
284  * Returns: multiple random dog images from a sub-breed, e.g. Afghan Hound
285  * Throws: DogAPIException on failure
286  */
287 string[] multipleRandomImagesBySubBreed(string breed, string subBreed, long imagesNumber)
288 {
289     try
290     {
291         auto response = getRequest(format("breed/%s/%s/images/random/%d", strip(breed), strip(subBreed), imagesNumber));
292         JSONValue data = parseJSON(response);
293         if (data["status"].str != "success")
294         {
295             throw new Exception(data["message"].str);
296         }
297         else
298         {
299             string[] list = [];
300             foreach (i; data["message"].array)
301             {
302                 list ~= i.str;
303             }
304             return list;
305         }
306     }
307     catch (CurlException ex)
308     {
309         throw new DogAPIException(ex.msg);
310     }
311     catch (JSONException ex)
312     {
313         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
314     }
315     catch (Exception ex)
316     {
317         throw new DogAPIException(ex.msg);
318     }
319 }
320 
321 /** 
322  * LIST ALL SUB-BREED IMAGES
323  *
324  * Params:
325  *   breed = breed name
326  *   subBreed = sub_breed name
327  * Returns: an array of all the images from the sub-breed
328  * Throws: DogAPIException on failure
329  */
330 string[] imagesBySubBreed(string breed, string subBreed)
331 {
332     try
333     {
334         auto response = getRequest(format("breed/%s/%s/images", strip(breed), strip(subBreed)));
335         JSONValue data = parseJSON(response);
336         if (data["status"].str != "success")
337         {
338             throw new Exception(data["message"].str);
339         }
340         else
341         {
342             string[] list = [];
343             foreach (i; data["message"].array)
344             {
345                 list ~= i.str;
346             }
347             return list;
348         }
349     }
350     catch (CurlException ex)
351     {
352         throw new DogAPIException(ex.msg);
353     }
354     catch (JSONException ex)
355     {
356         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
357     }
358     catch (Exception ex)
359     {
360         throw new DogAPIException(ex.msg);
361     }
362 }
363 
364 /** 
365  * LIST ALL BREEDS
366  *
367  * Returns: associative array of all the breeds as keys and sub-breeds as values if it has
368  * Throws: DogAPIException on failure
369  */
370 string[][string] breedsList()
371 {
372     try
373     {
374         auto response = getRequest("breeds/list/all");
375         JSONValue data = parseJSON(response);
376         if (data["status"].str != "success")
377         {
378             throw new Exception(data["message"].str);
379         }
380         else
381         {
382             string[][string] list;
383             foreach (k, v; data["message"].object)
384             {
385                 string[] val = [];
386                 foreach (value; v.array)
387                     val ~= value.str;
388                 list[k] = val;
389             }
390             return list;
391         }
392     }
393     catch (CurlException ex)
394     {
395         throw new DogAPIException(ex.msg);
396     }
397     catch (JSONException ex)
398     {
399         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
400     }
401     catch (Exception ex)
402     {
403         throw new DogAPIException(ex.msg);
404     }
405 }
406 
407 /** 
408  * LIST ALL SUB-BREEDS
409  *
410  * Params:
411  *   breed = breed name
412  * Returns: an array of all the sub-breeds from a breed if it has sub-breeds
413  * Throws: DogAPIException on failure
414  */
415 string[] subBreedsList(string breed)
416 {
417     try
418     {
419         auto response = getRequest(format("breed/%s/list", strip(breed)));
420         JSONValue data = parseJSON(response);
421         if (data["status"].str != "success")
422         {
423             throw new Exception(data["message"].str);
424         }
425         else
426         {
427             string[] list = [];
428             foreach (i; data["message"].array)
429             {
430                 list ~= i.str;
431             }
432             if (list.length == 0)
433                 throw new DogAPIException("the breed does not have sub-breeds");
434             return list;
435         }
436     }
437     catch (CurlException ex)
438     {
439         throw new DogAPIException(ex.msg);
440     }
441     catch (JSONException ex)
442     {
443         throw new DogAPIException(format("Something went wrong while reading josn: %s", ex.msg));
444     }
445     catch (Exception ex)
446     {
447         throw new DogAPIException(ex.msg);
448     }
449 }