Skip to content

Commit

Permalink
Implement merging
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-tengler committed Nov 17, 2024
1 parent 8004643 commit 27c3631
Show file tree
Hide file tree
Showing 6 changed files with 1,623 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using HotChocolate.Language;
using HotChocolate.Skimmed;
using static HotChocolate.Fusion.Composition.MergeExtensions;

Expand Down Expand Up @@ -42,6 +43,118 @@ public static OutputFieldDefinition CreateField(
return target;
}

private static List<int> GetSemanticNonNullLevels(IDirectivesProvider provider)
{
var directive = provider.Directives
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);

if (directive is null)
{
return [];
}

if (directive.Arguments.TryGetValue(BuiltIns.SemanticNonNull.Levels, out var levelsArg))
{
if (levelsArg is ListValueNode listValueNode)
{
return listValueNode.Items.Cast<IntValueNode>().Select(i => i.ToInt32()).ToList();
}
}

return [0];
}

private static void MergeSemanticNonNullability(
this CompositionContext context,
OutputFieldDefinition source,
OutputFieldDefinition target)
{
var sourceSemanticNonNullLevels = GetSemanticNonNullLevels(source);
var targetSemanticNonNullLevels = GetSemanticNonNullLevels(target);

if (sourceSemanticNonNullLevels.Count < 1 && targetSemanticNonNullLevels.Count < 1)
{
return;
}

List<int> levels = [];

var currentLevel = 0;
var currentSourceType = source.Type;
var currentTargetType = target.Type;
while (true)
{
if (currentTargetType is NonNullTypeDefinition targetNonNullType)
{
if (currentSourceType is not NonNullTypeDefinition)
{
if (sourceSemanticNonNullLevels.Contains(currentLevel))
{
levels.Add(currentLevel);
}
}

currentTargetType = targetNonNullType.NullableType;
}
else if (targetSemanticNonNullLevels.Contains(currentLevel))
{
if (currentSourceType is NonNullTypeDefinition || sourceSemanticNonNullLevels.Contains(currentLevel))
{
levels.Add(currentLevel);
}
}

if (currentSourceType is NonNullTypeDefinition sourceNonNullType)
{
currentSourceType = sourceNonNullType.NullableType;
}

if (currentTargetType is ListTypeDefinition targetListType)
{
currentTargetType = targetListType.ElementType;

if (currentSourceType is ListTypeDefinition sourceListType)
{
currentSourceType = sourceListType.ElementType;
}

currentLevel++;

continue;
}

break;
}

var targetSemanticNonNullDirective = target.Directives
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);

if (targetSemanticNonNullDirective is not null)
{
target.Directives.Remove(targetSemanticNonNullDirective);
}

if (levels.Count < 1)
{
return;
}

if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(BuiltIns.SemanticNonNull.Name,
out var semanticNonNullDirectiveDefinition))
{
if (levels is [0])
{
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition));
}
else
{
var levelsValueNode = new ListValueNode(levels.Select(l => new IntValueNode(l)).ToList());
var levelsArgument = new ArgumentAssignment(BuiltIns.SemanticNonNull.Levels, levelsValueNode);
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition, levelsArgument));
}
}
}

// This extension method merges two OutputFields by copying over their descriptions, deprecation reasons,
// and arguments (if they have the same name and type). It also logs errors if the arguments have different
// names or if the number of arguments does not match.
Expand All @@ -64,6 +177,8 @@ public static void MergeField(
return;
}

MergeSemanticNonNullability(context, source, target);

if (!mergedType.Equals(target.Type, TypeComparison.Structural))
{
target.Type = mergedType;
Expand Down Expand Up @@ -101,7 +216,7 @@ public static void MergeField(
return;
}

if(!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
if (!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
{
targetArgument.Type = mergedInputType;
}
Expand All @@ -128,7 +243,7 @@ public static void MergeField(
// If the target field is not deprecated and the source field is deprecated, copy over the
target.MergeDeprecationWith(source);

target.MergeDirectivesWith(source, context);
target.MergeDirectivesWith(source, context, shouldApplySemanticNonNull: false);

foreach (var sourceArgument in source.Arguments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ internal static void MergeDescriptionWith(this INamedTypeDefinition target, INam
internal static void MergeDirectivesWith(
this IDirectivesProvider target,
IDirectivesProvider source,
CompositionContext context)
CompositionContext context,
bool shouldApplySemanticNonNull = true)
{
foreach (var directive in source.Directives)
{
Expand All @@ -181,6 +182,11 @@ internal static void MergeDirectivesWith(
continue;
}

if (directive.Name == BuiltIns.SemanticNonNull.Name && !shouldApplySemanticNonNull)
{
continue;
}

context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition);

if (!target.Directives.ContainsName(directive.Name))
Expand Down
Loading

0 comments on commit 27c3631

Please sign in to comment.