1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <folly/Expected.h>
20#include <folly/Optional.h>
21#include <folly/dynamic.h>
22#include <folly/json_pointer.h>
23
24namespace folly {
25
26/*
27 * json_patch
28 *
29 * As described in RFC 6902 "JSON Patch".
30 *
31 * Implements parsing. Application over data structures must be
32 * implemented separately.
33 */
34class json_patch {
35 public:
36 enum class parse_error_code : uint8_t {
37 undefined,
38 invalid_shape,
39 missing_op,
40 unknown_op,
41 malformed_op,
42 missing_path_attr,
43 malformed_path_attr,
44 missing_from_attr,
45 malformed_from_attr,
46 missing_value_attr,
47 overlapping_pointers,
48 };
49
50 /*
51 * If parsing JSON patch object fails we return err code along with
52 * pointer to part of JSON document that we could not parse
53 */
54 struct parse_error {
55 // one of the above error codes
56 parse_error_code error_code{parse_error_code::undefined};
57 // pointer to object that caused the error
58 dynamic const* obj{};
59 };
60
61 enum class patch_operation_code : uint8_t {
62 invalid = 0,
63 test,
64 remove,
65 add,
66 replace,
67 move,
68 copy,
69 };
70
71 /*
72 * Single JSON patch operation. Argument may vary based on op type
73 */
74 struct patch_operation {
75 patch_operation_code op_code{patch_operation_code::invalid};
76 json_pointer path;
77 Optional<json_pointer> from;
78 Optional<dynamic> value;
79 friend bool operator==(
80 patch_operation const& lhs,
81 patch_operation const& rhs) {
82 return lhs.op_code == rhs.op_code && lhs.path == rhs.path &&
83 lhs.from == rhs.from && lhs.value == rhs.value;
84 }
85 friend bool operator!=(
86 patch_operation const& lhs,
87 patch_operation const& rhs) {
88 return !(lhs == rhs);
89 }
90 };
91
92 json_patch() = default;
93 ~json_patch() = default;
94
95 static folly::Expected<json_patch, parse_error> try_parse(
96 dynamic const& obj) noexcept;
97
98 std::vector<patch_operation> const& ops() const;
99
100 enum class patch_application_error_code : uint8_t {
101 other,
102 // "from" pointer did not resolve
103 from_not_found,
104 // "path" pointer did not resolve
105 path_not_found,
106 // "test" condition failed
107 test_failed,
108 };
109
110 struct patch_application_error {
111 patch_application_error_code error_code{};
112 // index of the patch element (in array) that caused error
113 size_t index{};
114 };
115
116 /*
117 * Mutate supplied object in accordance with patch operations. Leaves
118 * object in partially modified state if one of the operations fails.
119 */
120 Expected<Unit, patch_application_error> apply(folly::dynamic& obj);
121
122 private:
123 std::vector<patch_operation> ops_;
124};
125
126} // namespace folly
127