1 module handlebars.properties; 2 3 import std.string; 4 import std.traits; 5 import std.conv; 6 7 version(unittest) { 8 import fluent.asserts; 9 } 10 11 /// 12 struct Properties { 13 struct Property { 14 string value; 15 bool isEvaluated; 16 17 this(string value, bool isEvaluated) { 18 this.value = value; 19 this.isEvaluated = isEvaluated; 20 } 21 22 this(string value) { 23 if(value == "") { 24 return; 25 } 26 27 if(value[0] == '"' || value[0] == '\'') { 28 this.value = value[1..$-1]; 29 this.isEvaluated = true; 30 return; 31 } 32 33 if(isNumeric(value)) { 34 this.value = value; 35 this.isEvaluated = true; 36 return; 37 } 38 39 if(value == "true" || value == "false") { 40 this.value = value; 41 this.isEvaluated = true; 42 return; 43 } 44 45 this.value = value; 46 this.isEvaluated = false; 47 } 48 49 T get(T)() if (is(T == string)) { 50 return value; 51 } 52 53 T get(T)() if (isBuiltinType!T && !is(T == string)) { 54 static if(__traits(compiles, this.value.to!T)) { 55 return this.value.to!T; 56 } else { 57 throw new Exception("Can't get `" ~ value ~ "` as `" ~ T.stringof ~ "`"); 58 } 59 } 60 61 /// 62 string toParam() { 63 if(isEvaluated) { 64 if(value == "true" || value == "false") { 65 return value; 66 } 67 68 return `"` ~ value ~ `"`; 69 } 70 71 return "controller." ~ value; 72 } 73 } 74 75 /// 76 Property[] list; 77 78 /// 79 Property[string] hash; 80 81 string localName; 82 string indexName; 83 string name; 84 85 this(string value) { 86 while(value.length > 0) { 87 auto pos = value.breakIndex; 88 auto item = value[0..pos]; 89 auto eqPos = item.indexOf("="); 90 91 if(eqPos == -1) { 92 list ~= Property(item); 93 } else { 94 auto key = item[0..eqPos]; 95 auto val = item[eqPos+1..$]; 96 97 hash[key] = Property(val); 98 } 99 100 value = value[pos..$].strip; 101 102 } 103 104 if(list.length == 3 && !list[0].isEvaluated && list[1].value == "as" && list[2].value[0] == '|') { 105 name = list[0].value; 106 auto localProperties = Properties(list[2].value.replace("|", "").strip); 107 localName = localProperties.list[0].value; 108 109 if(localProperties.list.length == 2) { 110 indexName = localProperties.list[1].value; 111 } 112 113 list = []; 114 } 115 } 116 } 117 118 /// parse a list of string properties 119 unittest { 120 Properties(`"a" "b"`).list.should.equal([ 121 Properties.Property("a", true), 122 Properties.Property("b", true)]); 123 } 124 125 /// parse a list of string with spaces properties 126 unittest { 127 Properties(`"a b c" "c d e"`).list.should.equal([ 128 Properties.Property("a b c", true), 129 Properties.Property("c d e", true)]); 130 } 131 132 /// parse a list of variables with spaces properties 133 unittest { 134 Properties(`a 1 c "c d e"`).list.should.equal([ 135 Properties.Property("a", false), 136 Properties.Property("1", true), 137 Properties.Property("c", false), 138 Properties.Property("c d e", true)]); 139 } 140 141 /// parse a list of attributes with spaces properties 142 unittest { 143 Properties(`a=1 b=value`).hash["a"] 144 .should.equal(Properties.Property("1", true)); 145 146 Properties(`a=1 b=value`).hash["b"] 147 .should.equal(Properties.Property("value", false)); 148 } 149 150 /// parse each properties 151 unittest { 152 Properties(`list as |item|`).name.should.equal("list"); 153 Properties(`list as |item|`).localName.should.equal("item"); 154 Properties(`list as |item|`).indexName.should.equal(""); 155 156 157 Properties(`list as | item index |`).name.should.equal("list"); 158 Properties(`list as | item index |`).localName.should.equal("item"); 159 Properties(`list as | item index |`).indexName.should.equal("index"); 160 } 161 162 /// 163 size_t breakIndex(string value) { 164 size_t pos = value.length; 165 166 if(value.length == 0) { 167 return 0; 168 } 169 170 if(value[0] == '"' || value[0] == '\'' || value[0] == '|') { 171 auto tmp = value[1..$]; 172 173 while(tmp.length > 0) { 174 auto endPos = tmp.indexOf(value[0]); 175 176 if(tmp.length > 1 && tmp[endPos - 1] != '\\') { 177 tmp = tmp[endPos+1..$]; 178 break; 179 } 180 181 tmp = tmp[endPos+1..$]; 182 } 183 184 return value.length - tmp.length; 185 } 186 187 foreach(token; [ " ", "\t", "\n" ]) { 188 auto tmp = value.indexOf(token) ; 189 190 if(tmp < pos) { 191 pos = tmp; 192 } 193 } 194 195 return pos; 196 } 197 198 /// break index with no breaks should return the length of the string 199 unittest { 200 "".breakIndex.should.equal(0); 201 "abc".breakIndex.should.equal(3); 202 } 203 204 /// break index with one break should return the position of the next item 205 unittest { 206 "a b".breakIndex.should.equal(1); 207 "a\tb".breakIndex.should.equal(1); 208 "a\nb".breakIndex.should.equal(1); 209 `"abc" "def"`.breakIndex.should.equal(5); 210 `"a c" "def"`.breakIndex.should.equal(5); 211 `'a c' "def"`.breakIndex.should.equal(5); 212 } 213 214 /// break index with escaped strings 215 unittest { 216 `"a\"c" "def"`.breakIndex.should.equal(6); 217 `'a\'c' "def"`.breakIndex.should.equal(6); 218 } 219 220 /// break index with bar string 221 unittest { 222 `| a b |`.breakIndex.should.equal(8); 223 }