Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide line number in error handler #3

Merged
merged 4 commits into from
Aug 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/FlatFile.Core/Base/FlatFileEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public abstract class FlatFileEngine<TFieldSettings, TLayoutDescriptor> : IFlatF
/// <summary>
/// The handle entry read error func
/// </summary>
private readonly Func<string, Exception, bool> _handleEntryReadError;
private readonly Func<FlatFileErrorContext, bool> _handleEntryReadError;

/// <summary>
/// Gets the line builder.
Expand All @@ -42,7 +42,7 @@ public abstract class FlatFileEngine<TFieldSettings, TLayoutDescriptor> : IFlatF
/// Initializes a new instance of the <see cref="FlatFileEngine{TFieldSettings, TLayoutDescriptor}"/> class.
/// </summary>
/// <param name="handleEntryReadError">The handle entry read error.</param>
protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = null)
protected FlatFileEngine(Func<FlatFileErrorContext, bool> handleEntryReadError = null)
{
_handleEntryReadError = handleEntryReadError;
}
Expand Down Expand Up @@ -85,7 +85,7 @@ protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = nu
throw;
}

if (!_handleEntryReadError(line, ex))
if (!_handleEntryReadError(new FlatFileErrorContext(line, lineNumber, ex)))
{
throw;
}
Expand Down
51 changes: 51 additions & 0 deletions src/FlatFile.Core/Base/FlatFileErrorContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace FlatFile.Core.Base
{
using System;

/// <summary>
/// Provides information about a file parsing error.
/// </summary>
public struct FlatFileErrorContext
{
private readonly string line;
private readonly int lineNumber;
private readonly Exception exception;

/// <summary>
/// Initializes a new instance of <see cref="FlatFileErrorContext"/>.
/// </summary>
/// <param name="line">The content of the line on which the error occurred.</param>
/// <param name="lineNumber">The line numer at which the error occurred.</param>
/// <param name="exception">The error that occurred.</param>
public FlatFileErrorContext(string line, int lineNumber, Exception exception)
{
this.line = line;
this.lineNumber = lineNumber;
this.exception = exception;
}

/// <summary>
/// The content of the line on which the error occurred.
/// </summary>
public string Line
{
get { return line; }
}

/// <summary>
/// The line numer at which the error occurred.
/// </summary>
public int LineNumber
{
get { return lineNumber; }
}

/// <summary>
/// The error that occurred.
/// </summary>
public Exception Exception
{
get { return exception; }
}
}
}
1 change: 1 addition & 0 deletions src/FlatFile.Core/FlatFile.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
<Compile Include="Base\FieldsContainer.cs" />
<Compile Include="Base\FieldSettingsBase.cs" />
<Compile Include="Base\FlatFileEngine.cs" />
<Compile Include="Base\FlatFileErrorContext.cs" />
<Compile Include="Base\LayoutBase.cs" />
<Compile Include="Base\LayoutDescriptorBase.cs" />
<Compile Include="Base\LineBulderBase.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class DelimitedFileMultiEngine : FlatFileEngine<IDelimitedFieldSettingsCo
/// <summary>
/// The handle entry read error func
/// </summary>
readonly Func<string, Exception, bool> handleEntryReadError;
readonly Func<FlatFileErrorContext, bool> handleEntryReadError;
/// <summary>
/// The layout descriptors for this engine
/// </summary>
Expand Down Expand Up @@ -58,7 +58,7 @@ internal DelimitedFileMultiEngine(
Func<string, Type> typeSelectorFunc,
IDelimitedLineBuilderFactory lineBuilderFactory,
IDelimitedLineParserFactory lineParserFactory,
Func<string, Exception, bool> handleEntryReadError = null)
Func<FlatFileErrorContext, bool> handleEntryReadError = null)
{
if (typeSelectorFunc == null) throw new ArgumentNullException("typeSelectorFunc");
this.layoutDescriptors = layoutDescriptors.ToList();
Expand Down Expand Up @@ -170,7 +170,7 @@ public void Read(Stream stream)
throw;
}

if (!handleEntryReadError(line, ex))
if (!handleEntryReadError(new FlatFileErrorContext(line, lineNumber, ex)))
{
throw;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal DelimitedFileEngine(
IDelimitedLayoutDescriptor layoutDescriptor,
IDelimitedLineBuilderFactory builderFactory,
IDelimitedLineParserFactory parserFactory,
Func<string, Exception, bool> handleEntryReadError = null)
Func<FlatFileErrorContext, bool> handleEntryReadError = null)
: base(handleEntryReadError)
{
_builderFactory = builderFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,25 @@ public IFlatFileEngine GetEngine(
descriptor,
new DelimitedLineBuilderFactory(),
new DelimitedLineParserFactory(),
handleEntryReadError);
ctx => handleEntryReadError(ctx.Line, ctx.Exception));
}


