Fauna v10 .NET/C# Driver 0.2.0-beta
 
Loading...
Searching...
No Matches
PipelineExecutor.cs
Go to the documentation of this file.
1using System.Diagnostics;
2using System.Reflection;
3using System.Runtime.CompilerServices;
5using Fauna.Types;
6using Fauna.Util;
7
8namespace Fauna.Linq;
9
10internal interface IPipelineExecutor
11{
12 private static readonly MethodInfo _createEnumExec =
13 typeof(IPipelineExecutor).GetMethod(nameof(CreateEnumExec), BindingFlags.Public | BindingFlags.Static)!;
14
15 private static readonly MethodInfo _createScalarExec =
16 typeof(IPipelineExecutor).GetMethod(nameof(CreateScalarExec), BindingFlags.Public | BindingFlags.Static)!;
17
18 Type ElemType { get; }
19 Type ResType { get; }
20
21 IAsyncEnumerable<Page<object?>> PagedResult(QueryOptions? queryOptions, CancellationToken cancel = default);
22 Task<object?> Result(QueryOptions? queryOptions, CancellationToken cancel = default);
23
24 IAsyncEnumerable<Page<T>> PagedResult<T>(QueryOptions? queryOptions, CancellationToken cancel = default);
25 Task<T> Result<T>(QueryOptions? queryOptions, CancellationToken cancel = default);
26
27 public static IPipelineExecutor Create(
28 DataContext ctx,
29 Query query,
30 ISerializer ser,
31 Delegate? proj,
32 PipelineMode mode)
33 {
34 Debug.Assert(mode != PipelineMode.SetLoad);
35
36 var innerTy = ser.GetType()
37 .GetGenInst(typeof(ISerializer<>))!
38 .GetGenericArguments()[0];
39
40 var elemTy = proj is null ?
41 innerTy :
42 proj.GetType().GetGenInst(typeof(Func<,>))!
43 .GetGenericArguments()[1];
44
45 var method = mode switch
46 {
47 PipelineMode.Query or PipelineMode.Project => _createEnumExec,
48 PipelineMode.Scalar => _createScalarExec,
49 _ => throw new Exception("unreachable"),
50 };
51
52 var typeArgs = new Type[] { innerTy, elemTy };
53 var args = new object?[] { ctx, query, ser, proj };
54 var exec = method.MakeGenericMethod(typeArgs).Invoke(null, args);
55
56 return (IPipelineExecutor)exec!;
57 }
58
59 public static EnumExecutor<E> CreateEnumExec<I, E>(
60 DataContext ctx,
61 Query query,
63 Func<I, E>? proj) =>
64 new EnumExecutor<E>(ctx, query, new PageSerializer<E>(MapSer(ser, proj)));
65
66 public static ScalarExecutor<E> CreateScalarExec<I, E>(
67 DataContext ctx,
68 Query query,
70 Func<I, E>? proj) =>
71 new ScalarExecutor<E>(ctx, query, MapSer(ser, proj));
72
73 private static ISerializer<E> MapSer<I, E>(ISerializer<I> inner, Func<I, E>? proj)
74 {
75 if (proj is not null)
76 {
77 return new MappedDeserializer<I, E>(inner, proj);
78 }
79
80 Debug.Assert(typeof(I) == typeof(E));
81 return (ISerializer<E>)inner;
82 }
83
84 public readonly record struct EnumExecutor<E>(
85 DataContext Ctx,
87 PageSerializer<E> Ser) : IPipelineExecutor
88 {
89 public Type ElemType { get => typeof(E); }
90 public Type ResType { get => typeof(IEnumerable<E>); }
91
92 public IAsyncEnumerable<Page<T>> PagedResult<T>(QueryOptions? queryOptions, CancellationToken cancel = default)
93 {
94 var pages = Ctx.PaginateAsyncInternal(Query, Ser, queryOptions, cancel);
95 if (pages is IAsyncEnumerable<Page<T>> ret)
96 {
97 return ret;
98 }
99
100 Debug.Assert(typeof(T) == ElemType);
101 throw new Exception("unreachable");
102 }
103
104 public async Task<T> Result<T>(QueryOptions? queryOptions, CancellationToken cancel = default)
105 {
106 var pages = PagedResult<E>(queryOptions, cancel);
107 var elems = new List<E>();
108
109 if (elems is T res)
110 {
111 await foreach (var page in pages)
112 {
113 cancel.ThrowIfCancellationRequested();
114 elems.AddRange(page.Data);
115 }
116
117 return res;
118 }
119
120 Debug.Assert(typeof(T) == ResType, $"{typeof(T)} is not {ResType}");
121 throw new Exception("unreachable");
122 }
123
124 public async IAsyncEnumerable<Page<object?>> PagedResult(QueryOptions? queryOptions, [EnumeratorCancellation] CancellationToken cancel = default)
125 {
126 await foreach (var page in PagedResult<E>(queryOptions, cancel))
127 {
128 var data = page.Data.Select(e => (object?)e).ToList();
129 yield return new Page<object?>(data, page.After);
130 }
131 }
132
133 public async Task<object?> Result(QueryOptions? queryOptions, CancellationToken cancel = default) =>
134 await Result<IEnumerable<E>>(queryOptions, cancel);
135 }
136
137
138 public readonly record struct ScalarExecutor<E>(
139 DataContext Ctx,
140 Query Query,
141 ISerializer<E> Ser) : IPipelineExecutor
142 {
143 public Type ElemType { get => typeof(E); }
144 public Type ResType { get => typeof(E); }
145
146 public async Task<T> Result<T>(QueryOptions? queryOptions, CancellationToken cancel = default)
147 {
148 var qres = await Ctx.QueryAsync(Query, Ser, queryOptions, cancel);
149 if (qres.Data is T ret)
150 {
151 return ret;
152 }
153
154 if (qres.Data is null)
155 {
156 return default(T)!;
157 }
158
159 Debug.Assert(typeof(T) == ResType, $"{typeof(T)} is not {ResType}");
160 throw new Exception("unreachable");
161 }
162
163 public async IAsyncEnumerable<Page<T>> PagedResult<T>(QueryOptions? queryOptions, [EnumeratorCancellation] CancellationToken cancel = default)
164 {
165 if (await Result<E>(queryOptions, cancel) is T ret)
166 {
167 yield return new Page<T>(new List<T> { ret }, null);
168 }
169
170 Debug.Assert(typeof(T) == ElemType);
171 throw new Exception("unreachable");
172 }
173
174 public async Task<object?> Result(QueryOptions? queryOptions, CancellationToken cancel = default) =>
175 await Result<E>(queryOptions, cancel);
176
177 public async IAsyncEnumerable<Page<object?>> PagedResult(QueryOptions? queryOptions, [EnumeratorCancellation] CancellationToken cancel = default)
178 {
179 yield return new Page<object?>(new List<object?> { await Result(queryOptions, cancel) }, null);
180 }
181 }
182}
Represents the abstract base class for constructing FQL queries.
Definition Query.cs:11
Represents the options for customizing Fauna queries.
record Page< T >(IReadOnlyList< T > Data, string? After)
Represents a page in a dataset for pagination.