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

     1  // Copyright 2020 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 parser
     6  
     7  import (
     8  	"errors"
     9  	"sync"
    10  
    11  	"github.com/alimy/mir/v4/core"
    12  	"github.com/alimy/mir/v4/internal/reflex"
    13  	"github.com/alimy/mir/v4/internal/utils"
    14  )
    15  
    16  var (
    17  	// defaultTag indicate default mir's struct tag name
    18  	defaultTag       = "mir"
    19  	defautlMethodTag = "method"
    20  )
    21  
    22  func init() {
    23  	core.RegisterParsers(&mirParser{
    24  		engineInfo: &core.EngineInfo{},
    25  		tagName:    defaultTag,
    26  	})
    27  }
    28  
    29  // mirParser parse for struct tag
    30  type mirParser struct {
    31  	engineInfo   *core.EngineInfo
    32  	tagName      string
    33  	watchCtxDone bool
    34  	noneQuery    bool
    35  }
    36  
    37  // Name name of parser
    38  func (p *mirParser) Name() string {
    39  	return core.ParserStructTag
    40  }
    41  
    42  // Init init parser
    43  func (p *mirParser) Init(opts *core.ParserOpts) error {
    44  	if opts == nil {
    45  		return errors.New("init opts is nil")
    46  	}
    47  	if opts.EngineInfo != nil {
    48  		p.engineInfo = opts.EngineInfo
    49  	}
    50  	p.tagName = opts.DefaultTag
    51  	p.watchCtxDone = opts.WatchCtxDone
    52  	p.noneQuery = opts.NoneQuery
    53  	if p.tagName == "" {
    54  		p.tagName = defaultTag
    55  	}
    56  	return nil
    57  }
    58  
    59  // Parse serial parse interface defined object entries
    60  func (p *mirParser) Parse(entries []any) (core.Descriptors, error) {
    61  	if len(entries) == 0 {
    62  		return nil, errors.New("entries is empty")
    63  	}
    64  	r := reflex.NewReflex(p.engineInfo, p.tagName, p.watchCtxDone, p.noneQuery)
    65  	return r.Parse(entries)
    66  }
    67  
    68  // ParseContext concurrent parse interface defined object entries
    69  func (p *mirParser) ParseContext(ctx core.MirCtx, entries []any) {
    70  	_, ifaceSink := ctx.Pipe()
    71  	muxSet := utils.NewMuxSet()
    72  
    73  	wg := &sync.WaitGroup{}
    74  	for _, entry := range entries {
    75  		wg.Add(1)
    76  		go func(ctx core.MirCtx, wg *sync.WaitGroup, ifaceSink chan<- *core.IfaceDescriptor, entry any) {
    77  			defer wg.Done()
    78  
    79  			r := reflex.NewReflex(p.engineInfo, p.tagName, p.watchCtxDone, p.noneQuery)
    80  			iface, err := r.IfaceFrom(entry)
    81  			if err != nil {
    82  				core.Logus("ifaceFrom error: %s", err)
    83  				ctx.Cancel(err)
    84  				return
    85  			}
    86  			// no actual fields so just continue
    87  			if len(iface.Fields) == 0 {
    88  				return
    89  			}
    90  			core.Logus("parsed iface: %s.%s", iface.PkgName, iface.TypeName)
    91  			if err = muxSet.Add(iface.Group + iface.TypeName); err != nil {
    92  				core.Logus("muxSet.Add error: %s", err)
    93  				ctx.Cancel(err)
    94  				return
    95  			}
    96  			ifaceSink <- iface
    97  			core.Logus("delivered iface: %s.%s", iface.PkgName, iface.TypeName)
    98  		}(ctx, wg, ifaceSink, entry)
    99  	}
   100  	wg.Wait()
   101  
   102  	ctx.ParserDone()
   103  }
   104  
   105  // Clone return a copy of Parser
   106  func (p *mirParser) Clone() core.Parser {
   107  	return &mirParser{
   108  		tagName: p.tagName,
   109  	}
   110  }