/// <summary>
/// Gets the <see cref="IFlatFileEngine" />.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="handleEntryReadError">The handle entry read error func.</param>
/// <returns>IFlatFileEngine.</returns>
public IFlatFileEngine GetEngine(
IDelimitedLayoutDescriptor descriptor,
Func<FlatFileErrorContext, bool> handleEntryReadError)
{
return new DelimitedFileEngine(
descriptor,
new DelimitedLineBuilderFactory(),
new DelimitedLineParserFactory(),
handleEntryReadError);
}

/// <summary>
/// Gets the <see cref="IFlatFileMultiEngine"/>.
Expand All @@ -67,6 +82,26 @@ public IFlatFileMultiEngine GetEngine(
IEnumerable<IDelimitedLayoutDescriptor> layoutDescriptors,
Func<string, Type> typeSelectorFunc,
Func<string, Exception, bool> handleEntryReadError = null)
{
return new DelimitedFileMultiEngine(
layoutDescriptors,
typeSelectorFunc,
new DelimitedLineBuilderFactory(),
lineParserFactory,
ctx => handleEntryReadError(ctx.Line, ctx.Exception));
}

/// <summary>
/// Gets the <see cref="IFlatFileMultiEngine"/>.
/// </summary>
/// <param name="layoutDescriptors">The layout descriptors.</param>
/// <param name="typeSelectorFunc">The type selector function.</param>
/// <param name="handleEntryReadError">The handle entry read error func.</param>
/// <returns>IFlatFileMultiEngine.</returns>
public IFlatFileMultiEngine GetEngine(
IEnumerable<IDelimitedLayoutDescriptor> layoutDescriptors,
Func<string, Type> typeSelectorFunc,
Func<FlatFileErrorContext, bool> handleEntryReadError)
{
return new DelimitedFileMultiEngine(
layoutDescriptors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal FixedLengthFileEngine(
ILayoutDescriptor<IFixedFieldSettingsContainer> layoutDescriptor,
IFixedLengthLineBuilderFactory lineBuilderFactory,
IFixedLengthLineParserFactory lineParserFactory,
Func<string, Exception, bool> handleEntryReadError = null) : base(handleEntryReadError)
Func<FlatFileErrorContext, bool> handleEntryReadError = null) : base(handleEntryReadError)
{
this.lineBuilderFactory = lineBuilderFactory;
this.lineParserFactory = lineParserFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,24 @@ public IFlatFileEngine GetEngine(
return new FixedLengthFileEngine(
descriptor,
new FixedLengthLineBuilderFactory(),
lineParserFactory,
lineParserFactory,
ctx => handleEntryReadError(ctx.Line, ctx.Exception));
}

/// <summary>
/// Gets the <see cref="IFlatFileEngine" />.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <param name="handleEntryReadError">The handle entry read error func.</param>
/// <returns>IFlatFileEngine.</returns>
public IFlatFileEngine GetEngine(
ILayoutDescriptor<IFixedFieldSettingsContainer> descriptor,
Func<FlatFileErrorContext, bool> handleEntryReadError)
{
return new FixedLengthFileEngine(
descriptor,
new FixedLengthLineBuilderFactory(),
lineParserFactory,
handleEntryReadError);
}

Expand All @@ -63,6 +80,26 @@ public IFlatFileMultiEngine GetEngine(
IEnumerable<ILayoutDescriptor<IFixedFieldSettingsContainer>> layoutDescriptors,
Func<string, int, Type> typeSelectorFunc,
Func<string, Exception, bool> handleEntryReadError = null)
{
return new FixedLengthFileMultiEngine(
layoutDescriptors,
typeSelectorFunc,
new FixedLengthLineBuilderFactory(),
lineParserFactory,
ctx => handleEntryReadError(ctx.Line, ctx.Exception));
}

/// <summary>
/// Gets the <see cref="IFlatFileMultiEngine"/>.
/// </summary>
/// <param name="layoutDescriptors">The layout descriptors.</param>
/// <param name="typeSelectorFunc">The type selector function.</param>
/// <param name="handleEntryReadError">The handle entry read error func.</param>
/// <returns>IFlatFileMultiEngine.</returns>
public IFlatFileMultiEngine GetEngine(
IEnumerable<ILayoutDescriptor<IFixedFieldSettingsContainer>> layoutDescriptors,
Func<string, int, Type> typeSelectorFunc,
Func<FlatFileErrorContext, bool> handleEntryReadError)
{
return new FixedLengthFileMultiEngine(
layoutDescriptors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FixedLengthFileMultiEngine : FlatFileEngine<IFixedFieldSettingsCont
/// <summary>
/// The handle entry read error func
/// </summary>
readonly Func<string, Exception, bool> handleEntryReadError;
readonly Func<FlatFileErrorContext, bool> handleEntryReadError;
/// <summary>
/// The layout descriptors for this engine
/// </summary>
Expand Down Expand Up @@ -58,7 +58,7 @@ internal FixedLengthFileMultiEngine(
Func<string, int, Type> typeSelectorFunc,
IFixedLengthLineBuilderFactory lineBuilderFactory,
IFixedLengthLineParserFactory lineParserFactory,
Func<string, Exception, bool> handleEntryReadError = null)
Func<FlatFileErrorContext, bool> handleEntryReadError = null)
{
if (typeSelectorFunc == null) throw new ArgumentNullException("typeSelectorFunc");
this.layoutDescriptors = layoutDescriptors.ToList();
Expand Down Expand Up @@ -191,7 +191,7 @@ private void ReadInternal(StreamReader reader)
throw;
}

if (!handleEntryReadError(line, ex))
if (!handleEntryReadError(new FlatFileErrorContext(line, lineNumber, ex)))
{
throw;
}
Expand Down
93 changes: 93 additions & 0 deletions src/FlatFile.Tests/Delimited/DelimitedErrorHandlingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using FakeItEasy;
using FlatFile.Core.Base;
using FlatFile.Delimited;
using FlatFile.Delimited.Implementation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Xunit;

namespace FlatFile.Tests.Delimited
{
public class DelimitedErrorHandlingTests
{
private IDelimitedLayoutDescriptor layout;
readonly IDelimitedLineParserFactory lineParserFactory;
readonly IList<FlatFileErrorContext> errorContexts = new List<FlatFileErrorContext>();

const string TestData =
@"S,Test Description,00042
S,Test Description,00043
S,Test Description,00044";

public DelimitedErrorHandlingTests()
{
layout = A.Fake<IDelimitedLayoutDescriptor>();
A.CallTo(() => layout.TargetType).Returns(typeof(Record));

lineParserFactory = A.Fake<IDelimitedLineParserFactory>();
A.CallTo(() => lineParserFactory.GetParser(A<IDelimitedLayoutDescriptor>.Ignored))
.Returns(new FakeLineParser());

new DelimitedLineParserFactory(new Dictionary<Type, Type>
{
{ typeof(Record), typeof(FakeLineParser) }
});
}

[Fact]
public void ErrorContextShouldProvideAccurateInformation()
{
var engine = new DelimitedFileEngine(
layout,
A.Fake<IDelimitedLineBuilderFactory>(),
lineParserFactory,
HandleError);

using (var stream = new MemoryStream(Encoding.Default.GetBytes(TestData)))
engine.Read<Record>(stream).ToList();

Assert.Equal(3, errorContexts.Count);
Assert.Equal(new[] { 1, 2, 3 }, errorContexts.Select(ctx => ctx.LineNumber));
Assert.Equal(TestData.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries), errorContexts.Select(ctx => ctx.Line));
Assert.All(errorContexts, ctx => Assert.Equal("Parsing failed!", ctx.Exception.Message));
}

[Fact]
public void MultiEngineErrorContextShouldProvideAccurateInformation()
{
var engine = new DelimitedFileMultiEngine(
new[] { layout },
l => typeof(Record),
A.Fake<IDelimitedLineBuilderFactory>(),
lineParserFactory,
HandleError);

using (var stream = new MemoryStream(Encoding.Default.GetBytes(TestData)))
engine.Read(stream);

Assert.Equal(3, errorContexts.Count);
Assert.Equal(new[] { 1, 2, 3 }, errorContexts.Select(ctx => ctx.LineNumber));
Assert.Equal(TestData.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries), errorContexts.Select(ctx => ctx.Line));
Assert.All(errorContexts, ctx => Assert.Equal("Parsing failed!", ctx.Exception.Message));
}

private bool HandleError(FlatFileErrorContext context)
{
errorContexts.Add(context);
return true;
}

private class FakeLineParser : IDelimitedLineParser
{
public TEntity ParseLine<TEntity>(string line, TEntity entity) where TEntity : new()
{
throw new Exception("Parsing failed!");
}
}

private class Record { }
}
}
Loading