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 }