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 }