Skip to content

Commit

Permalink
more informative error handling and default for nulls
Browse files Browse the repository at this point in the history
  • Loading branch information
EliotJones committed Apr 7, 2022
1 parent c59f6e5 commit 89b445f
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 18 deletions.
122 changes: 106 additions & 16 deletions src/CsvSwan/Csv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private Csv(Stream stream, CsvOptions options, bool canDisposeStream)
this.canDisposeStream = canDisposeStream;

reader = new CsvReader(this.stream, options);
accessor = new RowAccessor(this);
accessor = new RowAccessor(this, options);

if (options.HasHeaderRow)
{
Expand Down Expand Up @@ -187,13 +187,15 @@ public void Dispose()
public class RowAccessor
{
private readonly Csv csv;
private readonly CsvOptions options;

private readonly object mutex = new object();
private readonly Dictionary<Type, TypeMapFactory> typeFactories = new Dictionary<Type, TypeMapFactory>();

internal RowAccessor(Csv csv)
internal RowAccessor(Csv csv, CsvOptions options)
{
this.csv = csv ?? throw new ArgumentNullException(nameof(csv));
this.options = options ?? new CsvOptions();
}

/// <summary>
Expand All @@ -209,18 +211,57 @@ public IReadOnlyList<string> GetValues()
/// Gets the value from the column at the given index as a <see langword="short"/>.
/// </summary>
public short GetShort(int index, IFormatProvider formatProvider = null)
{
var value = GetNullableShort(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
/// Gets the value from the column at the given index as an <see langword="short?"/>.
/// </summary>
public short? GetNullableShort(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return short.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);

var value = csv.currentValues[index];

if (string.IsNullOrEmpty(value))
{
return null;
}

return short.Parse(value, NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
}

/// <summary>
/// Gets the value from the column at the given index as an <see langword="int"/>.
/// </summary>
public int GetInt(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return int.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
var value = GetNullableInt(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand All @@ -245,8 +286,19 @@ public int GetInt(int index, IFormatProvider formatProvider = null)
/// </summary>
public long GetLong(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return long.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
var value = GetNullableLong(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand All @@ -271,8 +323,19 @@ public long GetLong(int index, IFormatProvider formatProvider = null)
/// </summary>
public decimal GetDecimal(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return decimal.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
var value = GetNullableDecimal(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand All @@ -297,8 +360,19 @@ public decimal GetDecimal(int index, IFormatProvider formatProvider = null)
/// </summary>
public double GetDouble(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return double.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
var value = GetNullableDouble(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand All @@ -323,8 +397,19 @@ public double GetDouble(int index, IFormatProvider formatProvider = null)
/// </summary>
public float GetFloat(int index, IFormatProvider formatProvider = null)
{
GuardIndex(index);
return float.Parse(csv.currentValues[index], NumberStyles.Number, formatProvider ?? CultureInfo.InvariantCulture);
var value = GetNullableFloat(index, formatProvider);

if (value.HasValue)
{
return value.Value;
}

if (options.DefaultNullValues)
{
return default;
}

throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand Down Expand Up @@ -353,12 +438,17 @@ public bool GetBool(int index, IFormatProvider formatProvider = null)

var nullable = GetNullableBool(index, formatProvider);

if (!nullable.HasValue)
if (nullable.HasValue)
{
return nullable.Value;
}

if (options.DefaultNullValues)
{
throw new NullReferenceException($"No bool value provided for bool at column index {index}, value was '{csv.currentValues[index]}'.");
return default;
}

return nullable.Value;
throw new InvalidOperationException($"Null or invalid value encountered for column {index} at row {csv.rowIndex}: '{csv.currentValues[index]}'.");
}

/// <summary>
Expand Down
11 changes: 9 additions & 2 deletions src/CsvSwan/CsvOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ public class CsvOptions
/// </summary>
public bool BackslashEscapesQuotes { get; set; } = false;

/// <summary>
/// For non-nullable types like <see langword="int" />s, <see langword="double" />s, <see langword="DateTime" />s
/// use a default value when <see langword="null" /> is encountered.
/// </summary>
public bool DefaultNullValues { get; set; } = true;

/// <summary>
/// Create a new <see cref="CsvOptions"/> with the specified separator.
/// </summary>
Expand All @@ -68,8 +74,9 @@ public static CsvOptions WithSeparator(char separator, bool hasHeaderRow = false
{
Separator = separator,
AreTextFieldsQuoted = true,
HasHeaderRow = hasHeaderRow
HasHeaderRow = hasHeaderRow,
DefaultNullValues = true
};
}
}
}
}

0 comments on commit 89b445f

Please sign in to comment.