1 /// Contains mixing for easy swizzling
2 module godot.util.swizzle;
3 
4 import std.array;
5 import std.string;
6 import std.algorithm;
7     
8 // used as:
9 //
10 // static if (N == 2 || N == 3 || N == 4) {
11 //     static if (N == 2) enum AccessString = "x y|w h|u v";
12 //     else
13 //     static if (N == 3) enum AccessString = "x y z|w h d|u v t|r g b";
14 //     else
15 //     static if (N == 4) enum AccessString = "x y z w|r g b a";
16 
17 //     mixin accessByString!(N, T, "data", AccessString);
18 // }
19 
20 mixin template accessByString( T, size_t N, string data, string AS, string VVASES=" ", string VVASVS="|")
21     if( isCompatibleArrayAccessStrings(N,AS,VVASES,VVASVS) ) {
22     pure @property {
23         T opDispatch(string v)() const if( getIndex(AS,v,VVASES,VVASVS) != -1 ) { 
24             mixin( format( "return this.%s[%d];", data, getIndex(AS,v,VVASES,VVASVS) ) ); 
25         }
26 
27         ref T opDispatch(string v)() if( getIndex(AS,v,VVASES,VVASVS) != -1 ) { 
28             mixin( format( "return this.%s[%d];", data, getIndex(AS,v,VVASES,VVASVS) ) ); 
29         }
30 
31         static if( isOneSymbolPerFieldForAnyAccessString(AS,VVASES,VVASVS) ) {
32             auto opDispatch(string v)() const if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) ) {
33                 static string gen() {
34                     string[] res;
35                     foreach( i, sym; v )
36                         res ~= format( "this.%s[%d]", data, getIndex( AS, ""~sym, VVASES, VVASVS ) );
37                     return res.join(",");
38                 }
39 
40                 mixin( `return Vector!(T, v.length)(` ~ gen() ~ `);` );
41             }
42 
43             auto opDispatch(string v,U)( in U b ) 
44             if( v.length > 1 && oneOfAnyAccessAll(AS,v,VVASES,VVASVS) && 
45                 isCompatibleArrayAccessString(v.length,v) && 
46             ( isSpecVector!(v.length,T,U) || ( isDynamicVector!U && is(typeof(T(U.datatype.init))) ) ) ) {
47                 static if( b.isDynamic ) enforce( v.length == b.length );
48 
49                 static string gen() {
50                     string[] res;
51                     foreach( i, sym; v )
52                         res ~= format( "this.%s[%d] = T( b[%d] );", data,
53                                     getIndex( AS, ""~sym, VVASES, VVASVS ), i );
54                     return res.join("\n");
55                 }
56 
57                 mixin( gen() );
58                 return b;
59             }
60         }
61     }
62 }
63 
64 /// compatible for creating access dispatches
65 pure bool isCompatibleArrayAccessStrings( size_t N, string str, string sep1="", string sep2="|" )
66 in { assert( sep1 != sep2 ); } do {
67     auto strs = str.split(sep2);
68     foreach( s; strs )
69         if( !isCompatibleArrayAccessString(N,s,sep1) )
70             return false;
71 
72     string[] fa;
73     foreach( s; strs )
74         fa ~= s.split(sep1);
75 
76     foreach( ref v; fa ) v = strip(v);
77 
78     foreach( i, a; fa )
79         foreach( j, b; fa )
80             if( i != j && a == b ) return false;
81 
82     return true;
83 }
84 
85 
86 /// compatible for creating access dispatches
87 pure bool isCompatibleArrayAccessString( size_t N, string str, string sep="" ) { 
88     return N == getAccessFieldsCount(str,sep) && isArrayAccessString(str,sep); 
89 }
90 
91 ///
92 pure bool isArrayAccessString( in string as, in string sep="", bool allowDot=false ) {
93     if( as.length == 0 ) return false;
94     auto splt = as.split(sep);
95     foreach( i, val; splt )
96         if( !isValueAccessString(val,allowDot) || canFind(splt[0..i],val) )
97             return false;
98     return true;
99 }
100 
101 ///
102 pure size_t getAccessFieldsCount( string str, string sep ) { return str.split(sep).length; }
103 
104 ///
105 pure ptrdiff_t getIndex( string as, string arg, string sep1="", string sep2="|" )
106 in { assert( sep1 != sep2 ); } do
107 {
108     foreach( str; as.split(sep2) )
109         foreach( i, v; str.split(sep1) )
110             if( arg == v ) return i;
111     return -1;
112 }
113 
114 ///
115 pure bool oneOfAccess( string str, string arg, string sep="" ) {
116     auto splt = str.split(sep);
117     return canFind(splt,arg);
118 }
119 
120 ///
121 pure bool oneOfAccessAll( string str, string arg, string sep="" ) {
122     auto splt = arg.split("");
123     return all!(a=>oneOfAccess(str,a,sep))(splt);
124 }
125 
126 ///
127 pure bool oneOfAnyAccessAll( string str, string arg, string sep1="", string sep2="|" )
128 in { assert( sep1 != sep2 ); } do
129 {
130     foreach( s; str.split(sep2) )
131         if( oneOfAccessAll(s,arg,sep1) ) return true;
132     return false;
133 }
134 
135 /// check symbol count for access to field
136 pure bool isOneSymbolPerFieldForAnyAccessString( string str, string sep1="", string sep2="|" )
137 in { assert( sep1 != sep2 ); } do
138 {
139     foreach( s; str.split(sep2) )
140         if( isOneSymbolPerFieldAccessString(s,sep1) ) return true;
141     return false;
142 }
143 
144 /// check symbol count for access to field
145 pure bool isOneSymbolPerFieldAccessString( string str, string sep="" ) {
146     foreach( s; str.split(sep) )
147         if( s.length > 1 ) return false;
148     return true;
149 }
150 
151 pure
152 {
153 
154     bool isValueAccessString( in string as, bool allowDot=false ) {
155         return as.length > 0 &&
156         startsWithAllowedChars(as) &&
157         (allowDot?(all!(a=>isValueAccessString(a))(as.split("."))):allowedCharsOnly(as));
158     }
159 
160     bool startsWithAllowedChars( in string as ) {
161         switch(as[0]) {
162             case 'a': .. case 'z': goto case;
163             case 'A': .. case 'Z': goto case;
164             case '_': return true;
165             default: return false;
166         }
167     }
168 
169     bool allowedCharsOnly( in string as ) {
170         foreach( c; as ) if( !allowedChar(c) ) return false;
171         return true;
172     }
173 
174     bool allowedChar( in char c ) {
175         switch(c) {
176             case 'a': .. case 'z': goto case;
177             case 'A': .. case 'Z': goto case;
178             case '0': .. case '9': goto case;
179             case '_': return true;
180             default: return false;
181         }
182     }
183 
184 }