Prechádzať zdrojové kódy

feat(di): add auto-instantiation support and typed factory delegates

- Add ObjectFactory for automatic type instantiation without explicit factory
- Add TypedFactoryDelegate<T> for type-safe factory registration
- Make factory parameters optional on register methods (defaults to auto-instantiation)
- Add runtime type compatibility checking in resolve_registration_as
- Add INCOMPATIBLE_TYPE error for type mismatches
- Add warning logs for injection failures
- Move FactoryDelegate declaration to DelegateFactory.vala
Billy Barrow 1 týždeň pred
rodič
commit
873e39bf8e

+ 16 - 7
src/Container.vala

@@ -18,7 +18,8 @@ namespace Inversion {
         /**
          * Resolved to a registration with an invalid lifecycle for the lifecycle of the scope
          */
-        ILLEGAL_LIFECYCLE_COMBINATION
+        ILLEGAL_LIFECYCLE_COMBINATION,
+        INCOMPATIBLE_TYPE
     }
 
     /**
@@ -83,24 +84,32 @@ namespace Inversion {
             return register_factory_type(typeof(T), factory, lifecycle);
         }
 
-        public Registration register_type(Type implementation_type, owned FactoryDelegate factory_func, Lifecycle lifecycle) {
+        public Registration register_type(Type implementation_type, owned FactoryDelegate? factory_func, Lifecycle lifecycle) {
+            if(factory_func == null) {
+                var object_factory = new ObjectFactory(implementation_type);
+                return register_factory_type(implementation_type, object_factory, lifecycle);
+            }
             var delegate_factory = new DelegateFactory((owned)factory_func);
             return register_factory_type(implementation_type, delegate_factory, lifecycle);
         }
 
-        public Registration register<T>(owned FactoryDelegate factory_func, Lifecycle lifecycle) {
-            return register_type(typeof(T), (owned)factory_func, lifecycle);
+        public Registration register<T>(owned TypedFactoryDelegate<T>? factory_func, Lifecycle lifecycle) {
+            if(factory_func == null) {
+                return register_type(typeof(T), null, lifecycle);
+            }
+            var delegate_factory = DelegateFactory.typed<T>((owned)factory_func);
+            return register_factory_type(typeof(T), delegate_factory, lifecycle);
         }
 
-        public Registration register_transient<T>(owned FactoryDelegate factory_func) {
+        public Registration register_transient<T>(owned TypedFactoryDelegate<T>? factory_func = null) {
             return register<T>((owned)factory_func, Lifecycle.TRANSIENT);
         }
 
-        public Registration register_scoped<T>(owned FactoryDelegate factory_func) {
+        public Registration register_scoped<T>(owned TypedFactoryDelegate<T>? factory_func = null) {
             return register<T>((owned)factory_func, Lifecycle.SCOPED);
         }
 
-        public Registration register_singleton<T>(owned FactoryDelegate factory_func) {
+        public Registration register_singleton<T>(owned TypedFactoryDelegate<T>? factory_func = null) {
             return register<T>((owned)factory_func, Lifecycle.SINGLETON);
         }
 

+ 8 - 0
src/DelegateFactory.vala

@@ -23,6 +23,10 @@ namespace Inversion {
             this._delegate = (owned)delegate;
         }
 
+        public static DelegateFactory typed<T>(owned TypedFactoryDelegate<T> delegate) {
+            return new DelegateFactory(s => (Object)(delegate(s)));
+        }
+
         /**
          * Creates a new instance using the delegate.
          * 
@@ -36,4 +40,8 @@ namespace Inversion {
 
     }
 
+    public delegate Object FactoryDelegate(Scope scope) throws Error;
+
+    public delegate T TypedFactoryDelegate<T>(Scope scope) throws Error;
+
 }

+ 2 - 0
src/Injection.vala

@@ -151,6 +151,7 @@ namespace Inversion {
             return context.scope.resolve<T>();
         }
         catch(Error e) {
+            warning(@"Error thrown while attempting to inject type $(typeof(T).name()) into $(context.registration.implementation_type.name()): $(e.message)");
             context.error = e;
             return null;
         }
@@ -167,6 +168,7 @@ namespace Inversion {
                 .to_immutable_buffer();
         }
         catch(Error e) {
+            warning(@"Error thrown while attempting to inject all of type $(typeof(T).name()) into $(context.registration.implementation_type.name()): $(e.message)");
             context.error = e;
             return Iterate.nothing<T>().to_immutable_buffer();
         }

+ 39 - 0
src/ObjectFactory.vala

@@ -0,0 +1,39 @@
+namespace Inversion {
+
+    /**
+     * A factory that creates instances using reflection.
+     * 
+     * This factory uses GLib's type system to instantiate objects of the
+     * specified type. The type must be a descendant of Object and have a
+     * default (parameterless) constructor.
+     */
+    public class ObjectFactory : Object, Factory {
+
+        /**
+         * The type of object this factory creates.
+         */
+        public Type instance_type { get; construct; }
+
+        /**
+         * Creates a new object factory for the specified type.
+         * 
+         * @param instance_type The type of object to create (must be a descendant of Object)
+         */
+        public ObjectFactory(Type instance_type) {
+            Object(instance_type: instance_type);
+        }
+
+        /**
+         * Creates a new instance of the configured type.
+         * 
+         * @param scope The current scope (unused for basic object creation)
+         * @return A new instance of the configured type
+         * @throws Error if instance creation fails
+         */
+        public Object create(Scope scope) throws Error {
+            return Object.new(instance_type);
+        }
+
+    }
+
+}

