1 
2 //          Copyright Ferdinand Majerech 2011.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 ///Exceptions thrown by D:YAML and _exception related code.
8 module dyaml.exception;
9 
10 import std.algorithm;
11 import std.array;
12 import std.conv;
13 import std.exception;
14 import std.format;
15 import std.range;
16 import std.string;
17 import std.typecons;
18 
19 
20 /// Base class for all exceptions thrown by D:YAML.
21 class YAMLException : Exception
22 {
23     mixin basicExceptionCtors;
24 }
25 
26 /// Position in a YAML stream, used for error messages.
27 struct Mark
28 {
29     /// File name.
30     string name = "<unknown>";
31     /// Line number.
32     ushort line;
33     /// Column number.
34     ushort column;
35 
36     public:
37         /// Construct a Mark with specified line and column in the file.
38         this(string name, const uint line, const uint column) @safe pure nothrow @nogc
39         {
40             this.name = name;
41             this.line = cast(ushort)min(ushort.max, line);
42             // This *will* overflow on extremely wide files but saves CPU time
43             // (mark ctor takes ~5% of time)
44             this.column = cast(ushort)column;
45         }
46 
47         /// Get a string representation of the mark.
48         void toString(W)(ref W writer) const scope
49         {
50             // Line/column numbers start at zero internally, make them start at 1.
51             void writeClamped(ushort v)
52             {
53                 writer.formattedWrite!"%s"(v + 1);
54                 if (v == ushort.max)
55                 {
56                     put(writer, "or higher");
57                 }
58             }
59             put(writer, name);
60             put(writer, ":");
61             writeClamped(line);
62             put(writer, ",");
63             writeClamped(column);
64         }
65 }
66 
67 /// Base class of YAML exceptions with marked positions of the problem.
68 abstract class MarkedYAMLException : YAMLException
69 {
70     /// Position of the error.
71     Mark mark;
72     /// Additional position information, usually the start of a token or scalar
73     Nullable!Mark mark2;
74     /// A label for the extra information
75     string mark2Label;
76 
77     // Construct a MarkedYAMLException with two marks
78     this(string context, const Mark mark, string mark2Label, const Nullable!Mark mark2,
79          string file = __FILE__, size_t line = __LINE__) @safe pure nothrow
80     {
81         super(context, file, line);
82         this.mark = mark;
83         this.mark2 = mark2;
84         this.mark2Label = mark2Label;
85     }
86 
87     // Construct a MarkedYAMLException with specified problem.
88     this(string msg, const Mark mark,
89          string file = __FILE__, size_t line = __LINE__)
90         @safe pure nothrow
91     {
92         super(msg, file, line);
93         this.mark = mark;
94     }
95 
96     /// Custom toString to add context without requiring allocation up-front
97     void toString(W)(ref W sink) const
98     {
99         sink.formattedWrite!"%s@%s(%s): "(typeid(this).name, file, line);
100         put(sink, msg);
101         put(sink, "\n");
102         mark.toString(sink);
103         if (!mark2.isNull)
104         {
105             put(sink, "\n");
106             put(sink, mark2Label);
107             put(sink, ":");
108             mark2.get.toString(sink);
109         }
110         put(sink, "\n");
111         put(sink, info.toString());
112     }
113     /// Ditto
114     override void toString(scope void delegate(in char[]) sink) const
115     {
116         toString!(typeof(sink))(sink);
117     }
118     /// An override of message
119     override const(char)[] message() const @safe nothrow
120     {
121         if (mark2.isNull)
122         {
123             return assertNotThrown(text(msg, "\n", mark));
124         }
125         else
126         {
127             return assertNotThrown(text(msg, "\n", mark, "\n", mark2Label, ": ", mark2.get));
128         }
129     }
130 }
131 
132 /// Exception thrown on composer errors.
133 class ComposerException : MarkedYAMLException
134 {
135     mixin MarkedExceptionCtors;
136 }
137 
138 /// Exception thrown on constructor errors.
139 class ConstructorException : MarkedYAMLException
140 {
141     mixin MarkedExceptionCtors;
142 }
143 
144 /// Exception thrown on loader errors.
145 class LoaderException : MarkedYAMLException
146 {
147     mixin MarkedExceptionCtors;
148 }
149 
150 /// Exception thrown on node related errors.
151 class NodeException : MarkedYAMLException
152 {
153     mixin MarkedExceptionCtors;
154 }
155 
156 /// Exception thrown on parser errors.
157 class ParserException : MarkedYAMLException
158 {
159     mixin MarkedExceptionCtors;
160 }
161 
162 /// Exception thrown on Reader errors.
163 class ReaderException : MarkedYAMLException
164 {
165     mixin MarkedExceptionCtors;
166 }
167 
168 /// Exception thrown on Representer errors.
169 class RepresenterException : YAMLException
170 {
171     mixin basicExceptionCtors;
172 }
173 
174 /// Exception thrown on scanner errors.
175 class ScannerException : MarkedYAMLException
176 {
177     mixin MarkedExceptionCtors;
178 }
179 
180 private:
181 
182 /// Constructors of marked YAML exceptions are identical, so we use a mixin.
183 ///
184 /// See_Also: MarkedYAMLException
185 template MarkedExceptionCtors()
186 {
187     public:
188         this(string msg, const Mark mark1, string mark2Label,
189              const Mark mark2, string file = __FILE__, size_t line = __LINE__)
190             @safe pure nothrow
191         {
192             super(msg, mark1, mark2Label, Nullable!Mark(mark2), file, line);
193         }
194 
195         this(string msg, const Mark mark,
196              string file = __FILE__, size_t line = __LINE__)
197             @safe pure nothrow
198         {
199             super(msg, mark, file, line);
200         }
201         this(string msg, const Mark mark1, string mark2Label,
202              const Nullable!Mark mark2, string file = __FILE__, size_t line = __LINE__)
203             @safe pure nothrow
204         {
205             super(msg, mark1, mark2Label, mark2, file, line);
206         }
207 }