1+ //<auto-generated/>
2+ #nullable enable
3+ using System ;
4+ using System . Collections . Generic ;
5+ using System . Globalization ;
6+ using System . Linq ;
7+ using System . Text . RegularExpressions ;
8+ using Microsoft . OData . Edm ;
9+ using Microsoft . OData . UriParser ;
10+ using Microsoft . OData . UriParser . Aggregation ;
11+
12+ namespace Devlooped ;
13+
14+ /// <summary>
15+ /// Helper class that fixes filter expressions containing DateTimeOffset comparisons.
16+ /// </summary>
17+ internal static class FilterExpressionFixer
18+ {
19+ /// <summary>
20+ /// Fixes filter expressions involving DateTimeOffset by converting them to the format expected by Azure Table Storage.
21+ /// </summary>
22+ /// <param name="filter">The filter expression to fix.</param>
23+ /// <returns>A fixed filter expression where DateTimeOffset comparisons are properly formatted.</returns>
24+ public static string ? Fix ( string ? filter )
25+ {
26+ if ( string . IsNullOrEmpty ( filter ) )
27+ return filter ;
28+
29+ var parser = new UriQueryExpressionParser ( int . MaxValue ) ;
30+ var node = parser . ParseFilter ( filter ) ;
31+
32+ if ( node == null )
33+ return filter ;
34+
35+ try
36+ {
37+ return node . Accept ( new QueryTokenToFilterStringVisitor ( ) ) ;
38+ }
39+ catch ( NotSupportedException )
40+ {
41+ return filter ;
42+ }
43+ }
44+
45+ class QueryTokenToFilterStringVisitor : ISyntacticTreeVisitor < string >
46+ {
47+ public string Visit ( BinaryOperatorToken token )
48+ {
49+ string left = token . Left . Accept ( this ) ;
50+ string right = token . Right . Accept ( this ) ;
51+ string op = ToOperatorString ( token . OperatorKind ) ;
52+ return $ "{ left } { op } { right } ";
53+ }
54+
55+ public string Visit ( UnaryOperatorToken token )
56+ {
57+ string operand = token . Operand . Accept ( this ) ;
58+ string op = ToOperatorString ( token . OperatorKind ) ;
59+ return $ "{ op } { operand } ";
60+ }
61+
62+ public string Visit ( LiteralToken token )
63+ {
64+ // You may want to handle different literal types more carefully
65+ if ( token . Value is string s )
66+ return $ "'{ s . Replace ( "'" , "''" ) } '";
67+ if ( token . Value is bool b )
68+ return b ? "true" : "false" ;
69+ if ( token . Value is DateTimeOffset dto )
70+ return $ "datetime'{ dto . UtcDateTime . ToString ( "o" , CultureInfo . InvariantCulture ) } '";
71+ if ( token . Value is DateTime dt )
72+ return $ "datetime'{ dt . ToUniversalTime ( ) . ToString ( "o" , CultureInfo . InvariantCulture ) } '";
73+ if ( token . Value is Guid g )
74+ return $ "guid'{ g . ToString ( ) } '";
75+ if ( token . Value == null )
76+ return "null" ;
77+
78+ return Convert . ToString ( token . Value , CultureInfo . InvariantCulture ) ! ;
79+ }
80+
81+ public string Visit ( EndPathToken token )
82+ {
83+ // Handles property/field access
84+ if ( token . NextToken != null )
85+ return $ "{ token . Identifier } /{ token . NextToken . Accept ( this ) } ";
86+ return token . Identifier ;
87+ }
88+
89+ public string Visit ( DottedIdentifierToken token )
90+ {
91+ if ( token . NextToken != null )
92+ return $ "{ token . Identifier } /{ token . NextToken . Accept ( this ) } ";
93+ return token . Identifier ;
94+ }
95+
96+ public string Visit ( FunctionCallToken token )
97+ {
98+ var args = token . Arguments ? . Select ( a => a . Accept ( this ) ) . ToArray ( ) ?? Array . Empty < string > ( ) ;
99+ string source = token . Source != null ? token . Source . Accept ( this ) + "/" : "" ;
100+ return $ "{ source } { token . Name } ({ string . Join ( "," , args ) } )";
101+ }
102+
103+ public string Visit ( FunctionParameterToken token ) => token . ValueToken . Accept ( this ) ;
104+
105+ public string Visit ( AnyToken token )
106+ {
107+ var parent = token . Parent ? . Accept ( this ) ;
108+ var expr = token . Expression ? . Accept ( this ) ;
109+ return $ "{ parent } /any({ token . Parameter } :{ expr } )";
110+ }
111+
112+ public string Visit ( AllToken token )
113+ {
114+ var parent = token . Parent ? . Accept ( this ) ;
115+ var expr = token . Expression ? . Accept ( this ) ;
116+ return $ "{ parent } /all({ token . Parameter } :{ expr } )";
117+ }
118+
119+ public string Visit ( InToken token )
120+ {
121+ string left = token . Left . Accept ( this ) ;
122+ string right = token . Right . Accept ( this ) ;
123+ return $ "{ left } in { right } ";
124+ }
125+
126+ // Add more Visit methods for other token types as needed...
127+
128+ // Helper to convert operator enums to OData string
129+ static string ToOperatorString ( BinaryOperatorKind op )
130+ {
131+ return op switch
132+ {
133+ BinaryOperatorKind . And => "and" ,
134+ BinaryOperatorKind . Or => "or" ,
135+ BinaryOperatorKind . Equal => "eq" ,
136+ BinaryOperatorKind . NotEqual => "ne" ,
137+ BinaryOperatorKind . GreaterThan => "gt" ,
138+ BinaryOperatorKind . GreaterThanOrEqual => "ge" ,
139+ BinaryOperatorKind . LessThan => "lt" ,
140+ BinaryOperatorKind . LessThanOrEqual => "le" ,
141+ _ => throw new NotSupportedException ( $ "Operator { op } not supported")
142+ } ;
143+ }
144+
145+ static string ToOperatorString ( UnaryOperatorKind op )
146+ {
147+ return op switch
148+ {
149+ UnaryOperatorKind . Not => "not" ,
150+ _ => throw new NotSupportedException ( $ "Operator { op } not supported")
151+ } ;
152+ }
153+
154+ // Implement stubs for other ISyntacticTreeVisitor<string> methods
155+ public string Visit ( CustomQueryOptionToken token ) => throw new NotSupportedException ( ) ;
156+ public string Visit ( AggregateExpressionToken token ) => throw new NotSupportedException ( ) ;
157+ public string Visit ( CountSegmentToken tokenIn ) => throw new NotImplementedException ( ) ;
158+ public string Visit ( ExpandToken tokenIn ) => throw new NotImplementedException ( ) ;
159+ public string Visit ( ExpandTermToken tokenIn ) => throw new NotImplementedException ( ) ;
160+ public string Visit ( LambdaToken tokenIn ) => throw new NotImplementedException ( ) ;
161+ public string Visit ( InnerPathToken tokenIn ) => throw new NotImplementedException ( ) ;
162+ public string Visit ( OrderByToken tokenIn ) => throw new NotImplementedException ( ) ;
163+ public string Visit ( RangeVariableToken tokenIn ) => throw new NotImplementedException ( ) ;
164+ public string Visit ( SelectToken tokenIn ) => throw new NotImplementedException ( ) ;
165+ public string Visit ( SelectTermToken tokenIn ) => throw new NotImplementedException ( ) ;
166+ public string Visit ( StarToken tokenIn ) => throw new NotImplementedException ( ) ;
167+ public string Visit ( AggregateToken tokenIn ) => throw new NotImplementedException ( ) ;
168+ public string Visit ( EntitySetAggregateToken tokenIn ) => throw new NotImplementedException ( ) ;
169+ public string Visit ( GroupByToken tokenIn ) => throw new NotImplementedException ( ) ;
170+ // ...and so on for all required token types
171+ }
172+ }
0 commit comments