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

     1  package buildexpression
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/ActiveState/cli/internal/errs"
    11  	"github.com/ActiveState/cli/internal/locale"
    12  	"github.com/ActiveState/cli/internal/logging"
    13  	"github.com/ActiveState/cli/internal/multilog"
    14  	"github.com/ActiveState/cli/internal/rtutils/ptr"
    15  	"github.com/ActiveState/cli/internal/sliceutils"
    16  	"github.com/ActiveState/cli/pkg/platform/api/buildplanner/types"
    17  	"github.com/go-openapi/strfmt"
    18  )
    19  
    20  const (
    21  	SolveFuncName                     = "solve"
    22  	SolveLegacyFuncName               = "solve_legacy"
    23  	RequirementsKey                   = "requirements"
    24  	PlatformsKey                      = "platforms"
    25  	AtTimeKey                         = "at_time"
    26  	RequirementNameKey                = "name"
    27  	RequirementNamespaceKey           = "namespace"
    28  	RequirementVersionRequirementsKey = "version_requirements"
    29  	RequirementVersionKey             = "version"
    30  	RequirementRevisionKey            = "revision"
    31  	RequirementComparatorKey          = "comparator"
    32  
    33  	ctxLet         = "let"
    34  	ctxIn          = "in"
    35  	ctxAp          = "ap"
    36  	ctxValue       = "value"
    37  	ctxAssignments = "assignments"
    38  	ctxIsAp        = "isAp"
    39  )
    40  
    41  var funcNodeNotFoundError = errors.New("Could not find function node")
    42  
    43  type BuildExpression struct {
    44  	Let         *Let
    45  	Assignments []*Value
    46  }
    47  
    48  type Let struct {
    49  	// Let statements can be nested.
    50  	// Each let will contain its own assignments and an in statement.
    51  	Let         *Let
    52  	Assignments []*Var
    53  	In          *In
    54  }
    55  
    56  type Var struct {
    57  	Name  string
    58  	Value *Value
    59  }
    60  
    61  type Value struct {
    62  	Ap    *Ap
    63  	List  *[]*Value
    64  	Str   *string
    65  	Null  *Null
    66  	Float *float64
    67  	Int   *int
    68  
    69  	Assignment *Var
    70  	Object     *[]*Var
    71  	Ident      *string
    72  }
    73  
    74  type Null struct {
    75  	Null string
    76  }
    77  
    78  type Ap struct {
    79  	Name      string
    80  	Arguments []*Value
    81  }
    82  
    83  type In struct {
    84  	FuncCall *Ap
    85  	Name     *string
    86  }
    87  
    88  // New creates a BuildExpression from a JSON byte array.
    89  // The JSON must be a valid BuildExpression in the following format:
    90  //
    91  //	{
    92  //	  "let": {
    93  //	    "runtime": {
    94  //	      "solve_legacy": {
    95  //	        "at_time": "$at_time",
    96  //	        "build_flags": [],
    97  //	        "camel_flags": [],
    98  //	        "platforms": [
    99  //	          "96b7e6f2-bebf-564c-bc1c-f04482398f38"
   100  //	        ],
   101  //	        "requirements": [
   102  //	          {
   103  //	            "name": "requests",
   104  //	            "namespace": "language/python"
   105  //	          },
   106  //	          {
   107  //	            "name": "python",
   108  //	            "namespace": "language",
   109  //	            "version_requirements": [
   110  //	              {
   111  //	                "comparator": "eq",
   112  //	                "version": "3.10.10"
   113  //	              }
   114  //	            ]
   115  //	          },
   116  //	        ],
   117  //	        "solver_version": null
   118  //	      }
   119  //	    },
   120  //	  "in": "$runtime"
   121  //	  }
   122  //	}
   123  func New(data []byte) (*BuildExpression, error) {
   124  	rawBuildExpression := make(map[string]interface{})
   125  	err := json.Unmarshal(data, &rawBuildExpression)
   126  	if err != nil {
   127  		return nil, errs.Wrap(err, "Could not unmarshal build expression")
   128  	}
   129  
   130  	if len(rawBuildExpression) != 1 {
   131  		return nil, errs.New("Build expression must have exactly one key")
   132  	}
   133  
   134  	expr := &BuildExpression{}
   135  	var path []string
   136  	for key, value := range rawBuildExpression {
   137  		switch v := value.(type) {
   138  		case map[string]interface{}:
   139  			// At this level the key must either be a let, an ap, or an assignment.
   140  			if key == "let" {
   141  				let, err := newLet(path, v)
   142  				if err != nil {
   143  					return nil, errs.Wrap(err, "Could not parse 'let' key")
   144  				}
   145  
   146  				expr.Let = let
   147  			} else if isAp(path, v) {
   148  				ap, err := newAp(path, v)
   149  				if err != nil {
   150  					return nil, errs.Wrap(err, "Could not parse '%s' key", key)
   151  				}
   152  
   153  				expr.Assignments = append(expr.Assignments, &Value{Ap: ap})
   154  			} else {
   155  				assignments, err := newAssignments(path, v)
   156  				if err != nil {
   157  					return nil, errs.Wrap(err, "Could not parse assignments")
   158  				}
   159  
   160  				expr.Assignments = append(expr.Assignments, &Value{Assignment: &Var{Name: key, Value: &Value{Object: &assignments}}})
   161  			}
   162  		default:
   163  			return nil, errs.New("Build expression's value must be a map[string]interface{}")
   164  		}
   165  	}
   166  
   167  	err = expr.validateRequirements()
   168  	if err != nil {
   169  		return nil, errs.Wrap(err, "Could not validate requirements")
   170  	}
   171  
   172  	err = expr.normalizeTimestamp()
   173  	if err != nil {
   174  		return nil, errs.Wrap(err, "Could not normalize timestamp")
   175  	}
   176  
   177  	return expr, nil
   178  }
   179  
   180  // NewEmpty creates a minimal, empty buildexpression.
   181  func NewEmpty() (*BuildExpression, error) {
   182  	// At this time, there is no way to ask the Platform for an empty buildexpression, so build one
   183  	// manually.
   184  	expr, err := New([]byte(`
   185  		{
   186  			"let": {
   187  				"runtime": {
   188  					"solve_legacy": {
   189  						"at_time": "$at_time",
   190  						"build_flags": [],
   191  						"camel_flags": [],
   192  						"platforms": [],
   193  						"requirements": [],
   194  						"solver_version": null
   195  					}
   196  				},
   197  				"in": "$runtime"
   198  			}
   199  		}
   200  	`))
   201  	if err != nil {
   202  		return nil, errs.Wrap(err, "Unable to create initial buildexpression")
   203  	}
   204  	return expr, nil
   205  }
   206  
   207  func newLet(path []string, m map[string]interface{}) (*Let, error) {
   208  	path = append(path, ctxLet)
   209  	defer func() {
   210  		_, _, err := sliceutils.Pop(path)
   211  		if err != nil {
   212  			multilog.Error("Could not pop context: %v", err)
   213  		}
   214  	}()
   215  
   216  	inValue, ok := m["in"]
   217  	if !ok {
   218  		return nil, errs.New("Build expression's 'let' object has no 'in' key")
   219  	}
   220  
   221  	in, err := newIn(path, inValue)
   222  	if err != nil {
   223  		return nil, errs.Wrap(err, "Could not parse 'in' key's value: %v", inValue)
   224  	}
   225  
   226  	// Delete in so it doesn't get parsed as an assignment.
   227  	delete(m, "in")
   228  
   229  	result := &Let{In: in}
   230  	let, ok := m["let"]
   231  	if ok {
   232  		letMap, ok := let.(map[string]interface{})
   233  		if !ok {
   234  			return nil, errs.New("'let' key's value is not a map[string]interface{}")
   235  		}
   236  
   237  		l, err := newLet(path, letMap)
   238  		if err != nil {
   239  			return nil, errs.Wrap(err, "Could not parse 'let' key")
   240  		}
   241  		result.Let = l
   242  
   243  		// Delete let so it doesn't get parsed as an assignment.
   244  		delete(m, "let")
   245  	}
   246  
   247  	assignments, err := newAssignments(path, m)
   248  	if err != nil {
   249  		return nil, errs.Wrap(err, "Could not parse assignments")
   250  	}
   251  
   252  	result.Assignments = assignments
   253  	return result, nil
   254  }
   255  
   256  func isAp(path []string, value map[string]interface{}) bool {
   257  	path = append(path, ctxIsAp)
   258  	defer func() {
   259  		_, _, err := sliceutils.Pop(path)
   260  		if err != nil {
   261  			multilog.Error("Could not pop context: %v", err)
   262  		}
   263  	}()
   264  
   265  	_, hasIn := value["in"]
   266  	if hasIn && !sliceutils.Contains(path, ctxAssignments) {
   267  		return false
   268  	}
   269  
   270  	return true
   271  }
   272  
   273  func newValue(path []string, valueInterface interface{}) (*Value, error) {
   274  	path = append(path, ctxValue)
   275  	defer func() {
   276  		_, _, err := sliceutils.Pop(path)
   277  		if err != nil {
   278  			multilog.Error("Could not pop context: %v", err)
   279  		}
   280  	}()
   281  
   282  	value := &Value{}
   283  
   284  	switch v := valueInterface.(type) {
   285  	case map[string]interface{}:
   286  		// Examine keys first to see if this is a function call.
   287  		for key, val := range v {
   288  			if _, ok := val.(map[string]interface{}); !ok {
   289  				continue
   290  			}
   291  
   292  			// If the length of the value is greater than 1,
   293  			// then it's not a function call. It's an object
   294  			// and will be set as such outside the loop.
   295  			if len(v) > 1 {
   296  				continue
   297  			}
   298  
   299  			if isAp(path, val.(map[string]interface{})) {
   300  				f, err := newAp(path, v)
   301  				if err != nil {
   302  					return nil, errs.Wrap(err, "Could not parse '%s' function's value: %v", key, v)
   303  				}
   304  				value.Ap = f
   305  			}
   306  		}
   307  
   308  		if value.Ap == nil {
   309  			// It's not a function call, but an object.
   310  			object, err := newAssignments(path, v)
   311  			if err != nil {
   312  				return nil, errs.Wrap(err, "Could not parse object: %v", v)
   313  			}
   314  			value.Object = &object
   315  		}
   316  
   317  	case []interface{}:
   318  		values := []*Value{}
   319  		for _, item := range v {
   320  			value, err := newValue(path, item)
   321  			if err != nil {
   322  				return nil, errs.Wrap(err, "Could not parse list: %v", v)
   323  			}
   324  			values = append(values, value)
   325  		}
   326  		value.List = &values
   327  
   328  	case string:
   329  		if sliceutils.Contains(path, ctxIn) {
   330  			value.Ident = &v
   331  		} else {
   332  			value.Str = ptr.To(v)
   333  		}
   334  
   335  	case float64:
   336  		value.Float = ptr.To(v)
   337  
   338  	case nil:
   339  		// An empty value is interpreted as JSON null.
   340  		value.Null = &Null{}
   341  
   342  	default:
   343  		logging.Debug("Unknown type: %T at path %s", v, strings.Join(path, "."))
   344  		// An empty value is interpreted as JSON null.
   345  		value.Null = &Null{}
   346  	}
   347  
   348  	return value, nil
   349  }
   350  
   351  func newAp(path []string, m map[string]interface{}) (*Ap, error) {
   352  	path = append(path, ctxAp)
   353  	defer func() {
   354  		_, _, err := sliceutils.Pop(path)
   355  		if err != nil {
   356  			multilog.Error("Could not pop context: %v", err)
   357  		}
   358  	}()
   359  
   360  	// m is a mapping of function name to arguments. There should only be one
   361  	// set of arugments. Since the arguments are key-value pairs, it should be
   362  	// a map[string]interface{}.
   363  	if len(m) > 1 {
   364  		return nil, errs.New("Function call has more than one argument mapping")
   365  	}
   366  
   367  	// Look in the given object for the function's name and argument mapping.
   368  	var name string
   369  	var argsInterface interface{}
   370  	for key, value := range m {
   371  		_, ok := value.(map[string]interface{})
   372  		if !ok {
   373  			return nil, errs.New("Incorrect argument format")
   374  		}
   375  
   376  		name = key
   377  		argsInterface = value
   378  	}
   379  
   380  	args := []*Value{}
   381  
   382  	switch v := argsInterface.(type) {
   383  	case map[string]interface{}:
   384  		for key, valueInterface := range v {
   385  			value, err := newValue(path, valueInterface)
   386  			if err != nil {
   387  				return nil, errs.Wrap(err, "Could not parse '%s' function's argument '%s': %v", name, key, valueInterface)
   388  			}
   389  			args = append(args, &Value{Assignment: &Var{Name: key, Value: value}})
   390  		}
   391  		sort.SliceStable(args, func(i, j int) bool { return args[i].Assignment.Name < args[j].Assignment.Name })
   392  
   393  	case []interface{}:
   394  		for _, item := range v {
   395  			value, err := newValue(path, item)
   396  			if err != nil {
   397  				return nil, errs.Wrap(err, "Could not parse '%s' function's argument list item: %v", name, item)
   398  			}
   399  			args = append(args, value)
   400  		}
   401  
   402  	default:
   403  		return nil, errs.New("Function '%s' expected to be object or list", name)
   404  	}
   405  
   406  	return &Ap{Name: name, Arguments: args}, nil
   407  }
   408  
   409  func newAssignments(path []string, m map[string]interface{}) ([]*Var, error) {
   410  	path = append(path, ctxAssignments)
   411  	defer func() {
   412  		_, _, err := sliceutils.Pop(path)
   413  		if err != nil {
   414  			multilog.Error("Could not pop context: %v", err)
   415  		}
   416  	}()
   417  
   418  	assignments := []*Var{}
   419  	for key, valueInterface := range m {
   420  		value, err := newValue(path, valueInterface)
   421  		if err != nil {
   422  			return nil, errs.Wrap(err, "Could not parse '%s' key's value: %v", key, valueInterface)
   423  		}
   424  		assignments = append(assignments, &Var{Name: key, Value: value})
   425  
   426  	}
   427  	sort.SliceStable(assignments, func(i, j int) bool {
   428  		return assignments[i].Name < assignments[j].Name
   429  	})
   430  	return assignments, nil
   431  }
   432  
   433  func newIn(path []string, inValue interface{}) (*In, error) {
   434  	path = append(path, ctxIn)
   435  	defer func() {
   436  		_, _, err := sliceutils.Pop(path)
   437  		if err != nil {
   438  			multilog.Error("Could not pop context: %v", err)
   439  		}
   440  	}()
   441  
   442  	in := &In{}
   443  
   444  	switch v := inValue.(type) {
   445  	case map[string]interface{}:
   446  		f, err := newAp(path, v)
   447  		if err != nil {
   448  			return nil, errs.Wrap(err, "'in' object is not a function call")
   449  		}
   450  		in.FuncCall = f
   451  
   452  	case string:
   453  		in.Name = ptr.To(strings.TrimPrefix(v, "$"))
   454  
   455  	default:
   456  		return nil, errs.New("'in' value expected to be a function call or string")
   457  	}
   458  
   459  	return in, nil
   460  }
   461  
   462  // validateRequirements ensures that the requirements in the BuildExpression contain
   463  // both the name and namespace fields. These fileds are used for requirement operations.
   464  func (e *BuildExpression) validateRequirements() error {
   465  	requirements, err := e.getRequirementsNode()
   466  	if err != nil {
   467  		return errs.Wrap(err, "Could not get requirements node")
   468  	}
   469  
   470  	for _, r := range requirements {
   471  		if r.Object == nil {
   472  			continue
   473  		}
   474  
   475  		// The requirement object needs to have a name and value field.
   476  		// The value can be a string (in the case of name or namespace)
   477  		// or a list (in the case of version requirements).
   478  		for _, o := range *r.Object {
   479  			if o.Name == "" {
   480  				return errs.New("Requirement object missing name field")
   481  			}
   482  
   483  			if o.Value == nil {
   484  				return errs.New("Requirement object missing value field")
   485  			}
   486  
   487  			if o.Name == RequirementNameKey || o.Name == RequirementNamespaceKey {
   488  				if o.Value.Str == nil {
   489  					return errs.New("Requirement object value is not set to a string")
   490  				}
   491  			}
   492  
   493  			if o.Name == RequirementVersionRequirementsKey {
   494  				if o.Value.List == nil {
   495  					return errs.New("Requirement object value is not set to a list")
   496  				}
   497  			}
   498  		}
   499  	}
   500  	return nil
   501  }
   502  
   503  // Requirements returns the requirements in the BuildExpression.
   504  // It returns an error if the requirements are not found or if they are malformed.
   505  // It expects the JSON representation of the solve node to be formatted as follows:
   506  //
   507  //	{
   508  //	  "requirements": [
   509  //	    {
   510  //	      "name": "requests",
   511  //	      "namespace": "language/python"
   512  //	    },
   513  //	    {
   514  //	      "name": "python",
   515  //	      "namespace": "language",
   516  //	      "version_requirements": [{
   517  //	          "comparator": "eq",
   518  //	          "version": "3.10.10"
   519  //	      }]
   520  //	    }
   521  //	  ]
   522  //	}
   523  func (e *BuildExpression) Requirements() ([]types.Requirement, error) {
   524  	requirementsNode, err := e.getRequirementsNode()
   525  	if err != nil {
   526  		return nil, errs.Wrap(err, "Could not get requirements node")
   527  	}
   528  
   529  	var requirements []types.Requirement
   530  	for _, r := range requirementsNode {
   531  		if r.Object == nil {
   532  			continue
   533  		}
   534  
   535  		var req types.Requirement
   536  		for _, o := range *r.Object {
   537  			if o.Name == RequirementNameKey {
   538  				req.Name = *o.Value.Str
   539  			}
   540  
   541  			if o.Name == RequirementNamespaceKey {
   542  				req.Namespace = *o.Value.Str
   543  			}
   544  
   545  			if o.Name == RequirementVersionRequirementsKey {
   546  				req.VersionRequirement = getVersionRequirements(o.Value.List)
   547  			}
   548  		}
   549  		requirements = append(requirements, req)
   550  	}
   551  
   552  	return requirements, nil
   553  }
   554  
   555  func (e *BuildExpression) getRequirementsNode() ([]*Value, error) {
   556  	solveAp, err := e.getSolveNode()
   557  	if err != nil {
   558  		return nil, errs.Wrap(err, "Could not get solve node")
   559  	}
   560  
   561  	var reqs []*Value
   562  	for _, arg := range solveAp.Arguments {
   563  		if arg.Assignment == nil {
   564  			continue
   565  		}
   566  
   567  		if arg.Assignment.Name == RequirementsKey && arg.Assignment.Value != nil {
   568  			reqs = *arg.Assignment.Value.List
   569  		}
   570  	}
   571  
   572  	return reqs, nil
   573  }
   574  
   575  func getVersionRequirements(v *[]*Value) []types.VersionRequirement {
   576  	var reqs []types.VersionRequirement
   577  
   578  	if v == nil {
   579  		return reqs
   580  	}
   581  
   582  	for _, r := range *v {
   583  		if r.Object == nil {
   584  			continue
   585  		}
   586  
   587  		versionReq := make(types.VersionRequirement)
   588  		for _, o := range *r.Object {
   589  			if o.Name == RequirementComparatorKey {
   590  				versionReq[RequirementComparatorKey] = *o.Value.Str
   591  			}
   592  
   593  			if o.Name == RequirementVersionKey {
   594  				versionReq[RequirementVersionKey] = *o.Value.Str
   595  			}
   596  		}
   597  		reqs = append(reqs, versionReq)
   598  	}
   599  	return reqs
   600  }
   601  
   602  // getSolveNode returns the solve node from the build expression.
   603  // It returns an error if the solve node is not found.
   604  // Currently, the solve node can have the name of "solve" or "solve_legacy".
   605  // It expects the JSON representation of the build expression to be formatted as follows:
   606  //
   607  //	{
   608  //	  "let": {
   609  //	    "runtime": {
   610  //	      "solve": {
   611  //	      }
   612  //	    }
   613  //	  }
   614  //	}
   615  func (e *BuildExpression) getSolveNode() (*Ap, error) {
   616  	// First, try to find the solve node via lets.
   617  	if e.Let != nil {
   618  		solveAp, err := recurseLets(e.Let)
   619  		if err != nil {
   620  			return nil, errs.Wrap(err, "Could not recurse lets")
   621  		}
   622  
   623  		return solveAp, nil
   624  	}
   625  
   626  	// Search for solve node in the top level assignments.
   627  	for _, a := range e.Assignments {
   628  		if a.Assignment == nil {
   629  			continue
   630  		}
   631  
   632  		if a.Assignment.Name == "" {
   633  			continue
   634  		}
   635  
   636  		if a.Assignment.Value == nil {
   637  			continue
   638  		}
   639  
   640  		if a.Assignment.Value.Ap == nil {
   641  			continue
   642  		}
   643  
   644  		if a.Assignment.Value.Ap.Name == SolveFuncName || a.Assignment.Value.Ap.Name == SolveLegacyFuncName {
   645  			return a.Assignment.Value.Ap, nil
   646  		}
   647  	}
   648  
   649  	return nil, funcNodeNotFoundError
   650  }
   651  
   652  // recurseLets recursively searches for the solve node in the let statements.
   653  // The solve node is specified by the name "runtime" and the function name "solve"
   654  // or "solve_legacy".
   655  func recurseLets(let *Let) (*Ap, error) {
   656  	for _, a := range let.Assignments {
   657  		if a.Value == nil {
   658  			continue
   659  		}
   660  
   661  		if a.Value.Ap == nil {
   662  			continue
   663  		}
   664  
   665  		if a.Name == "" {
   666  			continue
   667  		}
   668  
   669  		if a.Value.Ap.Name == SolveFuncName || a.Value.Ap.Name == SolveLegacyFuncName {
   670  			return a.Value.Ap, nil
   671  		}
   672  	}
   673  
   674  	// The highest level solve node is not found, so recurse into the next let.
   675  	if let.Let != nil {
   676  		return recurseLets(let.Let)
   677  	}
   678  
   679  	return nil, funcNodeNotFoundError
   680  }
   681  
   682  func (e *BuildExpression) getSolveNodeArguments() ([]*Value, error) {
   683  	solveAp, err := e.getSolveNode()
   684  	if err != nil {
   685  		return nil, errs.Wrap(err, "Could not get solve node")
   686  	}
   687  
   688  	return solveAp.Arguments, nil
   689  }
   690  
   691  func (e *BuildExpression) getSolveAtTimeValue() (*Value, error) {
   692  	solveAp, err := e.getSolveNode()
   693  	if err != nil {
   694  		return nil, errs.Wrap(err, "Could not get solve node")
   695  	}
   696  
   697  	for _, arg := range solveAp.Arguments {
   698  		if arg.Assignment != nil && arg.Assignment.Name == AtTimeKey {
   699  			return arg.Assignment.Value, nil
   700  		}
   701  	}
   702  
   703  	return nil, errs.New("Could not find %s", AtTimeKey)
   704  }
   705  
   706  func (e *BuildExpression) getPlatformsNode() (*[]*Value, error) {
   707  	solveAp, err := e.getSolveNode()
   708  	if err != nil {
   709  		return nil, errs.Wrap(err, "Could not get solve node")
   710  	}
   711  
   712  	for _, arg := range solveAp.Arguments {
   713  		if arg.Assignment == nil {
   714  			continue
   715  		}
   716  
   717  		if arg.Assignment.Name == PlatformsKey && arg.Assignment.Value != nil {
   718  			return arg.Assignment.Value.List, nil
   719  		}
   720  	}
   721  
   722  	return nil, errs.New("Could not find platforms node")
   723  }
   724  
   725  // Update updates the BuildExpression's requirements based on the operation and requirement.
   726  func (e *BuildExpression) UpdateRequirement(operation types.Operation, requirement types.Requirement) error {
   727  	var err error
   728  	switch operation {
   729  	case types.OperationAdded:
   730  		err = e.addRequirement(requirement)
   731  	case types.OperationRemoved:
   732  		err = e.removeRequirement(requirement)
   733  	case types.OperationUpdated:
   734  		err = e.removeRequirement(requirement)
   735  		if err != nil {
   736  			break
   737  		}
   738  		err = e.addRequirement(requirement)
   739  	default:
   740  		return errs.New("Unsupported operation")
   741  	}
   742  	if err != nil {
   743  		return errs.Wrap(err, "Could not update BuildExpression's requirements")
   744  	}
   745  
   746  	return nil
   747  }
   748  
   749  func (e *BuildExpression) addRequirement(requirement types.Requirement) error {
   750  	obj := []*Var{
   751  		{Name: RequirementNameKey, Value: &Value{Str: ptr.To(requirement.Name)}},
   752  		{Name: RequirementNamespaceKey, Value: &Value{Str: ptr.To(requirement.Namespace)}},
   753  	}
   754  
   755  	if requirement.Revision != nil {
   756  		obj = append(obj, &Var{Name: RequirementRevisionKey, Value: &Value{Int: requirement.Revision}})
   757  	}
   758  
   759  	if requirement.VersionRequirement != nil {
   760  		values := []*Value{}
   761  		for _, r := range requirement.VersionRequirement {
   762  			values = append(values, &Value{Object: &[]*Var{
   763  				{Name: RequirementComparatorKey, Value: &Value{Str: ptr.To(r[RequirementComparatorKey])}},
   764  				{Name: RequirementVersionKey, Value: &Value{Str: ptr.To(r[RequirementVersionKey])}},
   765  			}})
   766  		}
   767  		obj = append(obj, &Var{Name: RequirementVersionRequirementsKey, Value: &Value{List: &values}})
   768  	}
   769  
   770  	requirementsNode, err := e.getRequirementsNode()
   771  	if err != nil {
   772  		return errs.Wrap(err, "Could not get requirements node")
   773  	}
   774  
   775  	requirementsNode = append(requirementsNode, &Value{Object: &obj})
   776  
   777  	arguments, err := e.getSolveNodeArguments()
   778  	if err != nil {
   779  		return errs.Wrap(err, "Could not get solve node arguments")
   780  	}
   781  
   782  	for _, arg := range arguments {
   783  		if arg.Assignment == nil {
   784  			continue
   785  		}
   786  
   787  		if arg.Assignment.Name == RequirementsKey {
   788  			arg.Assignment.Value.List = &requirementsNode
   789  		}
   790  	}
   791  
   792  	return nil
   793  }
   794  
   795  type RequirementNotFoundError struct {
   796  	Name                   string
   797  	*locale.LocalizedError // for legacy non-user-facing error usages
   798  }
   799  
   800  func (e *BuildExpression) removeRequirement(requirement types.Requirement) error {
   801  	requirementsNode, err := e.getRequirementsNode()
   802  	if err != nil {
   803  		return errs.Wrap(err, "Could not get requirements node")
   804  	}
   805  
   806  	var found bool
   807  	for i, r := range requirementsNode {
   808  		if r.Object == nil {
   809  			continue
   810  		}
   811  
   812  		for _, o := range *r.Object {
   813  			if o.Name == RequirementNameKey && *o.Value.Str == requirement.Name {
   814  				requirementsNode = append(requirementsNode[:i], requirementsNode[i+1:]...)
   815  				found = true
   816  				break
   817  			}
   818  		}
   819  	}
   820  
   821  	if !found {
   822  		return &RequirementNotFoundError{
   823  			requirement.Name,
   824  			locale.NewInputError("err_remove_requirement_not_found", "", requirement.Name),
   825  		}
   826  	}
   827  
   828  	solveNode, err := e.getSolveNode()
   829  	if err != nil {
   830  		return errs.Wrap(err, "Could not get solve node")
   831  	}
   832  
   833  	for _, arg := range solveNode.Arguments {
   834  		if arg.Assignment == nil {
   835  			continue
   836  		}
   837  
   838  		if arg.Assignment.Name == RequirementsKey {
   839  			arg.Assignment.Value.List = &requirementsNode
   840  		}
   841  	}
   842  
   843  	return nil
   844  }
   845  
   846  func (e *BuildExpression) UpdatePlatform(operation types.Operation, platformID strfmt.UUID) error {
   847  	var err error
   848  	switch operation {
   849  	case types.OperationAdded:
   850  		err = e.addPlatform(platformID)
   851  	case types.OperationRemoved:
   852  		err = e.removePlatform(platformID)
   853  	default:
   854  		return errs.New("Unsupported operation")
   855  	}
   856  	if err != nil {
   857  		return errs.Wrap(err, "Could not update BuildExpression's platform")
   858  	}
   859  
   860  	return nil
   861  }
   862  
   863  func (e *BuildExpression) addPlatform(platformID strfmt.UUID) error {
   864  	platformsNode, err := e.getPlatformsNode()
   865  	if err != nil {
   866  		return errs.Wrap(err, "Could not get platforms node")
   867  	}
   868  
   869  	*platformsNode = append(*platformsNode, &Value{Str: ptr.To(platformID.String())})
   870  
   871  	return nil
   872  }
   873  
   874  func (e *BuildExpression) removePlatform(platformID strfmt.UUID) error {
   875  	platformsNode, err := e.getPlatformsNode()
   876  	if err != nil {
   877  		return errs.Wrap(err, "Could not get platforms node")
   878  	}
   879  
   880  	var found bool
   881  	for i, p := range *platformsNode {
   882  		if p.Str == nil {
   883  			continue
   884  		}
   885  
   886  		if *p.Str == platformID.String() {
   887  			*platformsNode = append((*platformsNode)[:i], (*platformsNode)[i+1:]...)
   888  			found = true
   889  			break
   890  		}
   891  	}
   892  
   893  	if !found {
   894  		return errs.New("Could not find platform")
   895  	}
   896  
   897  	return nil
   898  }
   899  
   900  func (e *BuildExpression) SetDefaultTimestamp() error {
   901  	atTimeNode, err := e.getSolveAtTimeValue()
   902  	if err != nil {
   903  		return errs.Wrap(err, "Could not get %s node", AtTimeKey)
   904  	}
   905  	atTimeNode.Str = ptr.To("$" + AtTimeKey)
   906  	return nil
   907  }
   908  
   909  // MaybeSetDefaultTimestamp changes the solve node's "at_time" value to "$at_time" if and only if
   910  // the current value is the given timestamp.
   911  // Buildscripts prefer to use variables for at_time and define them outside the buildexpression as
   912  // the expression's commit time.
   913  // While modern buildexpressions use variables, older ones bake in the commit time. This function
   914  // exists primarily to update those older buildexpressions for use in buildscripts.
   915  func (e *BuildExpression) MaybeSetDefaultTimestamp(ts *strfmt.DateTime) error {
   916  	if ts == nil {
   917  		return nil // nothing to compare to
   918  	}
   919  	atTimeNode, err := e.getSolveAtTimeValue()
   920  	if err != nil {
   921  		return errs.Wrap(err, "Could not get %s node", AtTimeKey)
   922  	}
   923  	if strings.HasPrefix(*atTimeNode.Str, "$") {
   924  		return nil
   925  	}
   926  	if atTime, err := strfmt.ParseDateTime(*atTimeNode.Str); err == nil && atTime == *ts {
   927  		return e.SetDefaultTimestamp()
   928  	} else if err != nil {
   929  		return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str)
   930  	}
   931  	return nil
   932  }
   933  
   934  // normalizeTimestamp normalizes the solve node's timestamp, if possible.
   935  // Platform timestamps may differ from the strfmt.DateTime format. For example, Platform
   936  // timestamps will have microsecond precision, while strfmt.DateTime will only have millisecond
   937  // precision. This will affect comparisons between buildexpressions (which is normally done
   938  // byte-by-byte).
   939  func (e *BuildExpression) normalizeTimestamp() error {
   940  	atTimeNode, err := e.getSolveAtTimeValue()
   941  	if err != nil {
   942  		return errs.Wrap(err, "Could not get at time node")
   943  	}
   944  
   945  	if atTimeNode.Str != nil && !strings.HasPrefix(*atTimeNode.Str, "$") {
   946  		atTime, err := strfmt.ParseDateTime(*atTimeNode.Str)
   947  		if err != nil {
   948  			return errs.Wrap(err, "Invalid timestamp: %s", *atTimeNode.Str)
   949  		}
   950  		atTimeNode.Str = ptr.To(atTime.String())
   951  	}
   952  
   953  	return nil
   954  }
   955  
   956  func (e *BuildExpression) Copy() (*BuildExpression, error) {
   957  	bytes, err := json.Marshal(e)
   958  	if err != nil {
   959  		return nil, errs.Wrap(err, "Failed to marshal build expression during copy")
   960  	}
   961  	return New(bytes)
   962  }
   963  
   964  func (e *BuildExpression) MarshalJSON() ([]byte, error) {
   965  	m := make(map[string]interface{})
   966  
   967  	if e.Let != nil {
   968  		m["let"] = e.Let
   969  	}
   970  
   971  	for _, value := range e.Assignments {
   972  		if value.Assignment == nil {
   973  			continue
   974  		}
   975  
   976  		m[value.Assignment.Name] = value
   977  	}
   978  
   979  	return json.Marshal(m)
   980  }
   981  
   982  func (l *Let) MarshalJSON() ([]byte, error) {
   983  	m := make(map[string]interface{})
   984  
   985  	if l.Let != nil {
   986  		m["let"] = l.Let
   987  	}
   988  
   989  	for _, v := range l.Assignments {
   990  		if v.Value == nil {
   991  			continue
   992  		}
   993  
   994  		m[v.Name] = v.Value
   995  	}
   996  
   997  	m["in"] = l.In
   998  
   999  	return json.Marshal(m)
  1000  }
  1001  
  1002  func (a *Var) MarshalJSON() ([]byte, error) {
  1003  	m := make(map[string]interface{})
  1004  	m[a.Name] = a.Value
  1005  	return json.Marshal(m)
  1006  }
  1007  
  1008  func (v *Value) MarshalJSON() ([]byte, error) {
  1009  	switch {
  1010  	case v.Ap != nil:
  1011  		return json.Marshal(v.Ap)
  1012  	case v.List != nil:
  1013  		return json.Marshal(v.List)
  1014  	case v.Str != nil:
  1015  		return json.Marshal(strings.Trim(*v.Str, `"`))
  1016  	case v.Null != nil:
  1017  		return json.Marshal(nil)
  1018  	case v.Assignment != nil:
  1019  		return json.Marshal(v.Assignment)
  1020  	case v.Float != nil:
  1021  		return json.Marshal(*v.Float)
  1022  	case v.Int != nil:
  1023  		return json.Marshal(*v.Int)
  1024  	case v.Object != nil:
  1025  		m := make(map[string]interface{})
  1026  		for _, assignment := range *v.Object {
  1027  			m[assignment.Name] = assignment.Value
  1028  		}
  1029  		return json.Marshal(m)
  1030  	case v.Ident != nil:
  1031  		return json.Marshal(v.Ident)
  1032  	}
  1033  	return json.Marshal([]*Value{})
  1034  }
  1035  
  1036  func (f *Ap) MarshalJSON() ([]byte, error) {
  1037  	m := make(map[string]interface{})
  1038  	args := make(map[string]interface{})
  1039  	for _, argument := range f.Arguments {
  1040  		switch {
  1041  		case argument.Assignment != nil:
  1042  			args[argument.Assignment.Name] = argument.Assignment.Value
  1043  		default:
  1044  			return nil, fmt.Errorf("Cannot marshal %v (arg %v)", f, argument)
  1045  		}
  1046  	}
  1047  	m[f.Name] = args
  1048  	return json.Marshal(m)
  1049  }
  1050  
  1051  func (i *In) MarshalJSON() ([]byte, error) {
  1052  	switch {
  1053  	case i.FuncCall != nil:
  1054  		return json.Marshal(i.FuncCall)
  1055  	case i.Name != nil:
  1056  		return json.Marshal("$" + *i.Name)
  1057  	}
  1058  	return nil, fmt.Errorf("Cannot marshal %v", i)
  1059  }