github.com/alimy/mir/v4@v4.1.0/internal/reflex/type.go (about)

     1  // Copyright 2022 Michael Li <alimy@gility.net>. All rights reserved.
     2  // Use of this source code is governed by Apache License 2.0 that
     3  // can be found in the LICENSE file.
     4  
     5  package reflex
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"reflect"
    12  	"strings"
    13  
    14  	"github.com/alimy/mir/v4/internal/utils"
    15  )
    16  
    17  // CheckStruct check struct type is in pkgPath and return other struct type field's
    18  // type that contained in type.
    19  // st must struct kind
    20  func CheckStruct(st reflect.Type, pkgPath string) ([]reflect.Type, error) {
    21  	var (
    22  		innerSts []reflect.Type
    23  		outSts   []reflect.Type
    24  	)
    25  	// just return if type is out pkgPath struct
    26  	if st.PkgPath() != pkgPath {
    27  		return []reflect.Type{st}, nil
    28  	}
    29  	innerSts = append(innerSts, st)
    30  	// get all field struct type
    31  	fields := utils.NewStrSet()
    32  	fields.Add(st.PkgPath() + "." + st.Name())
    33  	for i := 0; i < len(innerSts); i++ {
    34  		ist := innerSts[i]
    35  		for i := ist.NumField() - 1; i >= 0; i-- {
    36  			sf := ist.Field(i)
    37  			ft := sf.Type
    38  			ok, wbsts := isWriteableField(ft)
    39  			if !ok {
    40  				return nil, fmt.Errorf("yet not support field %v", ft)
    41  			}
    42  			for _, t := range wbsts {
    43  				if t.PkgPath() == pkgPath {
    44  					fn := t.PkgPath() + "." + ft.Name()
    45  					if err := fields.Add(fn); err == nil {
    46  						innerSts = append(innerSts, t)
    47  					}
    48  				} else {
    49  					outSts = append(outSts, t)
    50  				}
    51  			}
    52  		}
    53  	}
    54  	// collect all struct type
    55  	innerSts = append(innerSts, outSts...)
    56  	return innerSts, nil
    57  }
    58  
    59  // CheckExtStruct check external struct type is in pkgPath and return other struct type field's
    60  // type that contained in type.
    61  // st must struct kind
    62  func CheckExtStruct(st reflect.Type, pkgPath string) ([]reflect.Type, error) {
    63  	var (
    64  		innerSts []reflect.Type
    65  		outSts   []reflect.Type
    66  	)
    67  	// just return if type is out pkgPath struct
    68  	if st.PkgPath() != pkgPath {
    69  		return []reflect.Type{st}, nil
    70  	}
    71  	innerSts = append(innerSts, st)
    72  	// get all field struct type
    73  	fields := utils.NewStrSet()
    74  	fields.Add(st.PkgPath() + "." + st.Name())
    75  	for i := 0; i < len(innerSts); i++ {
    76  		ist := innerSts[i]
    77  		for i := ist.NumField() - 1; i >= 0; i-- {
    78  			sf := ist.Field(i)
    79  			ft := sf.Type
    80  			ok, wbsts := isWriteableField(ft)
    81  			if !ok {
    82  				return nil, fmt.Errorf("yet not support field %v", ft)
    83  			}
    84  			for _, t := range wbsts {
    85  				if t.PkgPath() == pkgPath {
    86  					fn := t.PkgPath() + "." + ft.Name()
    87  					if err := fields.Add(fn); err == nil {
    88  						innerSts = append(innerSts, t)
    89  					}
    90  				} else {
    91  					outSts = append(outSts, t)
    92  				}
    93  			}
    94  		}
    95  	}
    96  	return outSts, nil
    97  }
    98  
    99  // WriteStruct write struct type to *bytes.Buffer
   100  func WriteStruct(buf *bytes.Buffer, t reflect.Type, pkgPath string, imports map[string]string, indent string) (err error) {
   101  	if buf == nil || t == nil {
   102  		return errors.New("buf or t of type is nil")
   103  	}
   104  	if _, err = buf.WriteString(fmt.Sprintf("type %s struct {\n", t.Name())); err != nil {
   105  		return
   106  	}
   107  	l := t.NumField()
   108  	for i := 0; i < l; i++ {
   109  		field := t.Field(i)
   110  		if err = writeStructField(buf, field, pkgPath, imports, indent); err != nil {
   111  			return
   112  		}
   113  	}
   114  	_, err = buf.WriteString("}\n")
   115  	return
   116  }
   117  
   118  func writeStructField(buf *bytes.Buffer, sf reflect.StructField, pkgPath string, imports map[string]string, indent string) (err error) {
   119  	if ok, _ := isWriteableField(sf.Type); !ok {
   120  		return
   121  	}
   122  	buf.WriteString(indent)
   123  	if !sf.Anonymous {
   124  		if _, err = buf.WriteString(fmt.Sprintf("%s%s", sf.Name, indent)); err != nil {
   125  			return
   126  		}
   127  	}
   128  	if err = writeStructFieldType(buf, sf.Type, pkgPath, imports); err != nil {
   129  		return
   130  	}
   131  	if len(sf.Tag) > 0 {
   132  		_, err = buf.WriteString(fmt.Sprintf("%s`%s`\n", indent, sf.Tag))
   133  	} else {
   134  		_, err = buf.WriteString("\n")
   135  	}
   136  	return
   137  }
   138  
   139  func writeStructFieldType(buf *bytes.Buffer, t reflect.Type, pkgPath string, imports map[string]string) (err error) {
   140  	ft := t
   141  	for ; ft.Kind() == reflect.Pointer; ft = ft.Elem() {
   142  		if err = buf.WriteByte('*'); err != nil {
   143  			return
   144  		}
   145  	}
   146  	switch ft.Kind() {
   147  	case reflect.String, reflect.Bool,
   148  		reflect.Complex64, reflect.Complex128,
   149  		reflect.Float32, reflect.Float64,
   150  		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   151  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   152  		if _, err = buf.WriteString(ft.String()); err != nil {
   153  			return
   154  		}
   155  	case reflect.Array:
   156  		_, err = buf.WriteString(fmt.Sprintf("[%d]", ft.Len()))
   157  		if err != nil {
   158  			return
   159  		}
   160  		err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports)
   161  		if err != nil {
   162  			return
   163  		}
   164  	case reflect.Slice:
   165  		if _, err = buf.WriteString("[]"); err != nil {
   166  			return
   167  		}
   168  		if err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports); err != nil {
   169  			return
   170  		}
   171  	case reflect.Map:
   172  		if _, err = buf.WriteString("map["); err != nil {
   173  			return
   174  		}
   175  		if err = writeStructFieldType(buf, ft.Key(), pkgPath, imports); err != nil {
   176  			return
   177  		}
   178  		if _, err = buf.WriteString("]"); err != nil {
   179  			return
   180  		}
   181  		if err = writeStructFieldType(buf, ft.Elem(), pkgPath, imports); err != nil {
   182  			return
   183  		}
   184  	case reflect.Struct:
   185  		var typn string
   186  		ftPkgPath := ft.PkgPath()
   187  		if ftPkgPath == pkgPath {
   188  			typn = ft.Name()
   189  		} else if ftPkgPath == "" {
   190  			typn = ft.String()
   191  		} else {
   192  			typn = ft.String()
   193  			if alias := imports[ftPkgPath]; alias != "" {
   194  				pkgs := strings.Split(ftPkgPath, "/")
   195  				typn = strings.Replace(typn, pkgs[len(pkgs)-1]+".", alias+".", 1)
   196  			}
   197  		}
   198  		if _, err = buf.WriteString(typn); err != nil {
   199  			return
   200  		}
   201  	}
   202  	return
   203  }
   204  
   205  func isWriteableField(t reflect.Type) (b bool, sts []reflect.Type) {
   206  	for t.Kind() == reflect.Pointer {
   207  		t = t.Elem()
   208  	}
   209  	switch t.Kind() {
   210  	case reflect.String, reflect.Bool,
   211  		reflect.Complex64, reflect.Complex128,
   212  		reflect.Float32, reflect.Float64,
   213  		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   214  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   215  		return true, nil
   216  	case reflect.Array, reflect.Slice:
   217  		t = t.Elem()
   218  		return isWriteableField(t)
   219  	case reflect.Map:
   220  		kb, kt := isWriteableMapKey(t.Key())
   221  		vb, vt := isWriteableField(t.Elem())
   222  		if kt != nil {
   223  			sts = append(sts, kt)
   224  		}
   225  		sts = append(sts, vt...)
   226  		b = kb && vb
   227  		return
   228  	case reflect.Struct:
   229  		b = len(t.Name()) > 0
   230  		sts = append(sts, t)
   231  		return
   232  	}
   233  	return false, nil
   234  }
   235  
   236  func isWriteableMapKey(t reflect.Type) (bool, reflect.Type) {
   237  	for t.Kind() == reflect.Pointer {
   238  		t = t.Elem()
   239  	}
   240  	switch t.Kind() {
   241  	case reflect.String, reflect.Bool,
   242  		reflect.Complex64, reflect.Complex128,
   243  		reflect.Float32, reflect.Float64,
   244  		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   245  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   246  		return true, nil
   247  	case reflect.Struct:
   248  		return len(t.Name()) > 0, t
   249  	}
   250  	return false, nil
   251  }