|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using System.Reflection.Metadata.Ecma335; |
| 5 | +using System.Runtime.CompilerServices; |
| 6 | +using System.Text; |
| 7 | +using System.Threading.Tasks; |
| 8 | +using PocketCsvReader.Configuration; |
| 9 | + |
| 10 | +namespace Didot.Core.SourceParsers; |
| 11 | + |
| 12 | +[Extension(".csv")] |
| 13 | +internal class CsvSourceBuilder : BaseSourceBuilder<CsvSource> |
| 14 | +{ |
| 15 | + protected Dictionary<string, Action<string>> Actions = []; |
| 16 | + |
| 17 | + private DialectDescriptorBuilder _dialect = new(); |
| 18 | + |
| 19 | + public CsvSourceBuilder() |
| 20 | + { |
| 21 | + Initialize(); |
| 22 | + } |
| 23 | + |
| 24 | + protected virtual void Initialize() |
| 25 | + { |
| 26 | + Actions.Add("delimiter", (delimiter) => _dialect.WithDelimiter(MapEnumToChar<Delimiter>(delimiter))); |
| 27 | + Actions.Add("lineTerminator", (lineTerminator) => _dialect.WithLineTerminator(MapLineTerminator(lineTerminator))); |
| 28 | + Actions.Add("quoteChar", (quoteChar) => _dialect.WithQuoteChar(MapEnumToChar<QuoteChar>(quoteChar))); |
| 29 | + Actions.Add("doubleQuote", (doubleQuote) => _dialect.WithDoubleQuote(doubleQuote.ToBoolean())); |
| 30 | + Actions.Add("escapeChar", (escapeChar) => _dialect.WithEscapeChar(MapEnumToChar<EscapeChar>(escapeChar))); |
| 31 | + Actions.Add("nullSequence", (nullSequence) => _dialect.WithNullSequence(nullSequence)); |
| 32 | + Actions.Add("skipInitialSpace", (skipInitialSpace) => _dialect.WithSkipInitialSpace(skipInitialSpace.ToBoolean())); |
| 33 | + Actions.Add("commentChar", (commentChar) => _dialect.WithCommentChar(MapEnumToChar<CommentChar>(commentChar))); |
| 34 | + Actions.Add("header", (header) => _dialect.WithHeader(header.ToBoolean())); |
| 35 | + Actions.Add("caseSensitiveHeader", (caseSensitiveHeader) => _dialect.WithCaseSensitiveHeader(caseSensitiveHeader.ToBoolean())); |
| 36 | + } |
| 37 | + |
| 38 | + public override ISourceParser Build(IDictionary<string, string> parameters, string extension) |
| 39 | + { |
| 40 | + extension = $"{extension.NormalizeExtension()}@".Substring(1); |
| 41 | + foreach (var kv in parameters.Where(x => x.Key.StartsWith(extension))) |
| 42 | + if (Actions.TryGetValue(kv.Key.Split('@')[1], out var action)) |
| 43 | + action(kv.Value); |
| 44 | + |
| 45 | + var builder = new CsvReaderBuilder(); |
| 46 | + var csvReader = builder.WithDialectDescriptor((_) => _dialect).Build(); |
| 47 | + return new CsvSource(csvReader); |
| 48 | + } |
| 49 | + |
| 50 | + private char MapEnumToChar<T>(string value) where T : Enum |
| 51 | + { |
| 52 | + if (value.Length == 1) |
| 53 | + return value[0]; |
| 54 | + |
| 55 | + return (char)(int)Enum.Parse(typeof(T), |
| 56 | + Enum.GetNames(typeof(T)).FirstOrDefault(x => x.Equals(value, StringComparison.InvariantCultureIgnoreCase)) |
| 57 | + ?? throw new NotSupportedException(value) |
| 58 | + ); |
| 59 | + } |
| 60 | + |
| 61 | + private string MapLineTerminator(string value) |
| 62 | + { |
| 63 | + if (value.Length >= 1 && value.Length <= 2) |
| 64 | + return value; |
| 65 | + |
| 66 | + var name = Enum.Parse<LineTerminator>( |
| 67 | + Enum.GetNames<LineTerminator>().FirstOrDefault(x => x.Equals(value, StringComparison.InvariantCultureIgnoreCase)) |
| 68 | + ?? throw new NotSupportedException(value)); |
| 69 | + |
| 70 | + return name switch |
| 71 | + { |
| 72 | + LineTerminator.CarriageReturn => "\r", |
| 73 | + LineTerminator.LineFeed => "\n", |
| 74 | + LineTerminator.CarriageReturnLineFeed => "\r\n", |
| 75 | + _ => throw new NotSupportedException(value), |
| 76 | + }; |
| 77 | + } |
| 78 | +} |
0 commit comments