Skip to content

Commit

Permalink
Merge pull request #18 from theowiik/get-node-unique
Browse files Browse the repository at this point in the history
Add GetUniqueNode attribute
  • Loading branch information
theowiik authored Nov 16, 2023
2 parents bb0892b + f2ffe4f commit 73f456b
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 50 deletions.
3 changes: 3 additions & 0 deletions GodotSharper/AutoGetNode/AutoGetNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ private static void WireMembers<T>(Node node, IEnumerable<T> members)
where T : MemberInfo
{
foreach (var member in members)
{
member.GetCustomAttribute<GetNodeAttribute>()?.SetNode(member, node);
member.GetCustomAttribute<GetUniqueNodeAttribute>()?.SetNode(member, node);
}
}
}
61 changes: 61 additions & 0 deletions GodotSharper/AutoGetNode/BaseGetNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Reflection;
using Godot;
using GodotSharper.Exceptions;

namespace GodotSharper.AutoGetNode;

public abstract class BaseGetNode : Attribute
{
private readonly string _nodeIdentifier;

protected BaseGetNode(string nodeIdentifier)
{
_nodeIdentifier = nodeIdentifier;
}

/// <summary>
/// Sets the node specified by the attribute on the given member of the provided node.
/// </summary>
/// <param name="memberInfo">The member to set the node on.</param>
/// <param name="node">The node to get the child node from.</param>
/// <exception cref="NodeNotFoundException">Thrown if the child node cannot be found.</exception>
/// <exception cref="ArgumentException">Thrown if the child node is not of the expected type.</exception>
public void SetNode(MemberInfo memberInfo, Node node)
{
var childNode = GetNode(node, _nodeIdentifier);

if (childNode == null)
{
node.GetTree().Quit();
throw new NodeNotFoundException($"Cannot find Node for NodePath '{_nodeIdentifier}'");
}

var expectedType = memberInfo is FieldInfo fieldInfo
? fieldInfo.FieldType
: ((PropertyInfo)memberInfo).PropertyType;

if (childNode.GetType() != expectedType && !childNode.GetType().IsSubclassOf(expectedType))
{
node.GetTree().Quit();
throw new ArgumentException(
$"Node is not a valid type. Expected {expectedType} got {childNode.GetType()}"
);
}

switch (memberInfo)
{
case FieldInfo fieldInformation:
fieldInformation.SetValue(node, childNode);
break;
case PropertyInfo propertyInformation:
propertyInformation.SetValue(node, childNode);
break;
default:
throw new ArgumentException(
$"MemberInfo is not a valid type. Expected {nameof(FieldInfo)} or {nameof(PropertyInfo)} got {memberInfo.GetType()}"
);
}
}

protected abstract Node GetNode(Node node, string path);
}
56 changes: 6 additions & 50 deletions GodotSharper/AutoGetNode/GetNodeAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,63 +1,19 @@
using System.Reflection;
using Godot;
using GodotSharper.Exceptions;
using Godot;

namespace GodotSharper.AutoGetNode;

/// <summary>
/// Attribute used to automatically get a node from the scene tree.
/// Attribute used to automatically get a node from the scene tree via the path to the Node.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class GetNodeAttribute : Attribute
public sealed class GetNodeAttribute : BaseGetNode
{
private readonly string _path;

public GetNodeAttribute(string nodePath)
public GetNodeAttribute(string nodeNodeIdentifier) : base(nodeNodeIdentifier)
{
_path = nodePath;
}

/// <summary>
/// Sets the node specified by the attribute on the given member of the provided node.
/// </summary>
/// <param name="memberInfo">The member to set the node on.</param>
/// <param name="node">The node to get the child node from.</param>
/// <exception cref="NodeNotFoundException">Thrown if the child node cannot be found.</exception>
/// <exception cref="ArgumentException">Thrown if the child node is not of the expected type.</exception>
public void SetNode(MemberInfo memberInfo, Node node)
protected override Node GetNode(Node node, string path)
{
var childNode = node.GetNodeOrNull(_path);

if (childNode == null)
{
node.GetTree().Quit();
throw new NodeNotFoundException($"Cannot find Node for NodePath '{_path}'");
}

var expectedType = memberInfo is FieldInfo fieldInfo
? fieldInfo.FieldType
: ((PropertyInfo)memberInfo).PropertyType;

if (childNode.GetType() != expectedType && !childNode.GetType().IsSubclassOf(expectedType))
{
node.GetTree().Quit();
throw new ArgumentException(
$"Node is not a valid type. Expected {expectedType} got {childNode.GetType()}"
);
}

switch (memberInfo)
{
case FieldInfo fieldInformation:
fieldInformation.SetValue(node, childNode);
break;
case PropertyInfo propertyInformation:
propertyInformation.SetValue(node, childNode);
break;
default:
throw new ArgumentException(
$"MemberInfo is not a valid type. Expected {nameof(FieldInfo)} or {nameof(PropertyInfo)} got {memberInfo.GetType()}"
);
}
return node.GetNodeOrNull(path);
}
}
40 changes: 40 additions & 0 deletions GodotSharper/AutoGetNode/GetUniqueNodeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Godot;

namespace GodotSharper.AutoGetNode;

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class GetUniqueNodeAttribute : BaseGetNode
{
public GetUniqueNodeAttribute(string path) : base(path)
{
}

/// <summary>
/// Recursively searches for a child node with the given path, starting from the given node.
/// </summary>
/// <param name="node">The node to start the search from.</param>
/// <param name="path">The path of the child node to search for.</param>
/// <returns>The child node with the given path, or null if it was not found.</returns>
protected override Node GetNode(Node node, string path)
{
var directChild = node.GetNodeOrNull(path);
if (directChild != null)
return directChild;

var children = node.GetChildren();

// Base case
if (!children.Any())
return null;

foreach (var c in children)
{
var childNode = GetNode(c, path);

if (childNode != null)
return childNode;
}

return null;
}
}

0 comments on commit 73f456b

Please sign in to comment.