1 /*
2  *Copyright (C) 2018 Laurent Tréguier
3  *
4  *This file is part of DLS.
5  *
6  *DLS is free software: you can redistribute it and/or modify
7  *it under the terms of the GNU General Public License as published by
8  *the Free Software Foundation, either version 3 of the License, or
9  *(at your option) any later version.
10  *
11  *DLS is distributed in the hope that it will be useful,
12  *but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *GNU General Public License for more details.
15  *
16  *You should have received a copy of the GNU General Public License
17  *along with DLS.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 module dls.util.uri;
22 
23 class Uri
24 {
25     import dls.protocol.definitions : DocumentUri;
26     import std.regex : regex;
27 
28     private static enum _reg = regex(`([\w-]+):(?://([\w.@]+(?::\d+)?))?([^\?#]+)(?:\?([\w=&]+))?(?:#([\w-]+))?`);
29     private string _uri;
30     private string _scheme;
31     private string _authority;
32     private string _path;
33     private string _query;
34     private string _fragment;
35 
36     @property string path() const
37     {
38         return _path;
39     }
40 
41     this(DocumentUri uri)
42     {
43         import std.regex : matchAll;
44         import std.uri : decodeComponent;
45 
46         _uri = decodeComponent(uri);
47         auto matches = matchAll(_uri, _reg);
48 
49         //dfmt off
50         _scheme     = matches.front[1];
51         _authority  = matches.front[2];
52         _path       = matches.front[3].normalized;
53         _query      = matches.front[4];
54         _fragment   = matches.front[5];
55         //dfmt on
56     }
57 
58     private this(string scheme, string authority, string path, string query, string fragment)
59     {
60         _uri = scheme ~ ':';
61 
62         if (authority !is null)
63         {
64             _uri ~= "//" ~ authority;
65         }
66 
67         _uri ~= path;
68         
69         if (query !is null)
70         {
71             _uri ~= '?' ~ query;
72         }
73         
74         if (fragment !is null)
75         {
76             _uri ~= '#' ~ fragment;
77         }
78 
79         //dfmt off
80         _scheme    = scheme;
81         _authority = authority;
82         _path      = path.normalized;
83         _query     = query;
84         _fragment  = fragment;
85         //dfmt on
86     }
87 
88     override string toString() const
89     {
90         return _uri;
91     }
92 
93     static Uri fromPath(string path)
94     {
95         import std.algorithm : startsWith;
96         import std.format : format;
97         import std..string : tr;
98         import std.uri : encode;
99 
100         immutable uriPath = path.tr(`\`, `/`);
101         return new Uri("file", "", (uriPath.startsWith('/') ? "" : "/") ~ uriPath, null, null);
102     }
103 
104     alias toString this;
105 }
106 
107 string normalized(const string path)
108 {
109     import std.array : array;
110     import std.path : asNormalizedPath;
111 
112     string res;
113 
114     version (Windows)
115     {
116         import std.algorithm : startsWith;
117         import std.path : driveName, stripDrive;
118         import std.uni : asUpperCase;
119         import std.utf : toUTF8;
120 
121         if (path.startsWith('/') || path.startsWith('\\'))
122         {
123             return path[1 .. $].normalized;
124         }
125 
126         res = driveName(path).asUpperCase().toUTF8() ~ stripDrive(path);
127     }
128     else
129     {
130         res = path;
131     }
132 
133     return asNormalizedPath(res).array;
134 }
135 
136 int filenameCmp(const Uri a, const Uri b)
137 {
138     import std.path : filenameCmp;
139 
140     return filenameCmp(a.path, b.path);
141 }
142 
143 bool sameFile(const Uri a, const Uri b)
144 {
145     return filenameCmp(a, b) == 0;
146 }