Fauna .NET Driver 0.1.0-beta
 
Loading...
Searching...
No Matches
Utf8FaunaReader.cs
Go to the documentation of this file.
1using System.Buffers;
2using System.Globalization;
3using System.Text;
4using System.Text.Json;
5using Fauna.Types;
6
7namespace Fauna.Serialization;
8
12public ref struct Utf8FaunaReader
13{
14 private Utf8JsonReader _json;
15 private readonly Stack<object> _tokenStack = new();
16 private TokenType? _bufferedTokenType = null;
17
18 private readonly HashSet<TokenType> _closers = new()
19 {
20 TokenType.EndObject,
21 TokenType.EndPage,
22 TokenType.EndDocument,
23 TokenType.EndRef,
24 TokenType.EndArray
25 };
26
27 private string? _taggedTokenValue = null;
28
32 public TokenType CurrentTokenType { get; private set; }
33
34 private enum TokenTypeInternal
35 {
37 StartEscapedObject,
38 }
39
44 public Utf8FaunaReader(ReadOnlySequence<byte> bytes)
45 {
46 _json = new Utf8JsonReader(bytes);
48 }
49
54 public Utf8FaunaReader(string str)
55 {
56 var bytes = Encoding.UTF8.GetBytes(str);
57 var seq = new ReadOnlySequence<byte>(bytes);
58 _json = new Utf8JsonReader(seq);
60 }
61
65 public void Skip()
66 {
67 switch (CurrentTokenType)
68 {
69 case TokenType.StartObject:
70 case TokenType.StartArray:
71 case TokenType.StartPage:
72 case TokenType.StartRef:
73 case TokenType.StartDocument:
74 SkipInternal();
75 break;
76 }
77 }
78
79 private void SkipInternal()
80 {
81 var startCount = _tokenStack.Count;
82 while (Read())
83 {
84 if (_tokenStack.Count < startCount) break;
85 }
86 }
87
92 public bool Read()
93 {
94 _taggedTokenValue = null;
95
96 if (_bufferedTokenType != null)
97 {
98 CurrentTokenType = (TokenType)_bufferedTokenType;
99 _bufferedTokenType = null;
100 if (_closers.Contains(CurrentTokenType))
101 {
102 _tokenStack.Pop();
103 }
104 return true;
105 }
106
107 if (!Advance())
108 {
109 return false;
110 }
111
112 switch (_json.TokenType)
113 {
114 case JsonTokenType.PropertyName:
115 CurrentTokenType = TokenType.FieldName;
116 break;
117 case JsonTokenType.None:
118 break;
119 case JsonTokenType.StartObject:
120 HandleStartObject();
121 break;
122 case JsonTokenType.EndObject:
123 HandleEndObject();
124 break;
125 case JsonTokenType.StartArray:
126 _tokenStack.Push(TokenType.StartArray);
127 CurrentTokenType = TokenType.StartArray;
128 break;
129 case JsonTokenType.EndArray:
130 _tokenStack.Pop();
131 CurrentTokenType = TokenType.EndArray;
132 break;
133 case JsonTokenType.String:
135 break;
136 case JsonTokenType.True:
138 break;
139 case JsonTokenType.False:
141 break;
142 case JsonTokenType.Null:
144 break;
145 case JsonTokenType.Comment:
146 case JsonTokenType.Number:
147 default:
148 throw new SerializationException($"Unhandled JSON token type {_json.TokenType}.");
149 }
150
151 return true;
152 }
153
159 public object? GetValue()
160 {
161 return CurrentTokenType switch
162 {
163 TokenType.FieldName or TokenType.String => GetString(),
164 TokenType.Int => GetInt(),
165 TokenType.Long => GetLong(),
166 TokenType.Double => GetDouble(),
167 TokenType.Date => GetDate(),
168 TokenType.Time => GetTime(),
169 TokenType.True or TokenType.False => GetBoolean(),
170 TokenType.Module => GetModule(),
171 _ => throw new SerializationException($"{CurrentTokenType} does not have an associated value")
172 };
173 }
174
179 public string? GetString()
180 {
181 if (CurrentTokenType != TokenType.String && CurrentTokenType != TokenType.FieldName)
182 {
183 throw new InvalidOperationException($"Fauna token value isn't a {TokenType.String.ToString()} or a {TokenType.FieldName.ToString()}.");
184 }
185
186 try
187 {
188 return _json.GetString();
189 }
190 catch (Exception e)
191 {
192 throw new SerializationException("Failed to get string", e);
193 }
194 }
195
200 public bool GetBoolean()
201 {
202 try
203 {
204 return _json.GetBoolean();
205 }
206 catch (Exception e)
207 {
208 throw new SerializationException("Failed to get boolean", e);
209 }
210 }
211
216 public DateOnly GetDate()
217 {
218 ValidateTaggedType(TokenType.Date);
219
220 try
221 {
222 return DateOnly.Parse(_taggedTokenValue!);
223 }
224 catch (Exception e)
225 {
226 throw new SerializationException($"Failed to get date from {_taggedTokenValue}", e);
227 }
228 }
229
234 public double GetDouble()
235 {
236 ValidateTaggedType(TokenType.Double);
237
238 try
239 {
240 return double.Parse(_taggedTokenValue!, CultureInfo.InvariantCulture);
241 }
242 catch (Exception e)
243 {
244 throw new SerializationException($"Failed to get double from {_taggedTokenValue}", e);
245 }
246 }
247
252 public decimal GetDoubleAsDecimal()
253 {
254 ValidateTaggedType(TokenType.Double);
255
256 try
257 {
258 return decimal.Parse(_taggedTokenValue!, CultureInfo.InvariantCulture);
259 }
260 catch (Exception e)
261 {
262 throw new SerializationException($"Failed to get decimal from {_taggedTokenValue}", e);
263 }
264 }
265
270 public int GetInt()
271 {
272 ValidateTaggedType(TokenType.Int);
273
274 try
275 {
276 return int.Parse(_taggedTokenValue!);
277 }
278 catch (Exception e)
279 {
280 throw new SerializationException($"Failed to get int from {_taggedTokenValue}", e);
281 }
282 }
283
288 public long GetLong()
289 {
290 ValidateTaggedType(TokenType.Long);
291
292 try
293 {
294 return long.Parse(_taggedTokenValue!);
295 }
296 catch (Exception e)
297 {
298 throw new SerializationException($"Failed to get long from {_taggedTokenValue}", e);
299 }
300 }
301
307 {
308 ValidateTaggedType(TokenType.Module);
309
310 return new Module(_taggedTokenValue!);
311 }
312
317 public DateTime GetTime()
318 {
319 ValidateTaggedType(TokenType.Time);
320
321 try
322 {
323 return DateTime.Parse(_taggedTokenValue!);
324 }
325 catch (Exception e)
326 {
327 throw new SerializationException($"Failed to get time from {_taggedTokenValue}", e);
328 }
329 }
330
336 public string TryGetString(out string value)
337 {
338 throw new NotImplementedException();
339 }
340
346 public bool TryGetBoolean(out bool value)
347 {
348 throw new NotImplementedException();
349 }
350
356 public DateTime TryGetDateTime(out DateTime value)
357 {
358 throw new NotImplementedException();
359 }
360
366 public double TryGetDouble(out double value)
367 {
368 throw new NotImplementedException();
369 }
370
376 public int TryGetInt(out int value)
377 {
378 throw new NotImplementedException();
379 }
380
386 public long TryGetLong(out long value)
387 {
388 throw new NotImplementedException();
389 }
390
396 public Module TryGetModule(out Module value)
397 {
398 throw new NotImplementedException();
399 }
400
401 private void ValidateTaggedType(TokenType type)
402 {
403 if (CurrentTokenType != type || _taggedTokenValue == null || _taggedTokenValue.GetType() != typeof(string))
404 {
405 throw new InvalidOperationException($"CurrentTokenType is a {CurrentTokenType.ToString()}, not a {type.ToString()}.");
406 }
407 }
408
409 private void HandleStartObject()
410 {
411 AdvanceTrue();
412
413 switch (_json.TokenType)
414 {
415 case JsonTokenType.PropertyName:
416 switch (_json.GetString())
417 {
418 case "@date":
419 HandleTaggedString(TokenType.Date);
420 break;
421 case "@doc":
422 AdvanceTrue();
423 CurrentTokenType = TokenType.StartDocument;
424 _tokenStack.Push(TokenType.StartDocument);
425 break;
426 case "@double":
427 HandleTaggedString(TokenType.Double);
428 break;
429 case "@int":
430 HandleTaggedString(TokenType.Int);
431 break;
432 case "@long":
433 HandleTaggedString(TokenType.Long);
434 break;
435 case "@mod":
436 HandleTaggedString(TokenType.Module);
437 break;
438 case "@object":
439 AdvanceTrue();
440 CurrentTokenType = TokenType.StartObject;
441 _tokenStack.Push(TokenTypeInternal.StartEscapedObject);
442 break;
443 case "@ref":
444 AdvanceTrue();
445 CurrentTokenType = TokenType.StartRef;
446 _tokenStack.Push(TokenType.StartRef);
447 break;
448 case "@set":
449 AdvanceTrue();
450 CurrentTokenType = TokenType.StartPage;
451 _tokenStack.Push(TokenType.StartPage);
452 break;
453 case "@time":
454 HandleTaggedString(TokenType.Time);
455 break;
456 default:
457 _bufferedTokenType = TokenType.FieldName;
458 _tokenStack.Push(TokenType.StartObject);
459 CurrentTokenType = TokenType.StartObject;
460 break;
461 }
462 break;
463 case JsonTokenType.EndObject:
464 _bufferedTokenType = TokenType.EndObject;
465 _tokenStack.Push(TokenType.StartObject);
466 CurrentTokenType = TokenType.StartObject;
467 break;
468 default:
469 throw new SerializationException($"Unexpected token following StartObject: {_json.TokenType}");
470 }
471 }
472
473 private void HandleEndObject()
474 {
475 var startToken = _tokenStack.Pop();
476 switch (startToken)
477 {
478 case TokenType.StartDocument:
479 CurrentTokenType = TokenType.EndDocument;
480 AdvanceTrue();
481 break;
482 case TokenType.StartPage:
483 CurrentTokenType = TokenType.EndPage;
484 AdvanceTrue();
485 break;
486 case TokenType.StartRef:
488 AdvanceTrue();
489 break;
490 case TokenTypeInternal.StartEscapedObject:
491 CurrentTokenType = TokenType.EndObject;
492 AdvanceTrue();
493 break;
494 case TokenType.StartObject:
495 CurrentTokenType = TokenType.EndObject;
496 break;
497 default:
498 throw new SerializationException($"Unexpected token {startToken}. This might be a bug.");
499 }
500 }
501
513 private void HandleTaggedString(TokenType token)
514 {
515 AdvanceTrue();
516 CurrentTokenType = token;
517 _taggedTokenValue = _json.GetString();
518 AdvanceTrue();
519 }
520
521 private bool Advance()
522 {
523 try
524 {
525 return _json.Read();
526 }
527 catch (Exception e)
528 {
529 throw new SerializationException("Failed to advance underlying JSON reader.", e);
530 }
531 }
532
533 private void AdvanceTrue()
534 {
535 if (!Advance())
536 {
537 throw new SerializationException("Unexpected end of underlying JSON reader.");
538 }
539 }
540}
Represents error that occur during serialization and deserialization of Fauna data.
Represents a module, a singleton object grouping related functionalities. Modules are serialized as @...
Definition Module.cs:8
TokenType
Enumerates the types of tokens used in Fauna serialization.
Definition TokenType.cs:7
@ Module
The token type is the Fauna module.
Represents a reader that provides fast, non-cached, forward-only access to serialized data.
Utf8FaunaReader(ReadOnlySequence< byte > bytes)
Initializes a new Utf8FaunaReader to read from a ReadOnlySequence of bytes.
decimal GetDoubleAsDecimal()
Retrieves a decimal value from the current token.
DateOnly GetDate()
Retrieves a DateOnly value from the current token.
void Skip()
Skips the value of the current token.
DateTime TryGetDateTime(out DateTime value)
Tries to retrieve a DateTime value from the current token.
Module TryGetModule(out Module value)
Tries to retrieve a Module object from the current token.
long TryGetLong(out long value)
Tries to retrieve a long value from the current token.
double GetDouble()
Retrieves a double value from the current token.
Utf8FaunaReader(string str)
Initializes a new Utf8FaunaReader to read from a string.
string TryGetString(out string value)
Tries to retrieve a string value from the current token.
bool TryGetBoolean(out bool value)
Tries to retrieve a boolean value from the current token.
object? GetValue()
Gets the value of the current token.
TokenType CurrentTokenType
Gets the type of the current token.
int TryGetInt(out int value)
Tries to retrieve an integer value from the current token.
long GetLong()
Retrieves a long value from the current token.
bool GetBoolean()
Retrieves a boolean value from the current JSON token.
string? GetString()
Retrieves a string value from the current token.
bool Read()
Reads the next token from the source.
DateTime GetTime()
Retrieves a DateTime value from the current token.
double TryGetDouble(out double value)
Tries to retrieve a double value from the current token.
int GetInt()
Retrieves an integer value from the current token.
Module GetModule()
Retrieves a Module object from the current token.