1 /* 2 * Copyright (c) 2018-2020 sel-project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 */ 23 /** 24 * Copyright: Copyright (c) 2018-2020 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/sel-util/chat/sel/chat.d, sel/chat.d) 28 */ 29 module sel.chat; 30 31 import std.array : Appender; 32 import std.json; 33 import std.traits : EnumMembers; 34 35 import sel.format : Format; 36 37 public @safe string parseChat(JSONValue json) { 38 Appender!string ret; 39 parseChatImpl(ret, json); 40 return ret.data; 41 } 42 43 private @trusted void parseChatImpl(ref Appender!string appender, JSONValue json) { 44 if(json.type == JSON_TYPE.OBJECT) { 45 auto translate = "translate" in json.object; 46 if(translate && translate.type == JSON_TYPE.STRING) { 47 // cannot translate without a translation table 48 appender.put(translate.str); 49 } else { 50 auto color = "color" in json.object; 51 string format; 52 if(color && color.type == JSON_TYPE.STRING) { 53 format = convertColor(color.str); 54 } 55 auto text = "text" in json.object; 56 if(text && text.type == JSON_TYPE.STRING) { 57 appender.put(format); 58 appender.put(text.str); 59 } 60 auto extra = "extra" in json.object; 61 if(extra && extra.type == JSON_TYPE.ARRAY) { 62 foreach(i, element; extra.array) { 63 parseChatImpl(appender, element); 64 if(i < extra.array.length - 1) { 65 appender.put(cast(string)Format.reset); 66 appender.put(format); 67 } 68 } 69 } 70 } 71 } else if(json.type == JSON_TYPE.STRING) { 72 appender.put(json.str); 73 } 74 } 75 76 private @safe string convertColor(string cname) { 77 switch(cname) { 78 foreach(format ; __traits(allMembers, Format)) { 79 static if(isColor!format) { 80 case snakeCase!format: return mixin("Format." ~ format); 81 } 82 } 83 default: return ""; 84 } 85 } 86 87 @safe unittest { 88 89 assert(convertColor("black") == Format.black); 90 assert(convertColor("dark_blue") == Format.darkBlue); 91 assert(convertColor("reset") == ""); 92 93 } 94 95 private enum isColor(string format) = isColorImpl(mixin("Format." ~ format)[2]); 96 97 private bool isColorImpl(char c) { 98 return c >= '0' && c <= '9' || c >= 'a' && c <= 'f'; 99 } 100 101 private enum snakeCase(string str) = snakeCaseImpl(str); 102 103 private string snakeCaseImpl(string str) { 104 string ret; 105 foreach(c ; str) { 106 if(c >= 'A' && c <= 'Z') ret ~= "_" ~ cast(char)(c + 32); 107 else ret ~= c; 108 } 109 return ret; 110 }