1 /**
2 * Parsing mime/globs and mime/globs2 files.
3 * Authors:
4 * $(LINK2 https://github.com/FreeSlave, Roman Chistokhodov)
5 * License:
6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
7 * Copyright:
8 * Roman Chistokhodov, 2015-2016
9 */
10
11 module mime.files.globs;
12
13 public import mime.files.common;
14
15 private {
16 import mime.common : defaultGlobWeight, isNoGlobs;
17
18 import std.algorithm;
19 import std.conv : parse;
20 import std.exception;
21 import std.range;
22 import std.traits;
23 import std.typecons;
24 }
25
26 ///Represents one line in globs or globs2 file.
27 alias Tuple!(uint, "weight", string, "mimeType", string, "pattern", bool, "caseSensitive") GlobLine;
28
29 /**
30 * Parse mime/globs file by line ignoring empty lines and comments.
31 * Returns:
32 * Range of $(D GlobLine) tuples.
33 * Throws:
34 * $(D mime.files.common.MimeFileException) on parsing error.
35 */
36 auto globsFileReader(Range)(Range byLine) if(isInputRange!Range && is(ElementType!Range : string))
37 {
38 return byLine.filter!(lineFilter).map!(function(string line) {
39 auto splitted = line.splitter(':');
40 string mimeType, pattern;
41
42 if (!splitted.empty) {
43 mimeType = splitted.front;
44 splitted.popFront();
45 if (!splitted.empty) {
46 pattern = splitted.front;
47 splitted.popFront();
48 } else {
49 throw new MimeFileException("Malformed globs file: mime type and pattern must be presented", line);
50 }
51 }
52
53 if (!mimeType.length || !pattern.length) {
54 throw new MimeFileException("Malformed globs file: the line has wrong format", line);
55 }
56
57 return GlobLine(isNoGlobs(pattern) ? 0 : defaultGlobWeight, mimeType, pattern, false);
58 });
59 }
60
61 ///
62 unittest
63 {
64 string[] lines = ["#comment", "text/x-c++src:*.cpp", "text/x-csrc:*.c"];
65 auto expected = [GlobLine(defaultGlobWeight, "text/x-c++src", "*.cpp", false), GlobLine(defaultGlobWeight, "text/x-csrc", "*.c", false)];
66 assert(equal(globsFileReader(lines), expected));
67 assert(equal(globsFileReader(["text/plain:__NOGLOBS__"]), [GlobLine(0, "text/plain", "__NOGLOBS__", false)]));
68
69 assertThrown!MimeFileException(globsFileReader(["#comment", "text/plain:*.txt", "nocolon"]).array, "must throw");
70 }
71
72 /**
73 * Parse mime/globs2 file by line ignoring empty lines and comments.
74 * Returns:
75 * Range of $(D GlobLine) tuples.
76 * Throws:
77 * $(D mime.files.common.MimeFileException) on parsing error.
78 */
79 auto globs2FileReader(Range)(Range byLine) if(isInputRange!Range && is(ElementType!Range : string))
80 {
81 return byLine.filter!(lineFilter).map!(function(string line) {
82 auto splitted = line.splitter(':');
83 string weightStr, mimeType, pattern, optionsStr;
84
85 if (!splitted.empty) {
86 weightStr = splitted.front;
87 splitted.popFront();
88 if (!splitted.empty) {
89 mimeType = splitted.front;
90 splitted.popFront();
91 if (!splitted.empty) {
92 pattern = splitted.front;
93 splitted.popFront();
94 if (!splitted.empty) {
95 optionsStr = splitted.front;
96 splitted.popFront();
97 }
98 }
99 }
100 }
101
102 if (!weightStr.length || !mimeType.length || !pattern.length) {
103 throw new MimeFileException("Malformed globs2 file: the line has wrong format", line);
104 }
105
106 uint weight;
107 try {
108 weight = parse!uint(weightStr);
109 } catch(Exception e) {
110 throw new MimeFileException(e.msg, line, e.file, e.line, e.next);
111 }
112
113 auto flags = optionsStr.splitter(','); //The fourth field contains a list of comma-separated flags
114 bool cs = !flags.empty && flags.front == "cs";
115 return GlobLine(weight, mimeType, pattern, cs);
116 });
117 }
118
119 ///
120 unittest
121 {
122 string[] lines = [
123 "#comment",
124 "50:text/x-c++src:*.cpp",
125 "60:text/x-c++src:*.C:cs",
126 "50:text/x-csrc:*.c:cs"
127 ];
128
129 auto expected = [GlobLine(50, "text/x-c++src", "*.cpp", false), GlobLine(60, "text/x-c++src", "*.C", true), GlobLine(50, "text/x-csrc", "*.c", true)];
130 assert(equal(globs2FileReader(lines), expected));
131
132 assertThrown!MimeFileException(globs2FileReader(["notanumber:text/plain:*.txt"]).array, "must throw");
133
134 MimeFileException mfe;
135 try {
136 globs2FileReader(["notanumber:text/nopattern"]).array;
137 } catch(MimeFileException e) {
138 mfe = e;
139 assert(mfe.lineString == "notanumber:text/nopattern");
140 }
141 assert(mfe, "must throw");
142 }