* Set - Receives existing data structure, path to set, and data to set at that key. Returns: `value` - modified byte array `err` - On any parsing error */ SYS-REQ-009, SYS-REQ-051, SYS-REQ-068, SYS-REQ-069, SYS-REQ-070, SYS-REQ-110
(data []byte, setValue []byte, keys ...string)
| 1038 | */ |
| 1039 | // SYS-REQ-009, SYS-REQ-051, SYS-REQ-068, SYS-REQ-069, SYS-REQ-070, SYS-REQ-110 |
| 1040 | func Set(data []byte, setValue []byte, keys ...string) (value []byte, err error) { |
| 1041 | // ensure keys are set |
| 1042 | if len(keys) == 0 { |
| 1043 | return nil, KeyPathNotFoundError |
| 1044 | } |
| 1045 | |
| 1046 | _, _, startOffset, endOffset, err := internalGet(data, keys...) |
| 1047 | if err != nil { |
| 1048 | if err != KeyPathNotFoundError { |
| 1049 | // problem parsing the data |
| 1050 | return nil, err |
| 1051 | } |
| 1052 | // full path doesnt exist |
| 1053 | // does any subpath exist? |
| 1054 | var depth int |
| 1055 | for i := range keys { |
| 1056 | _, _, start, end, sErr := internalGet(data, keys[:i+1]...) |
| 1057 | if sErr != nil { |
| 1058 | break |
| 1059 | } else { |
| 1060 | endOffset = end |
| 1061 | startOffset = start |
| 1062 | depth++ |
| 1063 | } |
| 1064 | } |
| 1065 | comma := true |
| 1066 | object := false |
| 1067 | // KI-3: when the path's next component expects one container type but |
| 1068 | // the existing structure is the other (array-index [N] under an object, |
| 1069 | // or an object key under an array), auto-coerce the container to the |
| 1070 | // type expected by the path and proceed with fresh insertion. The |
| 1071 | // mismatched container is treated as if it needs to be (re)created, so |
| 1072 | // the output is always valid JSON. |
| 1073 | coerceTopLevel := false |
| 1074 | coerceStart := 0 |
| 1075 | if endOffset == -1 { |
| 1076 | firstToken := nextToken(data) |
| 1077 | if firstToken < 0 { |
| 1078 | return nil, KeyPathNotFoundError |
| 1079 | } |
| 1080 | pathIsIndex := len(keys[0]) > 0 && keys[0][0] == '[' |
| 1081 | // An empty trailing key component is the degenerate "no path |
| 1082 | // provided" case (SYS-REQ-111), not a real object key — it must |
| 1083 | // keep returning KeyPathNotFoundError on a non-object root, so it |
| 1084 | // is excluded from auto-coerce. |
| 1085 | pathIsObjectKey := len(keys[0]) > 0 && !pathIsIndex |
| 1086 | dataIsObject := data[firstToken] == '{' |
| 1087 | dataIsArray := data[firstToken] == '[' |
| 1088 | if (pathIsIndex && dataIsObject) || (pathIsObjectKey && dataIsArray) { |
| 1089 | // SYS-REQ-009: cross-type Set at the top level — replace the |
| 1090 | // mismatched container with a fresh container of the type the |
| 1091 | // path expects, then perform a fresh insertion. |
| 1092 | coerceTopLevel = true |
| 1093 | coerceStart = firstToken |
| 1094 | comma = false |
| 1095 | object = !pathIsIndex |
| 1096 | endOffset = lastToken(data) |
| 1097 | } else if !dataIsObject { |