1 module discord.api.util;
2 
3 import std.uri,
4        std.array,
5        std.format,
6        std.algorithm.iteration;
7 import vibe.http.client,
8        vibe.stream.operations;
9 import discord.types,
10        discord.util.errors;
11 
12 /**
13   Represents an error from the Discord API.
14 */
15 class APIError : BaseError {
16   this(int code, string msg) {
17     super("[%s] %s", code, msg);
18   }
19 }
20 
21 /**
22   URL constructor to build routes
23 */
24 struct U {
25   private {
26     string          _bucket;
27     string[]        paths;
28     string[string]  params;
29   }
30 
31   @property string value() {
32     string url = this.paths.join("/");
33 
34     if (this.params.length) {
35       string[] parts;
36       foreach (ref key, ref value; this.params) {
37         parts ~= format("%s=%s", key, encodeComponent(value));
38       }
39       url ~= "?" ~ parts.join("&");
40     }
41     return "/" ~ url;
42   }
43   this(string url) {
44     this.paths ~= url;
45   }
46   this(string paramKey, string paramValue) {
47     this.params[paramKey] = paramValue;
48   }
49   string getBucket() {
50     return this._bucket;
51   }
52   U opCall(string url) {
53     this.paths ~= url;
54     return this;
55   }
56   U opCall(Snowflake s) {
57     this.paths ~= s.toString();
58     return this;
59   }
60   U opCall(string paramKey, string paramValue) {
61     this.params[paramKey] = paramValue;
62     return this;
63   }
64   U bucket(string bucket) {
65     this._bucket = bucket;
66     return this;
67   }
68   U param(string name, string value) {
69     this.params[name] = value;
70     return this;
71   }
72 }
73 
74 /**
75   Represents an error response from Discord API.
76 */
77 class APIResponse {
78   private {
79     HTTPClientResponse res;
80   }
81   string content;
82   this(HTTPClientResponse res) {
83     this.res = res;
84     this.content = res.bodyReader.readAllUTF8();
85     this.res.disconnect();
86   }
87 
88   APIResponse ok() {
89     if (100 < this.statusCode && this.statusCode < 400) {
90       return this;
91     }
92     throw new APIError(this.statusCode, this.content);
93   }
94 
95   @property string contentType() {
96     return this.res.contentType;
97   }
98 
99   @property int statusCode() {
100     return this.res.statusCode;
101   }
102 
103   @property VibeJSON vibeJSON() {
104     return parseJsonString(this.content);
105   }
106 
107   string header(string name, string def="") {
108     if (name in this.res.headers) {
109       return this.res.headers[name];
110     }
111     return def;
112   }
113 }