1 /*
2  * Copyright (c) 2017-2019 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) 2017-2019 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/log.d, selery/log.d)
28  */
29 module selery.log;
30 
31 import std.algorithm : canFind;
32 import std.array : Appender;
33 import std.conv : to;
34 import std..string : indexOf;
35 import std.traits : EnumMembers;
36 
37 import sel.format : Format;
38 import sel.terminal : writeln;
39 
40 import terminal : Terminal;
41 
42 import selery.lang : LanguageManager, Translation, Translatable;
43 
44 /**
45  * Indicates a generic message. It can be a formatting code, a raw string
46  * or a translatable content.
47  */
48 struct Message {
49 
50 	enum : ubyte {
51 
52 		FORMAT = 1,
53 		TEXT = 2,
54 		TRANSLATION = 3
55 
56 	}
57 
58 	ubyte type;
59 
60 	union {
61 
62 		Format format;
63 		string text;
64 		Translation translation;
65 
66 	}
67 
68 	this(Format format) {
69 		this.type = FORMAT;
70 		this.format = format;
71 	}
72 
73 	this(string text) {
74 		this.type = TEXT;
75 		this.text = text;
76 	}
77 
78 	this(Translation translation) {
79 		this.type = TRANSLATION;
80 		this.translation = translation;
81 	}
82 
83 	/**
84 	 * Converts data into an array of messages.
85 	 */
86 	static Message[] convert(E...)(E args) {
87 		Message[] messages;
88 		foreach(arg ; args) {
89 			alias T  = typeof(arg);
90 			static if(is(T == Message) || is(T == Message[])) {
91 				messages ~= arg;
92 			} else static if(is(T == Format) || is(T == Translation)) {
93 				messages ~= Message(arg);
94 			} else static if(is(T == Translatable)) {
95 				messages ~= Message(Translation(arg));
96 			} else {
97 				messages ~= Message(to!string(arg));
98 			}
99 		}
100 		return messages;
101 	}
102 
103 }
104 
105 class Logger {
106 
107 	public Terminal terminal;
108 	private const LanguageManager lang;
109 
110 	public this(Terminal terminal, inout LanguageManager lang) {
111 		this.terminal = terminal;
112 		this.lang = cast(const)lang;
113 	}
114 	
115 	public void log(E...)(E args) {
116 		this.logMessage(Message.convert(args));
117 	}
118 	
119 	public void logWarning(E...)(E args) {
120 		this.log(Format.yellow, args);
121 	}
122 	
123 	public void logError(E...)(E args) {
124 		this.log(Format.red, args);
125 	}
126 
127 	public void logMessage(Message[] messages) {
128 		this.logImpl(messages);
129 	}
130 
131 	protected void logImpl(Message[] messages) {
132 		Appender!string text;
133 		foreach(message ; messages) {
134 			final switch(message.type) {
135 				case Message.FORMAT:
136 					text.put(cast(string)message.format);
137 					break;
138 				case Message.TEXT:
139 					text.put(message.text);
140 					break;
141 				case Message.TRANSLATION:
142 					text.put(this.lang.translate(message.translation.translatable.default_, message.translation.parameters));
143 					break;
144 			}
145 		}
146 		writeln(this.terminal, text.data);
147 	}
148 	
149 }