github.com/MontFerret/ferret@v0.18.0/pkg/runtime/core/scope.go (about)

     1  package core
     2  
     3  import (
     4  	"io"
     5  )
     6  
     7  type (
     8  	CloseFunc func() error
     9  
    10  	RootScope struct {
    11  		closed      bool
    12  		disposables []io.Closer
    13  	}
    14  
    15  	Scope struct {
    16  		root   *RootScope
    17  		parent *Scope
    18  		vars   map[string]Value
    19  	}
    20  )
    21  
    22  func NewRootScope() (*Scope, CloseFunc) {
    23  	root := &RootScope{
    24  		closed:      false,
    25  		disposables: make([]io.Closer, 0, 10),
    26  	}
    27  
    28  	return newScope(root, nil), root.Close
    29  }
    30  
    31  func (s *RootScope) AddDisposable(disposable io.Closer) {
    32  	if s.closed {
    33  		return
    34  	}
    35  
    36  	if disposable != nil {
    37  		s.disposables = append(s.disposables, disposable)
    38  	}
    39  }
    40  
    41  func (s *RootScope) Close() error {
    42  	if s.closed {
    43  		return Error(ErrInvalidOperation, "scope is already closed")
    44  	}
    45  
    46  	s.closed = true
    47  
    48  	var errors []error
    49  
    50  	// close all values implemented io.Close
    51  	for _, c := range s.disposables {
    52  		if err := c.Close(); err != nil {
    53  			if errors == nil {
    54  				errors = make([]error, 0, len(s.disposables))
    55  			}
    56  
    57  			errors = append(errors, err)
    58  		}
    59  	}
    60  
    61  	if errors == nil {
    62  		return nil
    63  	}
    64  
    65  	return Errors(errors...)
    66  }
    67  
    68  func newScope(root *RootScope, parent *Scope) *Scope {
    69  	return &Scope{
    70  		root:   root,
    71  		parent: parent,
    72  		vars:   make(map[string]Value),
    73  	}
    74  }
    75  
    76  func (s *Scope) SetVariable(name string, val Value) error {
    77  	if name != IgnorableVariable {
    78  		_, exists := s.vars[name]
    79  
    80  		// it already has been declared in the current scope
    81  		if exists {
    82  			return Errorf(ErrNotUnique, "variable is already declared: '%s'", name)
    83  		}
    84  
    85  		s.vars[name] = val
    86  	}
    87  
    88  	// we still want to make sure that nothing than needs to be closed leaks out
    89  	disposable, ok := val.(io.Closer)
    90  
    91  	if ok {
    92  		s.root.AddDisposable(disposable)
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func (s *Scope) HasVariable(name string) bool {
    99  	_, exists := s.vars[name]
   100  
   101  	// does not exist in the current scope
   102  	// try to find in a parent scope
   103  	if !exists {
   104  		if s.parent != nil {
   105  			return s.parent.HasVariable(name)
   106  		}
   107  	}
   108  
   109  	return exists
   110  }
   111  
   112  func (s *Scope) GetVariable(name string) (Value, error) {
   113  	out, exists := s.vars[name]
   114  
   115  	// does not exist in the current scope
   116  	// try to find in the parent scope
   117  	if !exists {
   118  		if s.parent != nil {
   119  			return s.parent.GetVariable(name)
   120  		}
   121  
   122  		return nil, Errorf(ErrNotFound, "variable: '%s'", name)
   123  	}
   124  
   125  	return out, nil
   126  }
   127  
   128  func (s *Scope) MustGetVariable(name string) Value {
   129  	out, err := s.GetVariable(name)
   130  
   131  	if err != nil {
   132  		panic(err)
   133  	}
   134  
   135  	return out
   136  }
   137  
   138  func (s *Scope) UpdateVariable(name string, val Value) error {
   139  	_, exists := s.vars[name]
   140  
   141  	if !exists {
   142  		return Errorf(ErrNotFound, "variable: '%s'", name)
   143  	}
   144  
   145  	delete(s.vars, name)
   146  
   147  	return s.SetVariable(name, val)
   148  }
   149  
   150  func (s *Scope) Fork() *Scope {
   151  	child := newScope(s.root, s)
   152  
   153  	return child
   154  }