Skip to content

Commit

Permalink
Merge pull request #59 from Peefy/publish-jsonpatch-module
Browse files Browse the repository at this point in the history
feat: impl init jsonpatch module
  • Loading branch information
Peefy authored Nov 8, 2023
2 parents 91c2ac1 + b5ef6e9 commit eb76a36
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 0 deletions.
47 changes: 47 additions & 0 deletions jsonpatch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## Introduction

`jsonpatch` is a module for applying JSON patches (RFC 6902) for KCL values.

TODO: more jsonpatch actions including "add", "remove", "move", etc.

## How to Use

+ Add the dependency

```shell
kcl mod add jsonpatch
```

+ Write the code

```python
import jsonpatch

data = {
"firstName": "John",
"lastName": "Doe",
"age": 30,
"address": {
"streetAddress": "1234 Main St",
"city": "New York",
"state": "NY",
"postalCode": "10001"
},
"phoneNumbers": [
{
"type": "home",
"number": "212-555-1234"
},
{
"type": "work",
"number": "646-555-5678"
}
]
}
phoneNumbers0type = jsonpatch.get_obj(data, "phoneNumbers/0/type")
newObj = jsonpatch.set_obj(data, "phoneNumbers/0/type", "school")
```

## Resource

The code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/jsonpatch)
5 changes: 5 additions & 0 deletions jsonpatch/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "jsonpatch"
version = "0.0.1"
description = "`jsonpatch` is a module for applying JSON patches (RFC 6902) for KCL values."

Empty file added jsonpatch/kcl.mod.lock
Empty file.
119 changes: 119 additions & 0 deletions jsonpatch/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
type PatchOperationType = "add" | "remove" | "replace" | "move" | "test" | "copy"
KCL_BUILTIN_TYPES = ["int", "str", "bool", "float", "NoneType", "UndefinedType", "any", "list", "dict", "function", "number_multiplier"]
NULL_CONSTANTS = [Undefined, None]

schema Patch:
op: PatchOperationType
path: str
value?: any

type JsonPatch = [Patch]

get_obj = lambda obj: any, path: str -> any {
elements = path.lstrip('/').split('/')
_get_obj_n(obj, elements, 0)
}

get_obj_by_key = lambda obj: any, key: str -> any {
result = Undefined
ty = typeof(obj)
# Schema
if ty not in KCL_BUILTIN_TYPES:
result = obj[key]
# Config
elif ty == "dict":
result = obj[key]
# List
elif ty == "list":
idx = _get_list_index_from_key(key)
if idx not in NULL_CONSTANTS:
result = obj[idx]
result
}

_get_list_index_from_key = lambda key: str -> int {
result = Undefined
if key == "-":
result = -1
elif key.startswith("-") and key[1:].isdigit():
result = -int(key)
elif key.isdigit():
result = int(key)
result
}

# Private function
_get_obj_n = lambda obj: any, elements: [str], n: int -> any {
assert n >= 0
result = obj
if n < len(elements):
result = _get_obj_n(get_obj_by_key(obj, elements[n]), elements, n + 1)
result
}

_build_patch_obj_n = lambda obj: {str:}, value: any, elements: [str], n: int -> {str:} {
assert n >= 0
result = Undefined
if n < len(elements):
current_path = "/".join(elements[:n+1])
current_obj = get_obj(obj, current_path)
assert current_obj not in NULL_CONSTANTS, "list value not found for path: ${current_path}"
if n + 1 < len(elements):
next_key = _get_list_index_from_key(elements[n + 1])
next_val = _build_patch_obj_n(obj, value, elements, n + 2)
# List key
if next_key not in NULL_CONSTANTS:
idx = next_key
assert 0 <= idx < len(current_obj), "value not found for path: {}".format(current_path + "/" + elements[n + 1])
result = {
"${elements[n]}": [None] * idx + [next_val] + [None] * (len(current_obj) - 1 - idx)
}
# Config key
else:
result = {
"${elements[n]}": next_val
}
# No Next value
else:
result = {
"${elements[n]}" = value
}
result
}

build_patch = lambda obj: any, path: str, value: any -> any {
_build_patch_obj_n(obj, value, path.lstrip('/').split('/'), 0)
}

set_obj = lambda obj: any, path: str, value: any -> any {
obj | _build_patch_obj_n(obj, value, path.lstrip('/').split('/'), 0)
}

apply_patch = lambda obj: any, patch: Patch {
dst = obj
if patch.op == "add":
assert False
elif patch.op == "remove":
assert False
elif patch.op == "replace":
dst = build_patch(obj, patch.path, patch.value)
elif patch.op == "move":
assert False
elif patch.op == "test":
assert False
elif patch.op == "copy":
assert False
dst
}

apply_patchs = lambda obj: any, patch: JsonPatch -> any {
"""Apply list of patches to specified json document."""
assert False, "TODO: PRs welcome"
{}
}

make_patch = lambda src: any, dst: any -> JsonPatch {
"""Generates patch by comparing two document objects."""
assert False, "TODO: PRs welcome"
[]
}

0 comments on commit eb76a36

Please sign in to comment.