squirtle
squirtle - RFC 6902 JSON Patch for Gleam
Apply patches to JSON documents, generate diffs, and serialize patches.
Types
A JSON document that can be patched.
This is a concrete representation of JSON that supports pattern matching and traversal, unlike gleam/json’s opaque types.
pub type Doc {
Null
String(String)
Int(Int)
Bool(Bool)
Float(Float)
Array(List(Doc))
Object(dict.Dict(String, Doc))
}
Constructors
-
Null -
String(String) -
Int(Int) -
Bool(Bool) -
Float(Float) -
Array(List(Doc)) -
An RFC 6902 JSON Patch operation.
Patches describe transformations to apply to a JSON document. When applied in sequence, they transform the document step by step.
pub type Patch {
Add(path: String, value: Doc)
Remove(path: String)
Replace(path: String, value: Doc)
Copy(from: String, to: String)
Move(from: String, to: String)
Test(path: String, expect: Doc)
}
Constructors
-
Add(path: String, value: Doc)Add a value at the target path. If the path points to an array index, the value is inserted at that position.
-
Remove(path: String)Remove the value at the target path.
-
Replace(path: String, value: Doc)Replace the value at the target path with a new value. The path must already exist.
-
Copy(from: String, to: String)Copy the value from one path to another.
-
Move(from: String, to: String)Move the value from one path to another (copy then remove).
-
Test(path: String, expect: Doc)Test that the value at a path equals the expected value. If the test fails, the entire patch operation fails.
Errors that can occur when applying patches.
pub type PatchError {
PathNotFound(path: String)
InvalidIndex(path: String, index: String)
IndexOutOfBounds(path: String, index: Int)
NotAContainer(path: String)
CannotRemoveRoot
TestFailed(path: String, expected: Doc, actual: Doc)
InvalidPath(reason: String)
}
Constructors
-
PathNotFound(path: String)The specified path does not exist in the document.
-
InvalidIndex(path: String, index: String)An array index in the path is invalid (not a number, has leading zeros, etc).
-
IndexOutOfBounds(path: String, index: Int)An array index is outside the bounds of the array.
-
NotAContainer(path: String)Attempted to navigate into a value that is not an object or array.
-
CannotRemoveRootCannot remove the root document.
-
A test operation failed because the values didn’t match.
-
InvalidPath(reason: String)The JSON pointer path is malformed.
Values
pub fn apply(
doc: Doc,
patches: List(Patch),
) -> Result(Doc, PatchError)
Apply a list of patches to a document.
Patches are applied in order. If any patch fails, the operation stops and returns an error. All patches must succeed for the operation to succeed.
Example
let assert Ok(doc) = squirtle.parse("{\"name\": \"John\"}")
let patches = [
squirtle.Replace(path: "/name", value: squirtle.String("Jane")),
squirtle.Add(path: "/age", value: squirtle.Int(30)),
]
squirtle.apply(doc, patches)
// => Ok(Object(...))
pub fn decode(
doc: Doc,
with decoder: decode.Decoder(a),
) -> Result(a, List(decode.DecodeError))
Decode a Doc into a custom type using a decoder.
Example
let doc = squirtle.Object(dict.from_list([#("name", squirtle.String("John"))]))
squirtle.decode(doc, decode.field("name", decode.string))
// => Ok("John")
pub fn decoder() -> decode.Decoder(Doc)
Decoder for parsing JSON into a Doc.
Use this with gleam/json.parse for custom parsing needs.
pub fn diff(from from: Doc, to to: Doc) -> List(Patch)
Generate a list of patches that transform one document into another.
The returned patches, when applied to from, will produce to.
Example
let from = squirtle.Object(dict.from_list([
#("name", squirtle.String("John")),
]))
let to = squirtle.Object(dict.from_list([
#("name", squirtle.String("Jane")),
#("age", squirtle.Int(30)),
]))
squirtle.diff(from, to)
// => [Replace("/name", String("Jane")), Add("/age", Int(30))]
pub fn error_to_string(error: PatchError) -> String
Convert a PatchError to a human-readable string.
pub fn parse(
json_string: String,
) -> Result(Doc, json.DecodeError)
Parse a JSON string into a Doc.
Example
squirtle.parse("{\"name\": \"John\", \"age\": 30}")
// => Ok(Object(...))
pub fn parse_patches(
json_string: String,
) -> Result(List(Patch), json.DecodeError)
Parse a JSON array of patch operations from a string.
Example
squirtle.parse_patches("[{\"op\": \"add\", \"path\": \"/name\", \"value\": \"John\"}]")
// => Ok([Add("/name", String("John"))])
pub fn patch_decoder() -> decode.Decoder(Patch)
Decoder for parsing a single patch operation.
pub fn patch_to_doc(patch: Patch) -> Doc
Convert a patch to its Doc representation (for custom serialization).
pub fn patch_to_string(patch: Patch) -> String
Convert a single patch to a JSON string.
pub fn patches_to_string(patches: List(Patch)) -> String
Convert a list of patches to a JSON array string.
Example
let patches = [
squirtle.Add(path: "/name", value: squirtle.String("John")),
]
squirtle.patches_to_string(patches)
// => "[{\"op\":\"add\",\"path\":\"/name\",\"value\":\"John\"}]"
pub fn to_dynamic(doc: Doc) -> dynamic.Dynamic
Convert a Doc to a Dynamic value.
Useful when you need to decode a Doc into a custom Gleam type using gleam/dynamic/decode.