Skip to content
This repository has been archived by the owner on Jul 5, 2023. It is now read-only.

Implemented Length type #63 #68

Merged
merged 6 commits into from
Jun 30, 2023
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
12 changes: 12 additions & 0 deletions src/Nox.Types.EntityFramework/Types/Length/LengthConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace Nox.Types.EntityFramework.Types;

public class LengthToFootConverter : ValueConverter<Length, double>
{
public LengthToFootConverter() : base(length => (double)length.ToFeet(), lengthValue => Length.FromFeet(lengthValue)) { }
}
public class LengthToMeterConverter : ValueConverter<Length, double>
{
public LengthToMeterConverter() : base(length => (double)length.ToMeters(), lengthValue => Length.FromMeters(lengthValue)) { }
}
37 changes: 37 additions & 0 deletions src/Nox.Types/Common/MeasurementConversionFactor.cs
rochar marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;

namespace Nox.Common;

internal sealed class MeasurementConversionFactor
{
private static readonly Dictionary<(MeasurementTypeUnit, MeasurementTypeUnit), double> DefinedConversionFactors = new()
{
{ (MeasurementTypeUnit.Foot, MeasurementTypeUnit.Meter), 0.30480000033},
{ (MeasurementTypeUnit.Meter, MeasurementTypeUnit.Foot), 3.28083989142},
{ (MeasurementTypeUnit.Kilometer, MeasurementTypeUnit.Mile), 0.62137119102},
{ (MeasurementTypeUnit.Mile, MeasurementTypeUnit.Kilometer), 1.60934400315},
{ (MeasurementTypeUnit.SquareFoot, MeasurementTypeUnit.SquareMeter), 0.09290304},
{ (MeasurementTypeUnit.SquareMeter, MeasurementTypeUnit.SquareFoot), 10.76391042},
};

public double Value { get; }

public MeasurementConversionFactor(MeasurementTypeUnit sourceUnit, MeasurementTypeUnit targetUnit)
{
Value = ResolveConversionFactor(sourceUnit, targetUnit);
}

private static double ResolveConversionFactor(MeasurementTypeUnit sourceUnit, MeasurementTypeUnit targetUnit)
{
var conversion = (sourceUnit, targetUnit);

if (DefinedConversionFactors.ContainsKey(conversion))
return DefinedConversionFactors[conversion];

else if (sourceUnit == targetUnit)
return 1;

throw new NotImplementedException($"No conversion defined from {sourceUnit} to {targetUnit}.");
}
}
14 changes: 14 additions & 0 deletions src/Nox.Types/Common/MeasurementTypeUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Nox.Common;

internal enum MeasurementTypeUnit
{
// Length
Foot = 1,
Meter = 2,
Kilometer = 3,
Mile = 4,

// Area
SquareFoot = 5,
SquareMeter = 6,
}
1 change: 1 addition & 0 deletions src/Nox.Types/Nox.Types.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
</PropertyGroup>
<ItemGroup>
<None Include="..\..\images\nox.png" Pack="true" PackagePath="" />
<InternalsVisibleTo Include="Nox.Types.Tests" />
</ItemGroup>
</Project>
29 changes: 16 additions & 13 deletions src/Nox.Types/Types/Area/Area.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Nox.Common;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Nox.Types;

Expand Down Expand Up @@ -91,7 +93,16 @@ internal override ValidationResult Validate()
return result;
}

public override string ToString() => $"{Value:G} {Unit.ToSymbol()}";
public override string ToString()
=> $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit.ToSymbol()}";

/// <summary>
/// Returns a string representation of the <see cref="Length"/> object using the specified <see cref="IFormatProvider"/>.
/// </summary>
/// <param name="formatProvider">The format provider for the length value.</param>
/// <returns>A string representation of the <see cref="Length"/> object with the value formatted using the specified <see cref="IFormatProvider"/>.</returns>
public string ToString(IFormatProvider formatProvider)
=> $"{Value.ToString(formatProvider)} {Unit.ToSymbol()}";

protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponents()
{
Expand All @@ -104,18 +115,10 @@ protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponen
private QuantityValue? _squareFeet;
public QuantityValue ToSquareFeet() => (_squareFeet ??= GetAreaIn(AreaTypeUnit.SquareFoot));

private QuantityValue GetAreaIn(AreaTypeUnit unit)
private QuantityValue GetAreaIn(AreaTypeUnit targetUnit)
{
if (Unit == unit)
return Round(Value);

else if (Unit == AreaTypeUnit.SquareMeter && unit == AreaTypeUnit.SquareFoot)
return Round(Value * 10.76391042);

else if (Unit == AreaTypeUnit.SquareFoot && unit == AreaTypeUnit.SquareMeter)
return Round(Value * 0.09290304);

throw new NotImplementedException($"No conversion defined from {Unit} to {unit}.");
var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value;
return Round(Value * factor);
}

private static QuantityValue Round(QuantityValue value)
Expand Down
4 changes: 2 additions & 2 deletions src/Nox.Types/Types/Area/AreaTypeUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace Nox.Types;

public enum AreaTypeUnit
{
SquareFoot,
SquareMeter,
SquareFoot = 5,
SquareMeter = 6,
}

public static class AreaTypeUnitExtensions
Expand Down
29 changes: 16 additions & 13 deletions src/Nox.Types/Types/Distance/Distance.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Nox.Common;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Nox.Types;

Expand Down Expand Up @@ -112,7 +114,16 @@ protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponen
yield return new KeyValuePair<string, object>(nameof(Value), ToKilometers());
}

public override string ToString() => $"{Value:G} {Unit.ToSymbol()}";
public override string ToString()
=> $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit.ToSymbol()}";

/// <summary>
/// Returns a string representation of the <see cref="Length"/> object using the specified <see cref="IFormatProvider"/>.
/// </summary>
/// <param name="formatProvider">The format provider for the length value.</param>
/// <returns>A string representation of the <see cref="Length"/> object with the value formatted using the specified <see cref="IFormatProvider"/>.</returns>
public string ToString(IFormatProvider formatProvider)
=> $"{Value.ToString(formatProvider)} {Unit.ToSymbol()}";

private QuantityValue? _kilometers;

Expand All @@ -122,18 +133,10 @@ protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponen

public QuantityValue ToMiles() => (_miles ??= GetDistanceIn(DistanceTypeUnit.Mile));

private QuantityValue GetDistanceIn(DistanceTypeUnit unit)
private QuantityValue GetDistanceIn(DistanceTypeUnit targetUnit)
{
if (Unit == unit)
return Round(Value);

else if (Unit == DistanceTypeUnit.Kilometer && unit == DistanceTypeUnit.Mile)
return Round(Value * 0.62137119102);

else if (Unit == DistanceTypeUnit.Mile && unit == DistanceTypeUnit.Kilometer)
return Round(Value * 1.60934400315);

throw new NotImplementedException($"No conversion defined from {Unit} to {unit}.");
var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value;
return Round(Value * factor);
}

private static QuantityValue Round(QuantityValue value)
Expand Down
4 changes: 2 additions & 2 deletions src/Nox.Types/Types/Distance/DistanceTypeUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace Nox.Types;

public enum DistanceTypeUnit
{
Kilometer,
Mile
Kilometer = 3,
Mile = 4,
}

public static class DistanceTypeUnitExtensions
Expand Down
123 changes: 120 additions & 3 deletions src/Nox.Types/Types/Length/Length.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,126 @@
using Nox.Common;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Nox.Types;

