Skip to content

Commit

Permalink
implemented writer
Browse files Browse the repository at this point in the history
  • Loading branch information
j0nimost committed Oct 10, 2023
1 parent 9cdea7b commit 5176a05
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/Kafa/Kafa.Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static IEnumerable<T> Read<T>(string content, KafaOptions? options = null
ArgumentNullException.ThrowIfNullOrEmpty(content, nameof(content));
var rows = Read(content.AsSpan(), options);
var typeInfo = new KafaTypeInfo(typeof(T), options);

Check warning on line 22 in src/Kafa/Kafa.Span.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'kafaOptions' in 'KafaTypeInfo.KafaTypeInfo(Type type, KafaOptions kafaOptions)'.
var reflection = new KafaReflection(typeInfo, rows.Headers);
var reflection = new KafaReflection(typeInfo);
return reflection.SetProperties<T>(rows);
}

Expand Down
44 changes: 42 additions & 2 deletions src/Kafa/Kafa.Stream.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using nyingi.Kafa.Reader;
using nyingi.Kafa.Reflection;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Xml;
using static nyingi.Kafa.Reader.KafaReader;
Expand Down Expand Up @@ -27,7 +30,7 @@ public static IEnumerable<T> Read<T>(Stream ioStream, KafaOptions? options = nul
{
var rowEnumerable = Read(ioStream, options);
var typeInfo = new KafaTypeInfo(typeof(T), options);

Check warning on line 32 in src/Kafa/Kafa.Stream.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'kafaOptions' in 'KafaTypeInfo.KafaTypeInfo(Type type, KafaOptions kafaOptions)'.
var reflection = new KafaReflection(typeInfo, rowEnumerable.Headers);
var reflection = new KafaReflection(typeInfo);
return reflection.SetProperties<T>(rowEnumerable);
}

Expand Down Expand Up @@ -60,7 +63,7 @@ public static async ValueTask<IEnumerable<T>> ReadAsync<T>(Stream ioStream, Kafa
{
var rows = await ReadAsync(ioStream, options, cancellationToken);
var typeInfo = new KafaTypeInfo(typeof(T), options);

Check warning on line 65 in src/Kafa/Kafa.Stream.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'kafaOptions' in 'KafaTypeInfo.KafaTypeInfo(Type type, KafaOptions kafaOptions)'.
var reflection = new KafaReflection(typeInfo, rows.Headers);
var reflection = new KafaReflection(typeInfo);
return reflection.SetProperties<T>(rows);
}

Expand All @@ -73,5 +76,42 @@ private static async ValueTask<RowEnumerable> ReadProcessorAsync(KafaReadState k

return reader.GetRows();
}

public static async ValueTask<TextWriter> WriteAsync<T>(List<T> entities, KafaOptions options =null)

Check warning on line 80 in src/Kafa/Kafa.Stream.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
{
ArgumentNullException.ThrowIfNull(entities, nameof(entities));
var reflection = SetupOptions<T>(options);
using var strWriter = new StringWriter(new StringBuilder());
return await reflection.GetProperties<T>(entities, strWriter);
}

public static async ValueTask<MemoryStream> WriteToStreamAsync<T>(List<T> entities, KafaOptions options = null)

Check warning on line 88 in src/Kafa/Kafa.Stream.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
{
ArgumentNullException.ThrowIfNull(entities, nameof(entities));
var reflection = SetupOptions<T>(options);
var memoryStream = new MemoryStream();
using var strWriter = new StreamWriter(memoryStream, leaveOpen: true);
var textStream = await reflection.GetProperties<T>(entities, strWriter);
textStream.Flush();
memoryStream.Seek(0, SeekOrigin.Begin);

return memoryStream;
}

public static async ValueTask WriteToFileAsync<T>(List<T> entities, string path, KafaOptions options = null)

Check warning on line 101 in src/Kafa/Kafa.Stream.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
{
ArgumentNullException.ThrowIfNull(entities, nameof(entities));
var reflection = SetupOptions<T>(options);
using var fs = new FileStream(path, FileMode.Create);
using var strWriter = new StreamWriter(fs, options.Encoding!, 512);
await reflection.GetProperties<T>(entities, strWriter);
}

private static KafaReflection SetupOptions<T>(KafaOptions options)
{
options = KafaOptions.ResolveKafaOptions(options);
var typeInfo = new KafaTypeInfo(typeof(T), options);
return new KafaReflection(typeInfo);
}
}
}
1 change: 1 addition & 0 deletions src/Kafa/Kafa.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<Folder Include="Writer\" />
<Folder Include="Reader\" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions src/Kafa/KafaTypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace nyingi.Kafa
{
internal class KafaTypeInfo
{
public Type Type { get; }
public KafaOptions KafaOptions { get; }
public readonly Type Type;
public readonly KafaOptions KafaOptions;
public bool HasAttributes { get; private set;}
public KafaTypeInfo(Type type, KafaOptions kafaOptions)
{
Expand Down
59 changes: 59 additions & 0 deletions src/Kafa/Reflection/KafaReflection.Reader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Diagnostics.Metrics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace nyingi.Kafa.Reflection
{
internal partial class KafaReflection
{
public async Task<TextWriter> GetProperties<T>(List<T> entities, TextWriter textWriter)
{

bool readHeader = false;
int propertyCount = 0;
int count = 0;
PropertyInfo[] propertyInfos = default!;
foreach (var entity in entities)
{
propertyInfos = entity.GetType().GetProperties();

Check warning on line 20 in src/Kafa/Reflection/KafaReflection.Reader.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

if(TypeInfo.KafaOptions.HasHeader && !readHeader)
{
int countHeader = 0;
foreach (var propertyInfo in propertyInfos)
{
await textWriter.WriteAsync(propertyInfo.Name);

if (countHeader < propertyInfos.Length - 1)
{
await textWriter.WriteAsync((char)TypeInfo.KafaOptions.FileType);

}
countHeader++;
}

await textWriter.WriteLineAsync();
readHeader= true;
}

propertyCount = propertyInfos.Length;
foreach (var propertyInfo in propertyInfos)
{
await textWriter.WriteAsync($"{propertyInfo.GetValue(entity)}");

if (count < propertyCount - 1)
{
await textWriter.WriteAsync((char)TypeInfo.KafaOptions.FileType);
}
count++;
}
await textWriter.WriteLineAsync();
count = 0;
}

return textWriter;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
using System.Collections.Specialized;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Reflection.PortableExecutable;
using static nyingi.Kafa.Reader.KafaReader;

namespace nyingi.Kafa.Reflection
{
internal class KafaReflection
internal partial class KafaReflection
{
public Dictionary<int, PropertyInfo> properties= default;
private Dictionary<int, PropertyInfo> properties= default;

Check warning on line 10 in src/Kafa/Reflection/KafaReflection.Writer.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.

public readonly KafaTypeInfo TypeInfo;
public KafaReflection(KafaTypeInfo typeInfo, OrderedDictionary Headers = default)
public KafaReflection(KafaTypeInfo typeInfo)
{
// match propertyName with header
TypeInfo = typeInfo;
// store all the properties
properties = new Dictionary<int, PropertyInfo>(TypeInfo.Type.GetProperties().Length);
}

private void ReadHeader(OrderedDictionary headers = null)

Check warning on line 19 in src/Kafa/Reflection/KafaReflection.Writer.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
{
properties = new Dictionary<int, PropertyInfo>(TypeInfo.Type.GetProperties().Length); // ahead

int count = 0;
foreach (var property in TypeInfo.Type.GetProperties())
{
if(Headers != null)
if (headers != null)
{
var kafa = property.GetCustomAttribute<KafaColumnAttribute>(false);

if (kafa != null)
{
if(!string.IsNullOrEmpty(kafa.FieldName))
if (!string.IsNullOrEmpty(kafa.FieldName))
{
properties.Add((int)Headers[kafa.FieldName], property);
properties.Add((int)headers[kafa.FieldName], property);

}
else
{
properties.Add((int)Headers[kafa.FieldIndex], property);
properties.Add((int)headers[kafa.FieldIndex], property);
}
}
else if (Headers.Contains(property.Name))
else if (headers.Contains(property.Name))
{
properties.Add((int)Headers[property.Name], property);
properties.Add((int)headers[property.Name], property);
}
}
else
{
properties.Add(count, property);
properties.Add(count, property);
count++;
}
}
}

public IEnumerable<T> SetProperties<T>(RowEnumerable rows)
{
// process header first
ReadHeader(rows.Headers);

if(properties.Count == 0)
{
throw new Exception("{0} class is empty");
Expand Down Expand Up @@ -81,8 +88,11 @@ public IEnumerable<T> SetProperties<T>(RowEnumerable rows)
return (List<T>)instance;
}



private object? TypeResolver(Type type, Col col)
{

if (type == typeof(string))
{
return col.Value.ToString();
Expand Down
63 changes: 62 additions & 1 deletion src/KafaTests/KafaReadToTypeTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace KafaTests
using System.IO;

namespace KafaTests
{
class CsvData
{
Expand Down Expand Up @@ -41,6 +43,17 @@ public void ReadFromStreamToTypeOf()
var rows = Kafa.Read<CsvData>(stream);
Assert.NotNull(rows);
Assert.NotEmpty(rows);

foreach (var row in rows)
{
Assert.NotEqual(DateTime.MinValue, row.Date);
Assert.NotEqual(default, row.Open);
Assert.NotEqual(default, row.High);
Assert.NotEqual(default, row.Low);
Assert.NotEqual(default, row.Close);
Assert.NotEqual(default, row.Volume);
Assert.False(string.IsNullOrEmpty(row.Name));
}
}

[Fact]
Expand Down Expand Up @@ -96,5 +109,53 @@ public void ReadToTypeWithAttributes()
}
}

[Fact]
public async Task WriteCSVNoHeaderAsync()
{
var csvs = new List<CsvData>()
{
new CsvData{ Date = DateTime.Parse("10/10/2023 4:09:45 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512},
new CsvData{ Date = DateTime.Parse("10/10/2023 4:09:45 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512}
};

var rowmem = await Kafa.WriteAsync<CsvData>(csvs, new KafaOptions() { HasHeader=false, FileType= FileType.CSV});
var expected = "10/10/2023 4:09:45 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n10/10/2023 4:09:45 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n";
var str = rowmem.ToString();
Assert.NotNull(str);
Assert.NotEmpty(str);
Assert.Equal(expected, str);
}

[Fact]
public async Task WriteCSVWithHeaderAsync()
{
var csvs = new List<CsvData>()
{
new CsvData{ Date = DateTime.Parse("10/10/2023 4:08:38 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512},
new CsvData{ Date = DateTime.Parse("10/10/2023 4:08:38 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512}
};
string expected = "Date,Open,High,Low,Close,Volume,Name\r\n10/10/2023 4:08:38 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n10/10/2023 4:08:38 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n";
var rowmem = await Kafa.WriteAsync<CsvData>(csvs);
var str = rowmem.ToString();
Assert.NotNull(str);
Assert.NotEmpty(str);
Assert.Equal(expected, str);
}

[Fact]
public async Task WriteCSVToStreamAsync()
{
var csvs = new List<CsvData>()
{
new CsvData{ Date = DateTime.Parse("10/10/2023 4:08:38 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512},
new CsvData{ Date = DateTime.Parse("10/10/2023 4:08:38 PM"), Open=12.45, Close=12.99, High=13.00, Low=12.1, Name="AMZN", Volume=1233435512}
};
string expected = "Date,Open,High,Low,Close,Volume,Name\r\n10/10/2023 4:08:38 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n10/10/2023 4:08:38 PM,12.45,13,12.1,12.99,1233435512,AMZN\r\n";
using var stream = await Kafa.WriteToStreamAsync<CsvData>(csvs);
Assert.NotNull(stream);

var result = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length);
Assert.Equal(expected, result);
}
}
}

0 comments on commit 5176a05

Please sign in to comment.