Skip to content

Commit

Permalink
feat(ProvideAttribute): use source generation
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ProvideAttribute now uses source generation and requires a partial class.
  • Loading branch information
jonisavo committed Oct 17, 2022
1 parent 0f79723 commit eed6208
Show file tree
Hide file tree
Showing 19 changed files with 638 additions and 57 deletions.
8 changes: 8 additions & 0 deletions Assets/UIComponents.Tests/DependencyInjectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public void Returns_Desired_Dependency()
Assert.That(injector.Provide<IDependency>(), Is.InstanceOf<DependencyOne>());
}

[Test]
public void Returns_Desired_Dependency_With_Non_Generic_Method()
{
var injector = new DependencyInjector(DiContext.Current.Container);
injector.SetDependency<IDependency>(new DependencyOne());
Assert.That(injector.Provide(typeof(IDependency)), Is.InstanceOf<DependencyOne>());
}

[Test]
public void Throws_If_No_Provider_Exists()
{
Expand Down
16 changes: 8 additions & 8 deletions Assets/UIComponents.Tests/ProvideAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace UIComponents.Tests
{
[TestFixture]
public class ProvideAttributeTests
public partial class ProvideAttributeTests
{
private interface IStringProperty
{
Expand All @@ -29,14 +29,14 @@ private class FloatClass : IFloatProperty

[Dependency(typeof(IStringProperty), provide: typeof(StringClass))]
[Dependency(typeof(IFloatProperty), provide: typeof(FloatClass))]
private class ComponentWithDependencies : UIComponent
private partial class ComponentWithDependencies : UIComponent
{
[Provide]
public readonly IStringProperty StringProperty;
public IStringProperty StringProperty;
[Provide]
public readonly IFloatProperty FloatProperty;
public IFloatProperty FloatProperty;
[Provide(CastFrom = typeof(IFloatProperty))]
public readonly FloatClass FloatClassInstance;
public FloatClass FloatClassInstance;
}

private TestBed _testBed;
Expand Down Expand Up @@ -67,13 +67,13 @@ public void Allows_Providing_Dependencies_With_A_Cast()
}

[Dependency(typeof(IFloatProperty), provide: typeof(FloatClass))]
private class ComponentWithInvalidDependency : UIComponent
private partial class ComponentWithInvalidDependency : UIComponent
{
[Provide]
public readonly IStringProperty StringProperty;
public IStringProperty StringProperty;

[Provide(CastFrom = typeof(IFloatProperty))]
public readonly StringClass StringClassInstance;
public StringClass StringClassInstance;
}

[Test]
Expand Down
2 changes: 1 addition & 1 deletion Assets/UIComponents/Core/AssetPathAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace UIComponents
/// [AssetPath("Components/MyComponent/")]
/// [Layout("MyComponent")] // resolves to Components/MyComponent/MyComponent(.uxml)
/// [Stylesheet("MyComponent.style")] // resolves to Components/MyComponent/MyComponent.style(.uss)
/// public class MyComponent : UIComponent {}
/// public partial class MyComponent : UIComponent {}
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
Expand Down
16 changes: 9 additions & 7 deletions Assets/UIComponents/Core/ProvideAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using JetBrains.Annotations;
using System.Diagnostics;
using UnityEngine.TestTools;

namespace UIComponents
{
Expand All @@ -12,21 +13,22 @@ namespace UIComponents
/// <example>
/// [Dependency(typeof(ILogger), provide: typeof(MyLogger)]
/// [Dependency(typeof(IDataService), provide: typeof(DataService)]
/// public class ComponentWithDependencies : UIComponent
/// public partial class ComponentWithDependencies : UIComponent
/// {
/// [Provide]
/// private readonly ILogger Logger;
/// private ILogger _logger;
///
/// [Provide]
/// private readonly IDataService DataService;
/// private IDataService _dataService;
///
/// [Provide(CastFrom = typeof(IDataService))]
/// private readonly DataService CastDataService;
/// private DataService _castDataService;
/// }
/// </example>
/// <seealso cref="DependencyAttribute"/>
[AttributeUsage(AttributeTargets.Field)]
[MeansImplicitUse]
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[Conditional("UICOMPONENTS_INCLUDE_ATTRIBUTES")]
[ExcludeFromCoverage]
public sealed class ProvideAttribute : Attribute
{
/// <summary>
Expand Down
10 changes: 5 additions & 5 deletions Assets/UIComponents/Core/QueryAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ namespace UIComponents
/// </summary>
/// <example>
/// [Layout("MyLayout")]
/// public class ComponentWithQueries : UIComponent
/// public partial class ComponentWithQueries : UIComponent
/// {
/// [Query(Name = "hello-world-label")]
/// private readonly Label HelloWorldLabel;
/// private Label HelloWorldLabel;
///
/// [Query(Class = "red")]
/// private readonly Label[] RedLabels;
/// private Label[] RedLabels;
///
/// [Query]
/// private readonly Label FirstLabel;
/// private Label FirstLabel;
///
/// [Query]
/// public readonly Label[] AllLabels;
/// public Label[] AllLabels;
/// }
/// </example>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
Expand Down
36 changes: 1 addition & 35 deletions Assets/UIComponents/Core/UIComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,40 +296,6 @@ private async Task<List<StyleSheet>> GetStyleSheets()

protected virtual void PopulateQueryFields() {}

protected virtual void PopulateProvideFields()
{
var fieldCache = CacheDictionary[_componentType].FieldCache;
var provideAttributeDictionary = fieldCache.ProvideAttributes;

foreach (var fieldInfo in provideAttributeDictionary.Keys)
{
var fieldType = fieldInfo.FieldType;

if (provideAttributeDictionary[fieldInfo].CastFrom != null)
fieldType = provideAttributeDictionary[fieldInfo].CastFrom;

object value;

try
{
value = _dependencyInjector.Provide(fieldType);

if (provideAttributeDictionary[fieldInfo].CastFrom != null)
value = Convert.ChangeType(value, fieldInfo.FieldType);
}
catch (MissingProviderException)
{
Logger.LogError($"Could not provide {fieldInfo.FieldType.Name} to {fieldInfo.Name}", this);
continue;
}
catch (InvalidCastException)
{
Logger.LogError($"Could not cast {fieldType.Name} to {fieldInfo.FieldType.Name}", this);
continue;
}

fieldInfo.SetValue(this, value);
}
}
protected virtual void PopulateProvideFields() {}
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using UIComponents.Roslyn.Generation.Generators.DependencyInjection;
using UIComponents.Roslyn.Generation.Tests.Utilities;

namespace UIComponents.Roslyn.Generation.Tests
{
[UsesVerify]
public class ProvideAugmentGeneratorSnapshotTests
{
[Fact]
public Task It_Generates_Provide_Calls()
{
var source = @"
using UIComponents;
public interface IDependency {}
public class Dependency : IDependency {}
public partial class BasicProvideComponent : UIComponent
{
[Provide]
public IDependency Dependency;
[Provide(CastFrom = typeof(IDependency))]
public Dependency ConcreteDependency;
}
";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Can_Be_Used_On_Non_UIComponent_Class()
{
var source = @"
using UIComponents;
public interface IDependency {}
public class Dependency : IDependency {}
public partial class MyClass
{
[Provide]
public IDependency Dependency;
[Provide(CastFrom = typeof(IDependency))]
public Dependency ConcreteDependency;
}
";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Handles_Subclasses()
{
var source = @"
using UIComponents;
public interface IDependency {}
public interface IAnotherDependency {}
public class Dependency : IDependency {}
public class AnotherDependency : IAnotherDependency {}
public partial class BaseProvideComponent : UIComponent
{
[Provide]
public IDependency Dependency;
[Provide(CastFrom = typeof(IDependency))]
public Dependency ConcreteDependency;
}
public partial class SubclassProvideComponent : BaseProvideComponent
{
[Provide]
public IAnotherDependency AnotherDependency;
}
public partial class SecondSubclassProvideComponent : SubclassProvideComponent
{
[Provide(CastFrom = typeof(IAnotherDependency))]
public AnotherDependency ThirdDependency;
}";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Handles_Namespaces_And_Nested_Types()
{
var source = @"
using UIComponents;
namespace Dependencies
{
public interface IDependency {}
public class Dependency : IDependency {}
}
namespace Components
{
public partial class ParentClass
{
public interface IOtherDependency {}
private partial class NestedProvideComponent : UIComponent
{
[Provide]
public Dependencies.IDependency Dependency;
[Provide(CastFrom = typeof(IDependency))]
public Dependencies.Dependency ConcreteDependency;
[Provide]
public IOtherDependency OtherDependency;
}
}
}";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Does_Not_Generate_For_Non_Interface_Or_Class_Fields()
{
var source = @"
using UIComponents;
public interface IDependency {}
public partial class InvalidProvideComponent : UIComponent
{
[Provide]
public IDependency Dependency;
[Provide]
public int InvalidIntDependency;
[Provide]
public float InvalidFloatDependency;
}
";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Does_Not_Generate_If_There_Are_No_Provide_Fields()
{
var source = @"
using UIComponents;
public interface IDependency {}
public partial class NoProvideComponent : UIComponent
{
public IDependency Dependency;
}
";
return GeneratorTester.Verify<ProvideAugmentGenerator>(source);
}

[Fact]
public Task It_Does_Not_Generate_If_ProvideAttribute_Type_Is_Missing()
{
var source = @"
namespace UIComponents
{
public class UIComponent {}
public class AssetPathAttribute {}
}
public interface IDependency {}
public partial class MissingProvideComponent : UIComponents.UIComponent
{
[UIComponents.Provide]
public IDependency Dependency;
}
";
return GeneratorTester.VerifyWithoutReferences<ProvideAugmentGenerator>(source);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Diagnostics.CodeAnalysis;

namespace UIComponents
{
[ExcludeFromCodeCoverage]
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class ProvideAttribute : Attribute
{
public Type CastFrom { get; set; }
}
}
Loading

0 comments on commit eed6208

Please sign in to comment.