github.com/CycloneDX/sbom-utility@v0.16.0/cmd/patch_format.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  /*
     3   * Licensed to the Apache Software Foundation (ASF) under one or more
     4   * contributor license agreements.  See the NOTICE file distributed with
     5   * this work for additional information regarding copyright ownership.
     6   * The ASF licenses this file to You under the Apache License, Version 2.0
     7   * (the "License"); you may not use this file except in compliance with
     8   * the License.  You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  package cmd
    19  
    20  import (
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"path/filepath"
    26  
    27  	"github.com/CycloneDX/sbom-utility/utils"
    28  )
    29  
    30  const (
    31  	// Patch operations
    32  	IETF_RFC6902_OP_ADD     = "add"
    33  	IETF_RFC6902_OP_REMOVE  = "remove"
    34  	IETF_RFC6902_OP_REPLACE = "replace"
    35  	IETF_RFC6902_OP_MOVE    = "move"
    36  	IETF_RFC6902_OP_COPY    = "copy"
    37  	IETF_RFC6902_OP_TEST    = "test"
    38  )
    39  
    40  type IETF6902Document struct {
    41  	filename    string
    42  	absFilename string
    43  	rawBytes    []byte
    44  	Records     []IETF6902Record
    45  }
    46  
    47  // Example of all opcodes:
    48  //
    49  //	{ "op": "test", "path": "/a/b/c", "value": "foo" },
    50  //	{ "op": "remove", "path": "/a/b/c" },
    51  //	{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
    52  //	{ "op": "replace", "path": "/a/b/c", "value": 42 },
    53  //	{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
    54  //	{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
    55  type IETF6902Record struct {
    56  	Operation string      `json:"op"`
    57  	Path      string      `json:"path"`
    58  	Value     interface{} `json:"value,omitempty"`
    59  	From      string      `json:"from,omitempty"`
    60  }
    61  
    62  func (record *IETF6902Record) String() string {
    63  	buffer, _ := utils.EncodeAnyToDefaultIndentedJSONStr(record)
    64  	return buffer.String()
    65  }
    66  
    67  func NewIETFRFC6902PatchDocument(patchFilename string) (document *IETF6902Document) {
    68  	temp := IETF6902Document{
    69  		filename: patchFilename,
    70  	}
    71  	return &temp
    72  }
    73  
    74  func (document *IETF6902Document) ReadRawBytes() (err error) {
    75  	getLogger().Enter()
    76  	defer getLogger().Exit()
    77  
    78  	// only read once
    79  	if document.rawBytes == nil {
    80  		// validate filename
    81  		if len(document.filename) == 0 {
    82  			return fmt.Errorf("schema: invalid filename: `%s`", document.filename)
    83  		}
    84  
    85  		// Conditionally append working directory if no abs. path detected
    86  		if len(document.filename) > 0 && !filepath.IsAbs(document.filename) {
    87  			document.absFilename = filepath.Join(utils.GlobalFlags.WorkingDir, document.filename)
    88  		} else {
    89  			document.absFilename = document.filename
    90  		}
    91  
    92  		// Open our jsonFile
    93  		var jsonFile *os.File
    94  		if jsonFile, err = os.Open(document.absFilename); err != nil {
    95  			return
    96  		}
    97  
    98  		// defer the closing of our jsonFile
    99  		defer jsonFile.Close()
   100  
   101  		// read our opened jsonFile as a byte array.
   102  		if document.rawBytes, err = io.ReadAll(jsonFile); err != nil {
   103  			return
   104  		}
   105  
   106  		getLogger().Tracef("read data from: `%s`", document.filename)
   107  		getLogger().Tracef("\n  >> rawBytes[:100]=[%s]", document.rawBytes[:100])
   108  	}
   109  	return
   110  }
   111  
   112  func (document *IETF6902Document) UnmarshalRecords() (err error) {
   113  	getLogger().Enter()
   114  	defer getLogger().Exit()
   115  
   116  	// Read in the bytes of the patch file
   117  	if err = document.ReadRawBytes(); err != nil {
   118  		return
   119  	}
   120  
   121  	// optimistically, prepare the receiving structure and unmarshal
   122  	if err = json.Unmarshal(document.rawBytes, &document.Records); err != nil {
   123  		getLogger().Warningf("unmarshal failed: %v", err)
   124  		return
   125  	}
   126  
   127  	return
   128  }