github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/runtime/buildscript/json.go (about)

     1  package buildscript
     2  
     3  import (
     4  	"encoding/json"
     5  	"strings"
     6  
     7  	"github.com/go-openapi/strfmt"
     8  
     9  	"github.com/ActiveState/cli/internal/errs"
    10  	"github.com/ActiveState/cli/internal/logging"
    11  	"github.com/ActiveState/cli/internal/rtutils/ptr"
    12  	"github.com/ActiveState/cli/pkg/platform/runtime/buildexpression"
    13  )
    14  
    15  // MarshalJSON marshals the Participle-produced Script into an equivalent buildexpression.
    16  // Users of buildscripts do not need to do this manually; the Expr field contains the
    17  // equivalent buildexpression.
    18  func (s *Script) MarshalJSON() ([]byte, error) {
    19  	m := make(map[string]interface{})
    20  	let := make(map[string]interface{})
    21  	for _, assignment := range s.Assignments {
    22  		key := assignment.Key
    23  		value := assignment.Value
    24  		switch key {
    25  		case buildexpression.AtTimeKey:
    26  			if value.Str == nil {
    27  				return nil, errs.New("String timestamp expected for '%s'", key)
    28  			}
    29  			atTime, err := strfmt.ParseDateTime(strings.Trim(*value.Str, `"`))
    30  			if err != nil {
    31  				return nil, errs.Wrap(err, "Invalid timestamp: %s", *value.Str)
    32  			}
    33  			s.AtTime = &atTime
    34  			continue // do not include this custom assignment in the let block
    35  		case "main":
    36  			key = "in"
    37  		}
    38  		let[key] = value
    39  	}
    40  	m["let"] = let
    41  	return json.Marshal(m)
    42  }
    43  
    44  func (a *Assignment) MarshalJSON() ([]byte, error) {
    45  	m := make(map[string]interface{})
    46  	m[a.Key] = a.Value
    47  	return json.Marshal(m)
    48  }
    49  
    50  func (v *Value) MarshalJSON() ([]byte, error) {
    51  	switch {
    52  	case v.FuncCall != nil:
    53  		return json.Marshal(v.FuncCall)
    54  	case v.List != nil:
    55  		return json.Marshal(v.List)
    56  	case v.Str != nil:
    57  		return json.Marshal(strings.Trim(*v.Str, `"`))
    58  	case v.Number != nil:
    59  		return json.Marshal(*v.Number)
    60  	case v.Null != nil:
    61  		return json.Marshal(nil)
    62  	case v.Assignment != nil:
    63  		return json.Marshal(v.Assignment)
    64  	case v.Object != nil:
    65  		m := make(map[string]interface{})
    66  		for _, assignment := range *v.Object {
    67  			m[assignment.Key] = assignment.Value
    68  		}
    69  		return json.Marshal(m)
    70  	case v.Ident != nil:
    71  		return json.Marshal("$" + *v.Ident)
    72  	}
    73  	return json.Marshal([]*Value{}) // participle does not create v.List if it's empty
    74  }
    75  
    76  func (f *FuncCall) MarshalJSON() ([]byte, error) {
    77  	if f.Name == reqFuncName {
    78  		return marshalReq(f.Arguments)
    79  	}
    80  
    81  	m := make(map[string]interface{})
    82  	args := make(map[string]interface{})
    83  	for _, argument := range f.Arguments {
    84  		switch {
    85  		case argument.Assignment != nil:
    86  			args[argument.Assignment.Key] = argument.Assignment.Value
    87  		case argument.FuncCall != nil:
    88  			args[argument.FuncCall.Name] = argument.FuncCall.Arguments
    89  		default:
    90  			return nil, errs.New("Cannot marshal %v (arg %v)", f, argument)
    91  		}
    92  	}
    93  
    94  	m[f.Name] = args
    95  	return json.Marshal(m)
    96  }
    97  
    98  func marshalReq(args []*Value) ([]byte, error) {
    99  	requirement := make(map[string]interface{})
   100  
   101  	for _, arg := range args {
   102  		assignment := arg.Assignment
   103  		if assignment == nil {
   104  			return nil, errs.New("Cannot marshal %v", arg)
   105  		}
   106  
   107  		switch {
   108  		// Marshal the name argument (e.g. name = "<name>") into {"name": "<name>"}
   109  		case assignment.Key == buildexpression.RequirementNameKey && assignment.Value.Str != nil:
   110  			requirement[buildexpression.RequirementNameKey] = strings.Trim(*assignment.Value.Str, `"`)
   111  
   112  		// Marshal the namespace argument (e.g. namespace = "<namespace>") into
   113  		// {"namespace": "<namespace>"}
   114  		case assignment.Key == buildexpression.RequirementNamespaceKey && assignment.Value.Str != nil:
   115  			requirement[buildexpression.RequirementNamespaceKey] = strings.Trim(*assignment.Value.Str, `"`)
   116  
   117  		// Marshal the version argument (e.g. version = <op>(value = "<version>")) into
   118  		// {"version_requirements": [{"comparator": "<op>", "version": "<version>"}]}
   119  		case assignment.Key == buildexpression.RequirementVersionKey && assignment.Value.FuncCall != nil:
   120  			var requirements []*Value
   121  			var addRequirement func(*FuncCall) error // recursive function for adding to requirements list
   122  			addRequirement = func(funcCall *FuncCall) error {
   123  				switch name := funcCall.Name; name {
   124  				case eqFuncName, neFuncName, gtFuncName, gteFuncName, ltFuncName, lteFuncName:
   125  					req := make([]*Assignment, 0)
   126  					req = append(req, &Assignment{buildexpression.RequirementComparatorKey, &Value{Str: ptr.To(strings.ToLower(name))}})
   127  					if len(funcCall.Arguments) == 0 || funcCall.Arguments[0].Assignment == nil ||
   128  						funcCall.Arguments[0].Assignment.Value.Str == nil || *funcCall.Arguments[0].Assignment.Value.Str == "value" {
   129  						return errs.New(`Illegal argument for version comparator '%s': 'value = "<version>"' expected`, name)
   130  					}
   131  					req = append(req, &Assignment{buildexpression.RequirementVersionKey, &Value{Str: funcCall.Arguments[0].Assignment.Value.Str}})
   132  					requirements = append(requirements, &Value{Object: &req})
   133  				case andFuncName:
   134  					if len(funcCall.Arguments) != 2 {
   135  						return errs.New("Illegal arguments for version comparator '%s': 2 arguments expected, got %d", name, len(funcCall.Arguments))
   136  					}
   137  					for _, a := range funcCall.Arguments {
   138  						if a.Assignment == nil || (a.Assignment.Key != "left" && a.Assignment.Key != "right") || a.Assignment.Value.FuncCall == nil {
   139  							return errs.New("Illegal argument for version comparator '%s': 'left|right = function' expected", name)
   140  						}
   141  						err := addRequirement(a.Assignment.Value.FuncCall)
   142  						if err != nil {
   143  							return errs.Wrap(err, "Could not marshal additional requirement")
   144  						}
   145  					}
   146  				default:
   147  					return errs.New("Unknown version comparator: %s", name)
   148  				}
   149  				return nil
   150  			}
   151  			err := addRequirement(assignment.Value.FuncCall)
   152  			if err != nil {
   153  				return nil, errs.Wrap(err, "Could not marshal requirement")
   154  			}
   155  			requirement[buildexpression.RequirementVersionRequirementsKey] = &Value{List: &requirements}
   156  
   157  		default:
   158  			logging.Debug("Adding unknown argument: %v", assignment)
   159  			requirement[assignment.Key] = assignment.Value
   160  		}
   161  	}
   162  
   163  	return json.Marshal(requirement)
   164  }