1 module dcord.api.util; 2 3 import std.uri, 4 std.array, 5 std.format, 6 std.algorithm.iteration; 7 8 import vibe.http.client, 9 vibe.stream.operations; 10 11 import dcord.types, 12 dcord.util.errors; 13 14 /** 15 An error returned from the `APIClient` based on HTTP response code. 16 */ 17 class APIError : BaseError { 18 this(int code, string msg) { 19 super("[%s] %s", code, msg); 20 } 21 } 22 23 /** 24 Simple URL constructor to help building routes 25 */ 26 struct U { 27 private { 28 string _bucket; 29 string[] paths; 30 string[string] params; 31 } 32 33 @property string value() { 34 string url = this.paths.join("/"); 35 36 if (this.params.length) { 37 string[] parts; 38 foreach (ref key, ref value; this.params) { 39 parts ~= format("%s=%s", key, encodeComponent(value)); 40 } 41 url ~= "?" ~ parts.join("&"); 42 } 43 44 return "/" ~ url; 45 } 46 47 this(string url) { 48 this.paths ~= url; 49 } 50 51 this(string paramKey, string paramValue) { 52 this.params[paramKey] = paramValue; 53 } 54 55 string getBucket() { 56 return this._bucket; 57 } 58 59 U opCall(string url) { 60 this.paths ~= url; 61 return this; 62 } 63 64 U opCall(Snowflake s) { 65 this.paths ~= s.toString(); 66 return this; 67 } 68 69 U opCall(string paramKey, string paramValue) { 70 this.params[paramKey] = paramValue; 71 return this; 72 } 73 74 U bucket(string bucket) { 75 this._bucket = bucket; 76 return this; 77 } 78 79 U param(string name, string value) { 80 this.params[name] = value; 81 return this; 82 } 83 } 84 85 /** 86 Wrapper for HTTP REST Responses. 87 */ 88 class APIResponse { 89 private { 90 HTTPClientResponse res; 91 } 92 93 // The library caches content so HTTPClientResponse can be GC'd without pain 94 string content; 95 96 this(HTTPClientResponse res) { 97 this.res = res; 98 this.content = res.bodyReader.readAllUTF8(); 99 this.res.disconnect(); 100 } 101 102 /** 103 Raises an APIError exception if the request failed. 104 */ 105 APIResponse ok() { 106 if (100 < this.statusCode && this.statusCode < 400) { 107 return this; 108 } 109 110 throw new APIError(this.statusCode, this.content); 111 } 112 113 @property string contentType() { 114 return this.res.contentType; 115 } 116 117 @property int statusCode() { 118 return this.res.statusCode; 119 } 120 121 @property VibeJSON vibeJSON() { 122 return parseJsonString(this.content); 123 } 124 125 string header(string name, string def="") { 126 if (name in this.res.headers) { 127 return this.res.headers[name]; 128 } 129 130 return def; 131 } 132 } 133