/// <summary>
/// Represents a Nox <see cref="Length"/> type and value object.
/// </summary>
public sealed class Length : ValueObject<QuantityValue, Length>
{
private const int QuantityValueDecimalPrecision = 6;

public LengthTypeUnit Unit { get; private set; } = LengthTypeUnit.Meter;

public Length() { Value = 0; }

/// <summary>
/// Creates a new instance of <see cref="Length"/> object in meters.
/// </summary>
/// <param name="value">The value to create the <see cref="Length"/> with</param>
/// <returns></returns>
/// <exception cref="TypeValidationException"></exception>
public static Length FromMeters(QuantityValue value)
=> From(value, LengthTypeUnit.Meter);

/// <summary>
/// Creates a new instance of <see cref="Length"/> object in feet.
/// </summary>
/// <param name="value">The origin value to create the <see cref="Length"/> with</param>
/// <returns></returns>
/// <exception cref="TypeValidationException"></exception>
public static Length FromFeet(QuantityValue value)
=> From(value, LengthTypeUnit.Foot);

/// <summary>
/// Represents a Nox <see cref="Length"/> type and value object.
/// Creates a new instance of <see cref="Length"/> object in meters.
/// </summary>
/// <remarks>Placeholder, needs to be implemented</remarks>
public sealed class Length : ValueObject<QuantityValue, Length>
/// <param name="value">The value to create the <see cref="Length"/> with</param>
/// <returns></returns>
/// <exception cref="TypeValidationException"></exception>
public new static Length From(QuantityValue value)
=> From(value, LengthTypeUnit.Meter);

/// <summary>
/// Creates a new instance of <see cref="Length"/> object with the specified <see cref="LengthTypeUnit"/>.
/// </summary>
/// <param name="value">The value to create the <see cref="Length"/> with</param>
/// <param name="unit">The <see cref="LengthTypeUnit"/> to create the <see cref="Length"/> with</param>
/// <returns></returns>
/// <exception cref="TypeValidationException"></exception>
public static Length From(QuantityValue value, LengthTypeUnit unit)
{
var newObject = new Length
{
Value = Round(value),
Unit = unit,
};

var validationResult = newObject.Validate();

if (!validationResult.IsValid)
{
throw new TypeValidationException(validationResult.Errors);
}

return newObject;
}

/// <summary>
/// Validates a <see cref="Length"/> object.
/// </summary>
/// <returns>true if the <see cref="Length"/> value is valid.</returns>
internal override ValidationResult Validate()
{
var result = Value.Validate();

if (Value < 0 && !double.IsNaN((double)Value) && !double.IsInfinity((double)Value))
rochar marked this conversation as resolved.
Show resolved Hide resolved
{
result.Errors.Add(new ValidationFailure(nameof(Value), $"Could not create a Nox Length type as negative length value {Value} is not allowed."));
}

if (!Enum.IsDefined(typeof(LengthTypeUnit), Unit))
{
result.Errors.Add(new ValidationFailure(nameof(Unit), $"Could not create a Nox Length type as unit {Unit} is not supported."));
}

return result;
}

protected override IEnumerable<KeyValuePair<string, object>> GetEqualityComponents()
{
yield return new KeyValuePair<string, object>(nameof(Value), ToMeters());
}

public override string ToString()
rochar marked this conversation as resolved.
Show resolved Hide resolved
=> $"{Value.ToString($"0.{new string('#', QuantityValueDecimalPrecision)}", CultureInfo.InvariantCulture)} {Unit.ToSymbol()}";

/// <summary>
/// Returns a string representation of the <see cref="Length"/> object using the specified <see cref="IFormatProvider"/>.
/// </summary>
/// <param name="formatProvider">The format provider for the length value.</param>
/// <returns>A string representation of the <see cref="Length"/> object with the value formatted using the specified <see cref="IFormatProvider"/>.</returns>
public string ToString(IFormatProvider formatProvider)
=> $"{Value.ToString(formatProvider)} {Unit.ToSymbol()}";

private QuantityValue? _meters;

public QuantityValue ToMeters()
=> (_meters ??= GetLengthIn(LengthTypeUnit.Meter));

private QuantityValue? _feet;

public QuantityValue ToFeet()
=> (_feet ??= GetLengthIn(LengthTypeUnit.Foot));

private QuantityValue GetLengthIn(LengthTypeUnit targetUnit)
{
var factor = new MeasurementConversionFactor((MeasurementTypeUnit)Unit, (MeasurementTypeUnit)targetUnit).Value;
return Round(Value * factor);
}

private static QuantityValue Round(QuantityValue value)
=> Math.Round((double)value, QuantityValueDecimalPrecision);
}
22 changes: 22 additions & 0 deletions src/Nox.Types/Types/Length/LengthTypeUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace Nox.Types;

public enum LengthTypeUnit
{
Foot = 1,
Meter = 2,
}

public static class LengthTypeUnitExtensions
{
public static string ToSymbol(this LengthTypeUnit unit)
{
return unit switch
{
LengthTypeUnit.Foot => "ft",
LengthTypeUnit.Meter => "m",
_ => throw new NotImplementedException($"No symbol defined for unit {unit}.")
};
}
}
Loading