+ 0 - 8
src/Registration.vala

@@ -86,12 +86,4 @@ namespace Inversion {
 
     }
 
-    /**
-     * Delegate for custom factory methods that create instances.
-     * 
-     * @param scope The current scope (for resolving dependencies)
-     * @return A new instance of the requested type
-     */
-    public delegate Object FactoryDelegate(Scope scope) throws Error;
-
 }

+ 25 - 11
src/Scope.vala

@@ -74,20 +74,28 @@ namespace Inversion {
             return register_local_factory_type(typeof(T), factory, lifecycle);
         }
 
-        public Registration register_local_type(Type implementation_type, owned FactoryDelegate factory_func, Lifecycle lifecycle) requires (lifecycle != Lifecycle.SINGLETON) {
+        public Registration register_local_type(Type implementation_type, owned FactoryDelegate? factory_func, Lifecycle lifecycle) requires (lifecycle != Lifecycle.SINGLETON) {
+            if(factory_func == null) {
+                var object_factory = new ObjectFactory(implementation_type);
+                return register_local_factory_type(implementation_type, object_factory, lifecycle);
+            }
             var delegate_factory = new DelegateFactory((owned)factory_func);
             return register_local_factory_type(implementation_type, delegate_factory, lifecycle);
         }
 
-        public Registration register_local<T>(owned FactoryDelegate factory_func, Lifecycle lifecycle) requires (lifecycle != Lifecycle.SINGLETON) {
-            return register_local_type(typeof(T), (owned)factory_func, lifecycle);
+        public Registration register_local<T>(owned TypedFactoryDelegate<T>? factory_func = null, Lifecycle lifecycle = Lifecycle.SCOPED) requires (lifecycle != Lifecycle.SINGLETON) {
+            if(factory_func == null) {
+                return register_local_type(typeof(T), null, lifecycle);
+            }
+            var delegate_factory = DelegateFactory.typed<T>((owned)factory_func);
+            return register_local_factory_type(typeof(T), delegate_factory, lifecycle);
         }
 
-        public Registration register_local_transient<T>(owned FactoryDelegate factory_func) {
+        public Registration register_local_transient<T>(owned TypedFactoryDelegate<T>? factory_func = null) {
             return register_local<T>((owned)factory_func, Lifecycle.TRANSIENT);
         }
 
-        public Registration register_local_scoped<T>(owned FactoryDelegate factory_func) {
+        public Registration register_local_scoped<T>(owned TypedFactoryDelegate<T>? factory_func = null) {
             return register_local<T>((owned)factory_func, Lifecycle.SCOPED);
         }
 
@@ -141,20 +149,26 @@ namespace Inversion {
         }
 
         private Object resolve_registration_as(Registration registration, Type requested_type) throws Error {
+            Object result = null;
             switch (registration.lifecycle) {
                 case Lifecycle.SINGLETON:
-                    return container.get_or_create_singleton(registration, requested_type);
+                    result = container.get_or_create_singleton(registration, requested_type);
+                    break;
                     
                 case Lifecycle.SCOPED:
                 case Lifecycle.TRANSIENT:
                 default:
-                    Object instance;
-                    if (!this.scoped_instances.try_get(registration, out instance)) {
-                        instance = create_with_injection_context(this, requested_type, registration);
-                        this.scoped_instances.set(registration, instance);
+                    if (!this.scoped_instances.try_get(registration, out result)) {
+                        result = create_with_injection_context(this, requested_type, registration);
+                        this.scoped_instances.set(registration, result);
                     }
-                    return instance;
+                    break;
+            }
+
+            if(!result.get_type().is_a(requested_type)) {
+                throw new ContainerError.INCOMPATIBLE_TYPE(@"Instansiated object has type $(result.get_type().name()) which is incompatible with the requested type $(requested_type.name())");
             }
+            return result;
         }
 
     }

+ 1 - 0
src/meson.build

@@ -3,6 +3,7 @@ sources = files(
     'DelegateFactory.vala',
     'Factory.vala',
     'InstanceFactory.vala',
+    'ObjectFactory.vala',
     'Lifecycle.vala',
     'Registration.vala',
     'Scope.vala',