Skip to content

Commit

Permalink
Implement injectable converters (#39)
Browse files Browse the repository at this point in the history
implemented injectable type converters
  • Loading branch information
fw2568 authored Apr 27, 2021
1 parent e8f6222 commit 75883e6
Show file tree
Hide file tree
Showing 28 changed files with 1,229 additions and 162 deletions.
2 changes: 2 additions & 0 deletions YaNco.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=abap/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
8 changes: 7 additions & 1 deletion src/YaNco.Abstractions/IRfcRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Dbosoft.YaNco;
using LanguageExt;

Expand Down Expand Up @@ -46,5 +47,10 @@ public interface IRfcRuntime
Either<RfcErrorInfo, string> GetTimeString(IDataContainerHandle containerHandle, string name);

Option<ILogger> Logger { get; }

Either<RfcErrorInfo, Unit> SetFieldValue<T>(IDataContainerHandle handle, T value, Func<Either<RfcErrorInfo, RfcFieldInfo>> func);
Either<RfcErrorInfo, T> GetFieldValue<T>(IDataContainerHandle handle, Func<Either<RfcErrorInfo, RfcFieldInfo>> func);
}


}
16 changes: 16 additions & 0 deletions src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Dbosoft.YaNco.TypeMapping
{
public class FieldMappingContext
{
public readonly IRfcRuntime RfcRuntime;
public readonly IDataContainerHandle Handle;
public readonly RfcFieldInfo FieldInfo;

public FieldMappingContext(IRfcRuntime rfcRuntime, IDataContainerHandle handle, RfcFieldInfo fieldInfo)
{
RfcRuntime = rfcRuntime;
Handle = handle;
FieldInfo = fieldInfo;
}
}
}
11 changes: 11 additions & 0 deletions src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using LanguageExt;

namespace Dbosoft.YaNco.TypeMapping
{
public interface IFieldMapper
{
Either<RfcErrorInfo, Unit> SetField<T>(T value, FieldMappingContext context);
Either<RfcErrorInfo, T> GetField<T>(FieldMappingContext context);

}
}
11 changes: 11 additions & 0 deletions src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using LanguageExt;

namespace Dbosoft.YaNco.TypeMapping
{
public interface IFromAbapValueConverter<T>
{
Try<T> ConvertTo(AbapValue abapValue);
bool CanConvertTo(RfcType rfcType);

}
}
11 changes: 11 additions & 0 deletions src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;

namespace Dbosoft.YaNco.TypeMapping
{
public interface IRfcConverterResolver
{
IEnumerable<IToAbapValueConverter<T>> GetToRfcConverters<T>(RfcType rfcType);
IEnumerable<IFromAbapValueConverter<T>> GetFromRfcConverters<T>(RfcType rfcType, Type abapValueType);
}
}
11 changes: 11 additions & 0 deletions src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using LanguageExt;

namespace Dbosoft.YaNco.TypeMapping
{
public interface IToAbapValueConverter<T>
{
Try<AbapValue> ConvertFrom(T value, RfcFieldInfo fieldInfo);
bool CanConvertFrom(RfcType rfcType);

}
}
2 changes: 1 addition & 1 deletion src/YaNco.Abstractions/YaNco.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This package contains abstraction definitions.
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dbosoft.Functional" Version="2.0.0-beta.1" />
<PackageReference Include="Dbosoft.Functional" Version="2.0.1" />
<PackageReference Include="GitVersionTask" Version="5.3.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand Down
155 changes: 3 additions & 152 deletions src/YaNco.Core/DataContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,162 +17,14 @@ protected DataContainer(IDataContainerHandle handle, IRfcRuntime rfcRuntime)

public Either<RfcErrorInfo, Unit> SetField<T>(string name, T value)
{
switch (value)
{
case int intValue:
return _rfcRuntime.SetInt(_handle, name, intValue);
case long longValue:
return _rfcRuntime.SetLong(_handle, name, longValue);
case DateTime dateTime:
return GetFieldInfo(name)
.Bind(typeDesc =>
{
switch (typeDesc.Type)
{
case RfcType.DATE:
return SetFieldAsDate(name, dateTime);
case RfcType.TIME:
return SetFieldAsTime(name, dateTime);
default:
return _rfcRuntime.SetString(_handle, name,
(string)Convert.ChangeType(value, typeof(string)));
}
});

default:
{
return _rfcRuntime.SetString(_handle, name,
(string)Convert.ChangeType(value, typeof(string)));

}
}
return _rfcRuntime.SetFieldValue<T>(_handle, value, () => GetFieldInfo(name));
}

