From aa8bfd58bbfaa738f3e91d9ba494cda2b17457c2 Mon Sep 17 00:00:00 2001 From: Narayan Sainaney Date: Mon, 20 Mar 2023 11:41:58 -0700 Subject: [PATCH 1/2] Added unit test for sub struct --- ioc/substruct_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 ioc/substruct_test.go diff --git a/ioc/substruct_test.go b/ioc/substruct_test.go new file mode 100644 index 0000000..a4775c0 --- /dev/null +++ b/ioc/substruct_test.go @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package ioc + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +type ConfigA struct { + Flag1 int + Flag2 int +} + +type ConfigB struct { + Flag3 int + Flag4 int +} + +type Config struct { + ConfigA + ConfigB +} + +type ServiceUsingConfigA struct { + ServiceConfig *ConfigA `inject:"Config"` +} + +func TestSubStruct(t *testing.T) { + locator, err := CreateAndBind("testing", func(binder Binder) error { + binder.Bind("Config", &Config{}).InScope(Singleton) + binder.Bind("ServiceA", &ServiceUsingConfigA{}).InScope(Singleton) + return nil + }) + if err != nil { + return + } + + // The next line fails + _, err = locator.GetDService("ServiceA") + assert.Nil(t, err, "Could not hydrate Service with sub struct") +} \ No newline at end of file From fd2a3fcebc523bbffa6b2364008360142d444e7a Mon Sep 17 00:00:00 2001 From: Narayan Sainaney Date: Mon, 20 Mar 2023 21:55:29 -0700 Subject: [PATCH 2/2] Added support for sub-structure injection --- ioc/substruct_test.go | 24 ++++++++++++++++++++++-- ioc/utilities.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/ioc/substruct_test.go b/ioc/substruct_test.go index a4775c0..608ff68 100644 --- a/ioc/substruct_test.go +++ b/ioc/substruct_test.go @@ -60,13 +60,33 @@ type Config struct { ConfigB } -type ServiceUsingConfigA struct { +type ServiceUsingConfigAPtr struct { ServiceConfig *ConfigA `inject:"Config"` } +type ServiceUsingConfigA struct { + ServiceConfig ConfigA `inject:"Config"` +} + + +func TestSubStructPtr(t *testing.T) { + locator, err := CreateAndBind("testing", func(binder Binder) error { + binder.BindConstant("Config", &Config{ConfigA: ConfigA{1, 2}, ConfigB: ConfigB{3, 4}}).InScope(Singleton) + binder.Bind("ServiceA", &ServiceUsingConfigAPtr{}).InScope(Singleton) + return nil + }) + if err != nil { + return + } + + // The next line fails + _, err = locator.GetDService("ServiceA") + assert.Nil(t, err, "Could not hydrate Service with sub struct") +} + func TestSubStruct(t *testing.T) { locator, err := CreateAndBind("testing", func(binder Binder) error { - binder.Bind("Config", &Config{}).InScope(Singleton) + binder.BindConstant("Config", &Config{ConfigA: ConfigA{1, 2}, ConfigB: ConfigB{3, 4}}).InScope(Singleton) binder.Bind("ServiceA", &ServiceUsingConfigA{}).InScope(Singleton) return nil }) diff --git a/ioc/utilities.go b/ioc/utilities.go index 674bde0..ae6df13 100644 --- a/ioc/utilities.go +++ b/ioc/utilities.go @@ -190,6 +190,8 @@ func createAndInject(locator *serviceLocatorData, desc Descriptor, dity reflect. fieldValue := indirect.Field(index) errRet := &errorReturn{} + + findAssignableSubStruct(indirect, fieldValue, value) safeSet(fieldValue, value, errRet) if errRet.err != nil { depErrors.AddError(errRet.err) @@ -242,6 +244,39 @@ func createAndInject(locator *serviceLocatorData, desc Descriptor, dity reflect. return iFace, nil } +func findAssignableSubStruct(indirect, toField reflect.Value, fromFieldRef *reflect.Value) { + fromField := *fromFieldRef + if fromField.Kind() == reflect.Pointer { + fromField = reflect.Indirect(fromField) + } + fromKind := fromField.Kind() + fromType := fromField.Type() + + if toField.Kind() == reflect.Pointer { + toField.Set(reflect.New(toField.Type().Elem())) + toField = toField.Elem() + } + // toKind := toField.Kind() + toType := toField.Type() + + // Used for debugging + // fmt.Printf("FROM %s %s\n", fromKind, fromType.Name()) + // fmt.Printf("TO %s %s\n", toKind, toType.Name()) + + if fromKind == reflect.Struct { + fields := reflect.VisibleFields(fromType) + for _, field := range fields { + isAnonymous := field.Anonymous + isAssignable := field.Type.AssignableTo(toType) + if isAnonymous && isAssignable { + val := indirect.FieldByIndex(field.Index) + *fromFieldRef = val + return + } + } + } +} + func safeValidate(validator Validator, info ValidationInformation, ret *errorReturn) { defer func() { if r := recover(); r != nil {