1 module postgresql.utils; 2 3 struct URI 4 { 5 string schema; 6 string host; 7 ushort port; 8 string username; 9 string password; 10 string database; 11 string[string] options; 12 } 13 14 bool parseURI(in string src, ref URI uri, ref string error) @safe // TODO: nothrow 15 { 16 import std..string: indexOf, split; 17 import std.conv: to; 18 19 20 void userInfo(string s) 21 { 22 auto i = s.indexOf(':'); 23 if ( i >= 0 ) 24 { 25 uri.username = s[0 .. i]; 26 uri.password = s[i + 1 .. $]; 27 } 28 else 29 { 30 uri.username = s; 31 } 32 } 33 34 bool resource(string s) 35 { 36 auto i = s.indexOf(':'); 37 if (i >= 0 ) 38 { 39 uri.host = s[0 .. i]; 40 if (s[i + 1 .. $].length == 0) 41 { 42 error = "URI: Empty port part"; 43 return false; 44 } 45 46 // FIXME: 47 uri.port = to!ushort(s[i + 1 .. $]); 48 } 49 else 50 { 51 uri.host = s; 52 } 53 return true; 54 } 55 56 bool authority(string s) 57 { 58 auto i = s.indexOf('@'); 59 if (i >= 0 ) { 60 61 userInfo(s[0 .. i]); 62 63 return resource(s[i + 1 .. $]); 64 } 65 return resource(s); 66 } 67 68 void queryString(string s) 69 { 70 foreach (pairs; s.split("&")) 71 { 72 auto kv = pairs.split("="); 73 74 if (kv.length && kv[0].length) 75 { 76 uri.options[kv[0]] = kv.length > 1 ? kv[1] : ""; 77 } 78 } 79 } 80 81 auto rest = src[]; 82 83 if (rest.length == 0) 84 { 85 error = "URI: Source string is empty"; 86 return false; 87 } 88 89 auto idx = rest.indexOf("://"); 90 91 if (idx < 0) 92 { 93 error = "URI: Invalid format"; 94 return false; 95 } 96 97 uri.schema = rest[0 .. idx]; 98 99 rest = rest[idx + 3 .. $]; 100 101 idx = rest.indexOf("/"); 102 103 if (idx < 0) 104 { 105 error = "URI: Invalid format"; 106 return false; 107 } 108 109 if (!authority(rest[0 .. idx])) 110 { 111 return false; 112 } 113 114 rest = rest[idx + 1 .. $]; 115 116 117 // Ignore fragments 118 idx = src.indexOf('#'); 119 if( idx >= 0 ) 120 { 121 rest = rest[0 .. idx]; 122 } 123 124 idx = rest.indexOf('?'); 125 126 if( idx >= 0 ) 127 { 128 queryString(rest[idx + 1 .. $]); 129 130 rest = rest[0 .. idx]; 131 } 132 133 uri.database = rest; 134 135 return true; 136 } 137 /// 138 unittest 139 { 140 URI uri; 141 string error; 142 import std.stdio; 143 144 parseURI("postgresql://username:password@hostname:5432/database?sslmode=disable&connect_timeout=20", uri, error); 145 146 writeln(uri); 147 writeln(error); 148 }