1 /// 2 module serialport.exception; 3 4 import std.conv : text; 5 6 import serialport.types; 7 8 /// 9 class ParseModeException : Exception 10 { 11 this(string msg, string file=__FILE__, size_t line=__LINE__) 12 @safe pure nothrow @nogc 13 { super(msg, file, line); } 14 } 15 16 /// General 17 class SerialPortException : Exception 18 { 19 string port; 20 private this() @safe pure nothrow @nogc { super(""); } 21 } 22 23 /// Unsupported config 24 class UnsupportedException : SerialPortException 25 { private this() @safe pure nothrow @nogc { super(); } } 26 27 /// 28 class PortClosedException : SerialPortException 29 { private this() @safe pure nothrow @nogc { super(); } } 30 31 /// 32 class TimeoutException : SerialPortException 33 { private this() @safe pure nothrow @nogc { super(); } } 34 35 /// 36 class SysCallException : SerialPortException 37 { 38 /// sys call name 39 string fnc; 40 /// errno or GetLastError 41 int err; 42 private this() @safe pure nothrow @nogc { super(); } 43 } 44 45 /// 46 class ReadException : SysCallException 47 { private this() @safe pure nothrow @nogc { super(); } } 48 49 /// 50 class WriteException : SysCallException 51 { private this() @safe pure nothrow @nogc { super(); } } 52 53 private E setFields(E: SerialPortException)(E e, string port, string msg, 54 string file, size_t line) 55 { 56 if (e is null) // assert(0) not omit on optimize by compiler 57 assert(0, "setField get null exception object"); 58 e.port = port; 59 e.msg = msg; 60 e.file = file; 61 e.line = line; 62 return e; 63 } 64 65 import std.format; 66 67 private enum preallocated; 68 private enum prealloc_prefix = "prealloc"; 69 70 private mixin template throwSPEMix(E, string defaultMsg="") 71 if (is(E: SerialPortException)) 72 { 73 enum string name = E.stringof; 74 mixin(` 75 @preallocated private %1$s %2$s%1$s; 76 void throw%1$s(string port, string msg="%3$s", 77 string file=__FILE__, size_t line=__LINE__) @nogc 78 { throw %2$s%1$s.setFields(port, msg, file, line); } 79 `.format(name, prealloc_prefix, (defaultMsg.length ? defaultMsg : name)) 80 ); 81 } 82 83 private enum fmtSPSCEMsgFmt = "call '%s' (%s) failed: error %d"; 84 85 private string fmtSPSCEMsg(string port, string fnc, int err) @nogc 86 { 87 import core.stdc.stdio : sprintf; 88 import std.algorithm : min; 89 import core.stdc.string : memcpy, memset; 90 91 enum SZ = 256; 92 93 static char[SZ] port_buf; 94 static char[SZ] fnc_buf; 95 static char[SZ*3] buf; 96 97 memset(port_buf.ptr, 0, SZ); 98 memset(fnc_buf.ptr, 0, SZ); 99 memset(buf.ptr, 0, SZ*3); 100 memcpy(port_buf.ptr, port.ptr, min(port.length, SZ)); 101 memcpy(fnc_buf.ptr, fnc.ptr, min(fnc.length, SZ)); 102 auto n = sprintf(buf.ptr, fmtSPSCEMsgFmt, fnc_buf.ptr, port_buf.ptr, err); 103 return cast(string)buf[0..n]; 104 } 105 106 unittest 107 { 108 import std.format : format; 109 110 static auto fmtSPSCEMsgGC(string port, string fnc, int err) 111 { return format!fmtSPSCEMsgFmt(fnc, port, err); } 112 113 void test(string port, string fnc, int err) 114 { 115 auto trg = fmtSPSCEMsg(port, fnc, err); 116 auto tst = fmtSPSCEMsgGC(port, fnc, err); 117 if (trg != tst) assert(0, "not equals:\n%s\n%s".format(trg, tst)); 118 } 119 120 test("/dev/ttyUSB0", "open", 2); 121 test("/very/very/very/very/very/very/very/very/very/very/big/path/to/com/port/device/dev", 122 "veryVeryVeryVeryLongFunctionName12345678901234567890123456789012345678901234567890", 123 int.max); 124 test("", "", 0); 125 } 126 127 private mixin template throwSPSCEMix(E) 128 if (is(E: SysCallException)) 129 { 130 enum name = E.stringof; 131 mixin(` 132 @preallocated private %1$s %2$s%1$s; 133 void throw%1$s(string port, string fnc, int err, string msg="", 134 string file=__FILE__, size_t line=__LINE__) @nogc 135 { 136 if (msg.length == 0) 137 msg = fmtSPSCEMsg(port, fnc, err); 138 auto e = %2$s%1$s.setFields(port, msg, file, line); 139 e.fnc = fnc; 140 e.err = err; 141 throw e; 142 } 143 `.format(name, prealloc_prefix) 144 ); 145 } 146 147 static this() 148 { 149 // can't use origin getSymbolsByUDA because 150 // https://issues.dlang.org/show_bug.cgi?id=20054 151 static if (__VERSION__ < 2088) 152 { 153 import std.traits : getSymbolsByUDA; 154 alias plist = getSymbolsByUDA!(mixin(__MODULE__), preallocated); 155 } 156 else 157 { 158 import std.meta : AliasSeq; 159 160 alias plist = AliasSeq!( 161 preallocSerialPortException, 162 preallocPortClosedException, 163 preallocTimeoutException, 164 preallocSysCallException, 165 preallocReadException, 166 preallocWriteException, 167 preallocUnsupported 168 ); 169 } 170 171 static foreach (sym; plist) sym = new typeof(sym); 172 } 173 174 mixin throwSPEMix!SerialPortException; 175 mixin throwSPEMix!PortClosedException; 176 mixin throwSPEMix!TimeoutException; 177 178 mixin throwSPSCEMix!SysCallException; 179 mixin throwSPSCEMix!ReadException; 180 mixin throwSPSCEMix!WriteException; 181 182 import serialport.types; 183 import core.stdc.stdio; 184 185 private char[1024] UEMPB; 186 187 @preallocated 188 private UnsupportedException preallocUnsupported; 189 190 void throwUnsupportedException(string port, int baudrate, 191 string file=__FILE__, size_t line=__LINE__) @nogc 192 { 193 auto ln = sprintf(UEMPB.ptr, "unsupported baudrate: %d", baudrate); 194 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 195 } 196 197 void throwUnsupportedException(string port, DataBits dbits, 198 string file=__FILE__, size_t line=__LINE__) @nogc 199 { 200 auto ln = sprintf(UEMPB.ptr, "unsupported data bits: %d", cast(int)dbits); 201 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 202 } 203 204 void throwUnsupportedException(string port, StopBits sbits, 205 string file=__FILE__, size_t line=__LINE__) @nogc 206 { 207 string str; 208 final switch (sbits) with (StopBits) 209 { 210 case one: str = "1\0"; break; 211 case two: str = "2\0"; break; 212 case onePointFive: str = "1.5\0"; break; 213 } 214 215 auto ln = sprintf(UEMPB.ptr, "unsupported stop bits: %s", str.ptr); 216 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 217 } 218 219 void throwUnsupportedException(string port, Parity parity, 220 string file=__FILE__, size_t line=__LINE__) @nogc 221 { 222 string str; 223 final switch (parity) with (Parity) 224 { 225 case none: str = "none\0"; break; 226 case even: str = "even\0"; break; 227 case odd: str = "odd\0"; break; 228 } 229 auto ln = sprintf(UEMPB.ptr, "unsupported parity: %s", str.ptr); 230 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 231 }