protected abstract Either<RfcErrorInfo, RfcFieldInfo> GetFieldInfo(string name);

public Either<RfcErrorInfo, T> GetField<T>(string name)
{
return GetFieldInfo(name)
.Bind(typeDesc =>
{
switch (typeDesc.Type)
{
case RfcType.BYTE:
return GetFieldAsInt<T>(name);
case RfcType.NUM:
return GetFieldAsInt<T>(name);
case RfcType.INT:
return GetFieldAsInt<T>(name);
case RfcType.INT2:
return GetFieldAsInt<T>(name);
case RfcType.INT1:
return GetFieldAsInt<T>(name);
case RfcType.INT8:
return GetFieldAsLong<T>(name);
case RfcType.DATE:
return GetFieldAsDate<T>(name);
case RfcType.TIME:
return GetFieldAsTime<T>(name);
default:
return GetFieldAsString<T>(name);
}
});
}

private Either<RfcErrorInfo, T> GetFieldAsString<T>(string name)
{
return _rfcRuntime.GetString(_handle, name).Map(r =>
{
object value = r;

if (typeof(T) == typeof(bool))
{
value = !string.IsNullOrWhiteSpace(r);
}

return (T)Convert.ChangeType(value, typeof(T));
});
}

private Either<RfcErrorInfo, Unit> SetFieldAsDate(string name, DateTime value)
{
var dateString = value.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
return _rfcRuntime.SetDateString(_handle, name, dateString);
}

private Either<RfcErrorInfo, Unit> SetFieldAsTime(string name, DateTime value)
{
var dateString = value.ToString("HHmmss", CultureInfo.InvariantCulture);
return _rfcRuntime.SetDateString(_handle, name, dateString);
}

private Either<RfcErrorInfo, T> GetFieldAsInt<T>(string name)
{
return _rfcRuntime.GetInt(_handle, name).Map(r =>
{
object value = r;

if (typeof(T) == typeof(bool))
{
value = r != 0;
}

return (T)Convert.ChangeType(value, typeof(T));
});
}

private Either<RfcErrorInfo, T> GetFieldAsLong<T>(string name)
{
return _rfcRuntime.GetLong(_handle, name).Map(r =>
{
object value = r;

if (typeof(T) == typeof(bool))
{
value = r != 0;
}

return (T)Convert.ChangeType(value, typeof(T));
});
}

private Either<RfcErrorInfo, T> GetFieldAsDate<T>(string name)
{
return _rfcRuntime.GetDateString(_handle, name).Map(r =>
{
object value;
if (typeof(T) == typeof(string))
value = r;
else
{
if (r == "00000000" || r == string.Empty)
value = DateTime.MinValue;
else
value = DateTime.ParseExact(r, "yyyyMMdd", CultureInfo.InvariantCulture);
}


return (T)Convert.ChangeType(value, typeof(T));
});
}

private Either<RfcErrorInfo, T> GetFieldAsTime<T>(string name)
{
return _rfcRuntime.GetDateString(_handle, name).Map(r =>
{
object value;
if (typeof(T) == typeof(string))
value = r;
else
{
if (r == "000000" || r == string.Empty)
value = DateTime.MinValue;
else
value = DateTime.ParseExact(r, "HHmmss", CultureInfo.InvariantCulture);
}

return (T)Convert.ChangeType(value, typeof(T));
});
return _rfcRuntime.GetFieldValue<T>(_handle,() => GetFieldInfo(name));
}

public Either<RfcErrorInfo, Unit> SetFieldBytes(string name, byte[] buffer, long bufferLength)
Expand All @@ -195,8 +47,7 @@ public Either<RfcErrorInfo, ITable> GetTable(string name)
{
return _rfcRuntime.GetTable(_handle, name).Map(handle => (ITable) new Table(handle, _rfcRuntime));
}



