Skip to content

Commit

Permalink
Reverting and adding [CandidOptional] (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekctek committed Oct 8, 2023
1 parent 63b3799 commit 9bb3b98
Show file tree
Hide file tree
Showing 146 changed files with 201 additions and 59,751 deletions.
22 changes: 13 additions & 9 deletions src/Candid/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
- [ToOptionalObject\`\`1(value)](#M-EdjCase-ICP-Candid-CandidConverter-ToOptionalObject``1-EdjCase-ICP-Candid-Models-Values-CandidOptional- 'EdjCase.ICP.Candid.CandidConverter.ToOptionalObject``1(EdjCase.ICP.Candid.Models.Values.CandidOptional)')
- [CandidConverterOptions](#T-EdjCase-ICP-Candid-CandidConverterOptions 'EdjCase.ICP.Candid.CandidConverterOptions')
- [CustomMappers](#P-EdjCase-ICP-Candid-CandidConverterOptions-CustomMappers 'EdjCase.ICP.Candid.CandidConverterOptions.CustomMappers')
- [UseOptionalValue](#P-EdjCase-ICP-Candid-CandidConverterOptions-UseOptionalValue 'EdjCase.ICP.Candid.CandidConverterOptions.UseOptionalValue')
- [AddCustomMapper(mapper)](#M-EdjCase-ICP-Candid-CandidConverterOptions-AddCustomMapper-EdjCase-ICP-Candid-Mapping-ICandidValueMapper- 'EdjCase.ICP.Candid.CandidConverterOptions.AddCustomMapper(EdjCase.ICP.Candid.Mapping.ICandidValueMapper)')
- [AddCustomMapper\`\`1()](#M-EdjCase-ICP-Candid-CandidConverterOptions-AddCustomMapper``1 'EdjCase.ICP.Candid.CandidConverterOptions.AddCustomMapper``1')
- [CandidDecodingException](#T-EdjCase-ICP-Candid-Exceptions-CandidDecodingException 'EdjCase.ICP.Candid.Exceptions.CandidDecodingException')
Expand Down Expand Up @@ -96,6 +95,7 @@
- [Equals()](#M-EdjCase-ICP-Candid-Models-Values-CandidOptional-Equals-EdjCase-ICP-Candid-Models-Values-CandidValue- 'EdjCase.ICP.Candid.Models.Values.CandidOptional.Equals(EdjCase.ICP.Candid.Models.Values.CandidValue)')
- [GetHashCode()](#M-EdjCase-ICP-Candid-Models-Values-CandidOptional-GetHashCode 'EdjCase.ICP.Candid.Models.Values.CandidOptional.GetHashCode')
- [ToString()](#M-EdjCase-ICP-Candid-Models-Values-CandidOptional-ToString 'EdjCase.ICP.Candid.Models.Values.CandidOptional.ToString')
- [CandidOptionalAttribute](#T-EdjCase-ICP-Candid-Mapping-CandidOptionalAttribute 'EdjCase.ICP.Candid.Mapping.CandidOptionalAttribute')
- [CandidOptionalType](#T-EdjCase-ICP-Candid-Models-Types-CandidOptionalType 'EdjCase.ICP.Candid.Models.Types.CandidOptionalType')
- [#ctor(value,recursiveId)](#M-EdjCase-ICP-Candid-Models-Types-CandidOptionalType-#ctor-EdjCase-ICP-Candid-Models-Types-CandidType,EdjCase-ICP-Candid-Models-CandidId- 'EdjCase.ICP.Candid.Models.Types.CandidOptionalType.#ctor(EdjCase.ICP.Candid.Models.Types.CandidType,EdjCase.ICP.Candid.Models.CandidId)')
- [Type](#P-EdjCase-ICP-Candid-Models-Types-CandidOptionalType-Type 'EdjCase.ICP.Candid.Models.Types.CandidOptionalType.Type')
Expand Down Expand Up @@ -1384,14 +1384,6 @@ Options for configuring how candid is convertered
List of custom mappers to use instead of the default mappers provided.
Order does matter, FIFO

<a name='P-EdjCase-ICP-Candid-CandidConverterOptions-UseOptionalValue'></a>
### UseOptionalValue `property`

##### Summary

If true, will use treat OptionalValue types as opt in candid
otherwise will treat all nullable/class types opt in candid

<a name='M-EdjCase-ICP-Candid-CandidConverterOptions-AddCustomMapper-EdjCase-ICP-Candid-Mapping-ICandidValueMapper-'></a>
### AddCustomMapper(mapper) `method`

Expand Down Expand Up @@ -1918,6 +1910,18 @@ This method has no parameters.

This method has no parameters.

<a name='T-EdjCase-ICP-Candid-Mapping-CandidOptionalAttribute'></a>
## CandidOptionalAttribute `type`

##### Namespace

EdjCase.ICP.Candid.Mapping

##### Summary

An attribute to use the raw nullable value vs OptionalValue type
E.g. OptionalValue of string, can be a string with this attribute

<a name='T-EdjCase-ICP-Candid-Models-Types-CandidOptionalType'></a>
## CandidOptionalType `type`

Expand Down
12 changes: 6 additions & 6 deletions src/Candid/API.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Candid/CandidConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ private ICandidValueMapper ResolveUncached(Type type)

return mapper == null
// Create a dynamic mapper if no custom one
? DefaultMapperFactory.Build(type, this._options.UseOptionalValue)
? DefaultMapperFactory.Build(type)
: mapper;
}

Expand Down
5 changes: 0 additions & 5 deletions src/Candid/CandidConverterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ namespace EdjCase.ICP.Candid
/// </summary>
public class CandidConverterOptions
{
/// <summary>
/// If true, will use treat OptionalValue types as opt in candid
/// otherwise will treat all nullable/class types opt in candid
/// </summary>
public bool UseOptionalValue { get; set; } = true;
/// <summary>
/// List of custom mappers to use instead of the default mappers provided.
/// Order does matter, FIFO
Expand Down
56 changes: 20 additions & 36 deletions src/Candid/Mapping/IResolvableTypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,16 @@ public ComplexTypeInfo(Type objType, List<Type> dependencies, ResolveMapper reso

internal static class DefaultMapperFactory
{
public static ICandidValueMapper Build(Type objType, bool useOptionalValue)
public static ICandidValueMapper Build(Type objType)
{
Dictionary<Type, CandidType> resolvedDependencies = new();

return ResolveDependencies(objType, resolvedDependencies, useOptionalValue);
return ResolveDependencies(objType, resolvedDependencies);
}

private static ICandidValueMapper ResolveDependencies(
Type objType,
Dictionary<Type, CandidType> resolvedDependencies,
bool useOptionalValue
Dictionary<Type, CandidType> resolvedDependencies
)
{
IResolvableTypeInfo info = BuildTypeInfo(objType);
Expand All @@ -94,25 +93,12 @@ bool useOptionalValue
// Skip already resolved dependencies
continue;
}
ResolveDependencies(depType, resolvedDependencies, useOptionalValue);
ResolveDependencies(depType, resolvedDependencies);
}
}

(ICandidValueMapper objectMapper, CandidType type) = info.Resolve(resolvedDependencies);
if (!useOptionalValue)
{
bool isOptionalValue = info.ObjType.IsGenericType
&& info.ObjType.GetGenericTypeDefinition() == typeof(OptionalValue<>);
if (!isOptionalValue && !info.ObjType.IsValueType)
{
// If type is nullable, not OptionalValue<T> and has the
// useOptionalValue as false, then treat each nullable type
// as an opt in candid
objectMapper = new NullableValueMapper(objectMapper);
type = new CandidOptionalType(type);
}
}


resolvedDependencies[objType] = type;
return objectMapper;
}
Expand Down Expand Up @@ -285,7 +271,7 @@ private static IResolvableTypeInfo BuildTypeInfo(Type objType)
}

// Enum variant
if(objType.IsEnum)
if (objType.IsEnum)
{
return BuildEnumVariant(objType);
}
Expand Down Expand Up @@ -420,7 +406,7 @@ Type valueType
{
CandidType keyCandidType = resolvedMappings[keyType];
CandidType valueCandidType = resolvedMappings[valueType];
Dictionary<CandidTag, CandidType> fields = new()
Dictionary<CandidTag, CandidType> fields = new()
{
[0] = keyCandidType,
[1] = valueCandidType
Expand Down Expand Up @@ -492,7 +478,7 @@ private static IResolvableTypeInfo BuildVariant(Type objType, VariantAttribute a
);

var optionTypes = new Dictionary<CandidTag, Type>();
foreach(MethodInfo classMethod in objType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
foreach (MethodInfo classMethod in objType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
{
CandidTag tag;
var optionAttribute = classMethod.GetCustomAttribute<VariantOptionAttribute>();
Expand All @@ -508,12 +494,12 @@ private static IResolvableTypeInfo BuildVariant(Type objType, VariantAttribute a
{
continue;
}

optionTypes.Add(tag, classMethod.ReturnType);
}
foreach(PropertyInfo property in properties)
foreach (PropertyInfo property in properties)
{
if(property == variantTagProperty || property == valueProperty)
if (property == variantTagProperty || property == valueProperty)
{
// Skip tag and value properties
continue;
Expand Down Expand Up @@ -633,11 +619,12 @@ private static IResolvableTypeInfo BuildRecord(Type objType)
{
tag = CandidTag.FromName(property.Name);
}
PropertyMetaData propertyMetaData = new(property, CustomMapper: null); // TODO attribute custom mapper
CandidOptionalAttribute? optionalAttribute = property.GetCustomAttribute<CandidOptionalAttribute>();
bool useOptionalOverride = optionalAttribute != null;
PropertyMetaData propertyMetaData = new(property, useOptionalOverride);
propertyMetaDataMap.Add(tag, propertyMetaData);
}
List<Type> dependencies = propertyMetaDataMap
.Where(p => p.Value.CustomMapper == null) // Only resolve the ones that need to
.Select(p => p.Value.PropertyInfo.PropertyType)
.ToList();
return new ComplexTypeInfo(objType, dependencies, (resolvedMappings) =>
Expand All @@ -647,16 +634,13 @@ private static IResolvableTypeInfo BuildRecord(Type objType)
p => p.Key,
p =>
{
if (p.Value.CustomMapper != null)
CandidType type = resolvedMappings[p.Value.PropertyInfo.PropertyType];
if (p.Value.UseOptionalOverride)
{
CandidType? type = p.Value.CustomMapper!.GetMappedCandidType(p.Value.PropertyInfo.PropertyType);
if (type == null)
{
throw new InvalidOperationException($"Property '{p.Value.PropertyInfo.Name}' given incompatible candid value mapper");
}
return type;
// Property is really optional type
type = new CandidOptionalType(type);
}
return resolvedMappings[p.Value.PropertyInfo.PropertyType];
return type;
}
);
CandidRecordType type = new CandidRecordType(fieldTypes);
Expand All @@ -668,7 +652,7 @@ private static IResolvableTypeInfo BuildRecord(Type objType)
}
internal record PropertyMetaData(
PropertyInfo PropertyInfo,
ICandidValueMapper? CustomMapper
bool UseOptionalOverride
);


Expand Down
10 changes: 10 additions & 0 deletions src/Candid/Mapping/MapperAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ public class CandidIgnoreAttribute : Attribute
{
}

/// <summary>
/// An attribute to use the raw nullable value vs OptionalValue type
/// E.g. OptionalValue of string, can be a string with this attribute
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class CandidOptionalAttribute : Attribute
{

}

/// <summary>
/// An attribute to put on a class to identify it as a variant type for serialization.
/// Requires the use of `VariantTagPropertyAttribute`, `VariantOptionTypeAttribute` and
Expand Down
50 changes: 0 additions & 50 deletions src/Candid/Mapping/Mappers/NullableValueMapper.cs

This file was deleted.

22 changes: 8 additions & 14 deletions src/Candid/Mapping/Mappers/RecordMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public object Map(CandidValue value, CandidConverter converter)
// Record has property that is not in the candid type, skip
continue;
}
if (fieldType is not CandidOptionalType)
if (fieldType is not CandidOptionalType && !property.UseOptionalOverride)
{
// Only throw if fieldType is not an opt value since the value is unset (null)
// or has an extra
Expand All @@ -43,14 +43,7 @@ public object Map(CandidValue value, CandidConverter converter)
// Set to optional value if not specified in record
fieldCandidValue = new CandidOptional(null);
}
if (property.CustomMapper != null)
{
fieldValue = property.CustomMapper.Map(fieldCandidValue, converter);
}
else
{
fieldValue = converter.ToObject(property.PropertyInfo.PropertyType, fieldCandidValue);
}
fieldValue = converter.ToObject(property.PropertyInfo.PropertyType, fieldCandidValue);
property.PropertyInfo.SetValue(obj, fieldValue);
}
return obj;
Expand All @@ -61,19 +54,20 @@ public CandidValue Map(object value, CandidConverter converter)
Dictionary<CandidTag, CandidValue> fields = new();
foreach ((CandidTag tag, PropertyMetaData property) in this.Properties)
{
object propValue = property.PropertyInfo.GetValue(value);
object? propValue = property.PropertyInfo.GetValue(value);
CandidValue v;
if (propValue == null)
{
v = new CandidOptional(null);
}
else if (property.CustomMapper != null)
{
v = property.CustomMapper.Map(propValue, converter);
}
else
{
v = converter.FromObject(propValue);
if (property.UseOptionalOverride)
{
// Wrap in candid optional if has override [CandidOptional]
v = new CandidOptional(v);
}
}
fields.Add(tag, v);
}
Expand Down
Loading

0 comments on commit 9bb3b98

Please sign in to comment.