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