protected virtual void Dispose(bool disposing)
{
if (disposing)
Expand Down
4 changes: 2 additions & 2 deletions src/YaNco.Core/Internal/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,13 @@ public static RfcRc GetBytes(IDataContainerHandle containerHandle, string name,
if (rc != RfcRc.RFC_BUFFER_TOO_SMALL)
{
buffer = new byte[bufferLength];
tempBuffer.CopyTo(buffer, 0);
Array.Copy(tempBuffer, buffer, bufferLength);

return rc;
}

tempBuffer = new byte[bufferLength];
rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, 255, out _, out errorInfo);
rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, bufferLength, out _, out errorInfo);
buffer = new byte[bufferLength];
tempBuffer.CopyTo(buffer, 0);

Expand Down
36 changes: 34 additions & 2 deletions src/YaNco.Core/RfcRuntime.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Dbosoft.YaNco.Internal;
using Dbosoft.YaNco.TypeMapping;
using LanguageExt;
using FieldMappingContext = Dbosoft.YaNco.TypeMapping.FieldMappingContext;

// ReSharper disable UnusedMember.Global

namespace Dbosoft.YaNco
{
public class RfcRuntime : IRfcRuntime
{
public RfcRuntime(ILogger logger = null)
private readonly IFieldMapper _fieldMapper;

public RfcRuntime(ILogger logger = null, IFieldMapper fieldMapper = null)
{
Logger = logger == null ? Option<ILogger>.None : Option<ILogger>.Some(logger);
_fieldMapper = fieldMapper ??
new DefaultFieldMapper(
new CachingConverterResolver(
DefaultConverterResolver.CreateWithBuildInConverters()));

}

private Either<RfcErrorInfo, TResult> ResultOrError<TResult>(TResult result, RfcErrorInfo errorInfo, bool logAsError = false)
Expand Down Expand Up @@ -292,6 +303,27 @@ public Either<RfcErrorInfo, string> GetTimeString(IDataContainerHandle container

public Option<ILogger> Logger { get; }

public Either<RfcErrorInfo, Unit> SetFieldValue<T>(IDataContainerHandle handle, T value, Func<Either<RfcErrorInfo, RfcFieldInfo>> func)
{
return func().Bind(fieldInfo =>
{
Logger.IfSome(l => l.LogTrace("setting field value", new { handle, fieldInfo, SourceType= typeof(T) }));
return _fieldMapper.SetField(value, new FieldMappingContext(this, handle, fieldInfo));
});

}

public Either<RfcErrorInfo, T> GetFieldValue<T>(IDataContainerHandle handle, Func<Either<RfcErrorInfo, RfcFieldInfo>> func)
{
return func().Bind(fieldInfo =>
{
Logger.IfSome(l => l.LogTrace("reading field value", new { handle, fieldInfo, TargetType = typeof(T) }));
return _fieldMapper.GetField<T>(new FieldMappingContext(this, handle, fieldInfo));
});


}

public Either<RfcErrorInfo, Unit> SetInt(IDataContainerHandle containerHandle, string name, int value)
{
Logger.IfSome(l => l.LogTrace("setting int value by name", new { containerHandle, name, value }));
Expand Down
49 changes: 49 additions & 0 deletions src/YaNco.Core/TypeMapping/ByteValueConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using LanguageExt;

namespace Dbosoft.YaNco.TypeMapping
{
public class ByteValueConverter: IToAbapValueConverter<byte[]>, IFromAbapValueConverter<byte[]>
{
public Try<AbapValue> ConvertFrom(byte[] value, RfcFieldInfo fieldInfo)
{
return Prelude.Try<AbapValue>(() =>
{
if (!IsSupportedRfcType(fieldInfo.Type))
throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to byte array.");

return new AbapByteValue(fieldInfo, value);
});

}

public bool CanConvertFrom(RfcType rfcType)
{
return IsSupportedRfcType(rfcType);
}

public Try<byte[]> ConvertTo(AbapValue abapValue)
{
return Prelude.Try(() => (abapValue as AbapByteValue)?.Value);
}

public bool CanConvertTo(RfcType rfcType)
{
return IsSupportedRfcType(rfcType);
}

private bool IsSupportedRfcType(RfcType rfcType)
{
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
switch (rfcType)
{
case RfcType.BYTE:
case RfcType.XSTRING:
return true;
default:
return false;
}

}
}
}
Loading

0 comments on commit 75883e6

Please sign in to comment.