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 e.port = port; 57 e.msg = msg; 58 e.file = file; 59 e.line = line; 60 return e; 61 } 62 63 import std.format; 64 65 private enum preallocated; 66 private enum prealloc_prefix = "prealloc"; 67 68 private mixin template throwSPEMix(E, string defaultMsg="") 69 if (is(E: SerialPortException)) 70 { 71 enum string name = E.stringof; 72 mixin(` 73 @preallocated private %1$s %2$s%1$s; 74 void throw%1$s(string port, string msg="%3$s", 75 string file=__FILE__, size_t line=__LINE__) @nogc 76 { throw %2$s%1$s.setFields(port, msg, file, line); } 77 `.format(name, prealloc_prefix, (defaultMsg.length ? defaultMsg : name)) 78 ); 79 } 80 81 private enum fmtSPSCEMsgFmt = "call '%s' (%s) failed: error %d"; 82 83 private string fmtSPSCEMsg(string port, string fnc, int err) @nogc 84 { 85 import core.stdc.stdio : sprintf; 86 import std.algorithm : min; 87 import core.stdc.string : memcpy, memset; 88 89 enum SZ = 256; 90 91 static char[SZ] port_buf; 92 static char[SZ] fnc_buf; 93 static char[SZ*3] buf; 94 95 memset(port_buf.ptr, 0, SZ); 96 memset(fnc_buf.ptr, 0, SZ); 97 memset(buf.ptr, 0, SZ*3); 98 memcpy(port_buf.ptr, port.ptr, min(port.length, SZ)); 99 memcpy(fnc_buf.ptr, fnc.ptr, min(fnc.length, SZ)); 100 auto n = sprintf(buf.ptr, fmtSPSCEMsgFmt, fnc_buf.ptr, port_buf.ptr, err); 101 return cast(string)buf[0..n]; 102 } 103 104 unittest 105 { 106 import std.format : format; 107 108 static auto fmtSPSCEMsgGC(string port, string fnc, int err) 109 { return format!fmtSPSCEMsgFmt(fnc, port, err); } 110 111 void test(string port, string fnc, int err) 112 { 113 auto trg = fmtSPSCEMsg(port, fnc, err); 114 auto tst = fmtSPSCEMsgGC(port, fnc, err); 115 if (trg != tst) assert(0, "not equals:\n%s\n%s".format(trg, tst)); 116 } 117 118 test("/dev/ttyUSB0", "open", 2); 119 test("/very/very/very/very/very/very/very/very/very/very/big/path/to/com/port/device/dev", 120 "veryVeryVeryVeryLongFunctionName12345678901234567890123456789012345678901234567890", 121 int.max); 122 test("", "", 0); 123 } 124 125 private mixin template throwSPSCEMix(E) 126 if (is(E: SysCallException)) 127 { 128 enum name = E.stringof; 129 mixin(` 130 @preallocated private %1$s %2$s%1$s; 131 void throw%1$s(string port, string fnc, int err, string msg="", 132 string file=__FILE__, size_t line=__LINE__) @nogc 133 { 134 if (msg.length == 0) 135 msg = fmtSPSCEMsg(port, fnc, err); 136 auto e = %2$s%1$s.setFields(port, msg, file, line); 137 e.fnc = fnc; 138 e.err = err; 139 throw e; 140 } 141 `.format(name, prealloc_prefix) 142 ); 143 } 144 145 static this() 146 { 147 import std.traits : getSymbolsByUDA; 148 static foreach (sym; getSymbolsByUDA!(mixin(__MODULE__), preallocated)) 149 sym = new typeof(sym); 150 } 151 152 mixin throwSPEMix!SerialPortException; 153 mixin throwSPEMix!PortClosedException; 154 mixin throwSPEMix!TimeoutException; 155 156 mixin throwSPSCEMix!SysCallException; 157 mixin throwSPSCEMix!ReadException; 158 mixin throwSPSCEMix!WriteException; 159 160 161 import serialport.types; 162 import core.stdc.stdio; 163 164 private char[1024] UEMPB; 165 166 @preallocated 167 private UnsupportedException preallocUnsupported; 168 169 void throwUnsupportedException(string port, int baudrate, 170 string file=__FILE__, size_t line=__LINE__) @nogc 171 { 172 auto ln = sprintf(UEMPB.ptr, "unsupported baudrate: %d", baudrate); 173 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 174 } 175 176 void throwUnsupportedException(string port, DataBits dbits, 177 string file=__FILE__, size_t line=__LINE__) @nogc 178 { 179 auto ln = sprintf(UEMPB.ptr, "unsupported data bits: %d", cast(int)dbits); 180 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 181 } 182 183 void throwUnsupportedException(string port, StopBits sbits, 184 string file=__FILE__, size_t line=__LINE__) @nogc 185 { 186 string str; 187 final switch (sbits) with (StopBits) 188 { 189 case one: str = "1\0"; break; 190 case two: str = "2\0"; break; 191 case onePointFive: str = "1.5\0"; break; 192 } 193 194 auto ln = sprintf(UEMPB.ptr, "unsupported stop bits: %s", str.ptr); 195 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 196 } 197 198 void throwUnsupportedException(string port, Parity parity, 199 string file=__FILE__, size_t line=__LINE__) @nogc 200 { 201 string str; 202 final switch (parity) with (Parity) 203 { 204 case none: str = "none\0"; break; 205 case even: str = "even\0"; break; 206 case odd: str = "odd\0"; break; 207 } 208 auto ln = sprintf(UEMPB.ptr, "unsupported parity: %s", str.ptr); 209 throw preallocUnsupported.setFields(port, cast(immutable)UEMPB[0..ln], file, line); 210 }