1 module des.math.util.accessstring;
2 
3 import std..string;
4 import std.algorithm;
5 import std.stdio;
6 
7 /// construct valid value access strings
8 string arrayAccessStringCtor( string sep1, string sep2, string[][] variants... ) pure
9 in
10 {
11     assert( sep1 != sep2 );
12     assert( variants.length > 0 );
13     auto l = variants[0].length;
14     assert( all!(a=>a.length == l)( variants ) );
15 }
16 body
17 {
18     string[] rr;
19     foreach( var; variants )
20         rr ~= var.join(sep1);
21     return rr.join(sep2);
22 }
23 
24 ///
25 unittest
26 {
27     enum s1 = " ", s2 = "|";
28     static assert( isCompatibleArrayAccessStrings( 2, arrayAccessStringCtor( s1, s2, ["x","y"], ["alpha","beta"] ), s1, s2 ) );
29 }
30 
31 /// compatible for creating access dispatches
32 pure bool isCompatibleArrayAccessStrings( size_t N, string str, string sep1="", string sep2="|" )
33 in { assert( sep1 != sep2 ); } body
34 {
35     auto strs = str.split(sep2);
36     foreach( s; strs )
37         if( !isCompatibleArrayAccessString(N,s,sep1) )
38             return false;
39 
40     string[] fa;
41     foreach( s; strs )
42         fa ~= s.split(sep1);
43 
44     foreach( ref v; fa ) v = strip(v);
45 
46     foreach( i, a; fa )
47         foreach( j, b; fa )
48             if( i != j && a == b ) return false;
49 
50     return true;
51 }
52 
53 
54 /// compatible for creating access dispatches
55 pure bool isCompatibleArrayAccessString( size_t N, string str, string sep="" )
56 { return N == getAccessFieldsCount(str,sep) && isArrayAccessString(str,sep); }
57 
58 ///
59 pure bool isArrayAccessString( in string as, in string sep="", bool allowDot=false )
60 {
61     if( as.length == 0 ) return false;
62     auto splt = as.split(sep);
63     foreach( i, val; splt )
64         if( !isValueAccessString(val,allowDot) || canFind(splt[0..i],val) )
65             return false;
66     return true;
67 }
68 
69 ///
70 pure size_t getAccessFieldsCount( string str, string sep )
71 { return str.split(sep).length; }
72 
73 ///
74 pure ptrdiff_t getIndex( string as, string arg, string sep1="", string sep2="|" )
75 in { assert( sep1 != sep2 ); } body
76 {
77     foreach( str; as.split(sep2) )
78         foreach( i, v; str.split(sep1) )
79             if( arg == v ) return i;
80     return -1;
81 }
82 
83 ///
84 pure bool oneOfAccess( string str, string arg, string sep="" )
85 {
86     auto splt = str.split(sep);
87     return canFind(splt,arg);
88 }
89 
90 ///
91 pure bool oneOfAccessAll( string str, string arg, string sep="" )
92 {
93     auto splt = arg.split("");
94     return all!(a=>oneOfAccess(str,a,sep))(splt);
95 }
96 
97 ///
98 pure bool oneOfAnyAccessAll( string str, string arg, string sep1="", string sep2="|" )
99 in { assert( sep1 != sep2 ); } body
100 {
101     foreach( s; str.split(sep2) )
102         if( oneOfAccessAll(s,arg,sep1) ) return true;
103     return false;
104 }
105 
106 /// check symbol count for access to field
107 pure bool isOneSymbolPerFieldForAnyAccessString( string str, string sep1="", string sep2="|" )
108 in { assert( sep1 != sep2 ); } body
109 {
110     foreach( s; str.split(sep2) )
111         if( isOneSymbolPerFieldAccessString(s,sep1) ) return true;
112     return false;
113 }
114 
115 /// check symbol count for access to field
116 pure bool isOneSymbolPerFieldAccessString( string str, string sep="" )
117 {
118     foreach( s; str.split(sep) )
119         if( s.length > 1 ) return false;
120     return true;
121 }
122 
123 /++
124 Using all functions
125 +/
126 unittest
127 {
128     static assert(  isValueAccessString( "hello" ) );
129     static assert(  isValueAccessString( "x" ) );
130     static assert(  isValueAccessString( "_ok" ) );
131     static assert(  isValueAccessString( "__ok" ) );
132     static assert(  isValueAccessString( "__o1k" ) );
133     static assert(  isValueAccessString( "_2o1k3" ) );
134     static assert( !isValueAccessString( "0__ok" ) );
135     static assert( !isValueAccessString( "__o-k" ) );
136     static assert(  isArrayAccessString( "xyz" ) );
137     static assert(  isArrayAccessString( "x|dx|y|dy", "|" ) );
138     static assert(  isCompatibleArrayAccessString( 4, "x|dx|y|dy", "|" ) );
139     static assert(  isCompatibleArrayAccessString( 3, "xyz" ) );
140     static assert( !isCompatibleArrayAccessString( 4, "xxxy" ) );
141     static assert( !isCompatibleArrayAccessString( 3, "xxx" ) );
142     static assert(  isCompatibleArrayAccessStrings( 3, "xyz" ) );
143     static assert(  isCompatibleArrayAccessStrings( 3, "x y z", " " ) );
144     static assert(  isCompatibleArrayAccessStrings( 2, "xy|uv" ) );
145     static assert(  isCompatibleArrayAccessStrings( 3, "abc|efg" ) );
146     static assert( !isCompatibleArrayAccessStrings( 3, "abc|afg" ) );
147     static assert( !isCompatibleArrayAccessStrings( 3, "xxy|uv" ) );
148     static assert(  isCompatibleArrayAccessStrings( 3, "x,y,z;u,v,t", ",", ";" ) );
149     static assert( getIndex( "x y z", "x", " " ) == 0 );
150     static assert( getIndex( "x y z", "y", " " ) == 1 );
151     static assert( getIndex( "x y z", "z", " " ) == 2 );
152     static assert( getIndex( "x|dx|y|dy", "dx", "|", ";" ) == 1 );
153     static assert( getIndex( "x|dx|y|dy", "1dx", "|", ";" ) == -1 );
154 
155     static assert( oneOfAccessAll("xyz","xy") );
156     static assert( oneOfAccessAll("xyz","yx") );
157     static assert( oneOfAccessAll("xyz","xxxxyxyyyz") );
158     static assert( oneOfAccessAll("x,y,z","xxxxyxyyyz",",") );
159     static assert( isOneSymbolPerFieldAccessString("xyz") );
160     static assert( isOneSymbolPerFieldAccessString("x,y,z",",") );
161     static assert( isOneSymbolPerFieldForAnyAccessString( "xy|uv", "", "|" ) );
162     static assert( isOneSymbolPerFieldForAnyAccessString( "near far|n f", " ", "|" ) );
163 
164     static assert( !isArrayAccessString("x.y.z","",false) );
165     static assert( !isArrayAccessString("x.y.z","",true) );
166     static assert( !isArrayAccessString("x.y.z"," ",false) );
167     static assert(  isArrayAccessString("x.y.z"," ",true) );
168     static assert(  isArrayAccessString("pos.x pos.y pos.z vel.x vel.y vel.z"," ",true) );
169 
170     static assert(  isArrayAccessString( "pos vel", " ", true ) );
171     static assert(  isArrayAccessString( "abcd", " ", true ) );
172     static assert(  isArrayAccessString( "a1 a2", " ", true ) );
173     static assert(  isArrayAccessString( "ok.no", " ", true ) );
174     auto fstr = "pos.x pos.y vel.x vel.y";
175     assert(  isArrayAccessString( fstr, " ", true ) );
176     assert( !isArrayAccessString( fstr[0 .. $-1], " ", true ) );
177     assert( !isArrayAccessString( "ok.1", " ", true ) );
178     assert( !isArrayAccessString( "1abcd", " ", true ) );
179     assert( !isArrayAccessString( "not 2ok", " ", true ) );
180 }
181 
182 pure
183 {
184 
185 bool isValueAccessString( in string as, bool allowDot=false )
186 {
187     return as.length > 0 &&
188     startsWithAllowedChars(as) &&
189     (allowDot?(all!(a=>isValueAccessString(a))(as.split("."))):allowedCharsOnly(as));
190 }
191 
192 bool startsWithAllowedChars( in string as )
193 {
194     switch(as[0])
195     {
196         case 'a': .. case 'z': goto case;
197         case 'A': .. case 'Z': goto case;
198         case '_': return true;
199         default: return false;
200     }
201 }
202 
203 bool allowedCharsOnly( in string as )
204 {
205     foreach( c; as ) if( !allowedChar(c) ) return false;
206     return true;
207 }
208 
209 bool allowedChar( in char c )
210 {
211     switch(c)
212     {
213         case 'a': .. case 'z': goto case;
214         case 'A': .. case 'Z': goto case;
215         case '0': .. case '9': goto case;
216         case '_': return true;
217         default: return false;
218     }
219 }
220 
221 }