Skip to content

Instantly share code, notes, and snippets.

@JCWasmx86
Created May 26, 2024 16:33
Show Gist options
  • Save JCWasmx86/95f449abf2a8958b22d0e48279b07ba7 to your computer and use it in GitHub Desktop.
Save JCWasmx86/95f449abf2a8958b22d0e48279b07ba7 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# AGPL license, if used in Netbox+its forks Apache-2.0
import json
import html
def regular_line_ending(s: str):
if s.endswith(","):
return True
return not (s.endswith("{") or s.endswith("["))
def begin_of_complex(s: str):
return s.endswith("[") or s.endswith("{")
def has_indent(s: str, indent: str):
return not s.removeprefix(indent).startswith(" ")
def make_diff(old: str, new: str):
old_lines = old.splitlines(False)
new_lines = new.splitlines(False)
old_list = []
new_list = []
old_idx = 0
new_idx = 0
while True:
if old_idx == len(old_lines) and new_idx == len(new_lines):
break
if old_idx == len(old_lines):
while new_idx != len(new_lines):
new_list.append((new_lines[new_idx], True))
old_list.append(("", False))
new_idx += 1
break
if new_idx == len(new_lines):
while old_idx != len(old_lines):
old_list.append((old_list[old_idx], True))
new_list.append(("", False))
old_idx += 1
break
old_s = old_lines[old_idx]
old_idx += 1
new_s = new_lines[new_idx]
new_idx += 1
if "{" == old_s.strip() and "{" == new_s.strip():
old_list.append(("{", False))
new_list.append(("{", False))
continue
if "}" == old_s.strip() and "}" == new_s.strip():
old_list.append(("}", False))
new_list.append(("}", False))
continue
# Handle:
# "foo": null "foo": [
# "bar",
# "baz",
# "]"
if regular_line_ending(old_s) and begin_of_complex(new_s):
indent_of_new = new_s.replace(new_s.strip(), "")
new_list.append((new_s, True))
old_list.append((old_s, True))
while not has_indent(new_lines[new_idx], indent_of_new):
old_list.append(("", True))
new_list.append((new_lines[new_idx], True))
new_idx += 1
old_list.append(("", True))
new_list.append((new_lines[new_idx], True))
new_idx += 1
continue
# Handle:
# "foo": { "foo": null
# "foo": "bar"
# }
if begin_of_complex(old_s) and regular_line_ending(new_s):
indent_of_old = old_s.replace(old_s.strip(), "")
old_list.append((old_s, True))
new_list.append((new_s, True))
while not has_indent(old_lines[old_idx], indent_of_old):
new_list.append(("", True))
old_list.append((old_lines[old_idx], True))
old_idx += 1
new_list.append(("", True))
old_list.append((old_lines[old_idx], True))
old_idx += 1
continue
if begin_of_complex(old_s) and begin_of_complex(new_s):
indent_of_old = old_s.replace(old_s.strip(), "")
indent_of_new = new_s.replace(new_s.strip(), "")
old_list.append((old_s, False))
new_list.append((new_s, False))
print(">>", new_list[-1], new_s)
old_tmp = []
new_tmp = []
while not has_indent(old_lines[old_idx], indent_of_old):
old_tmp.append(old_lines[old_idx])
old_idx += 1
while not has_indent(new_lines[new_idx], indent_of_new):
new_tmp.append(new_lines[new_idx])
new_idx += 1
a, b = make_diff("\n".join(old_tmp), "\n".join(new_tmp))
assert len(a) == len(b)
old_list += a
new_list += b
old_list.append((old_lines[old_idx], False))
old_idx += 1
new_list.append((new_lines[new_idx], False))
new_idx += 1
continue
if old_s == new_s:
old_list.append((old_s, False))
new_list.append((new_s, False))
continue
# Handle:
# "foo": 1(,) "foo": 2(,)
if regular_line_ending(old_s) and regular_line_ending(new_s):
old_list.append((old_s, True))
new_list.append((new_s, True))
continue
return old_list, new_list
def format_html(tup, color):
if tup[0] == "":
return f"<pre>{html.escape('<<EMPTY>>')}</pre>"
if not tup[1]:
return f"<pre>{html.escape(tup[0])}</pre>"
return f'<pre style="background-color: {color}">{html.escape(tup[0])}</pre>'
old = {
"a": 5,
"b": 2,
"c": [],
"d": None,
"e": {"foo": "baz"},
"f": [1],
"g": [1, 2, 3],
"h": {"a": "b", "c": "d", "q": "r"},
}
new = {
"a": 5,
"b": None,
"c": None,
"d": [1],
"e": None,
"f": [1, 2],
"g": [],
"h": {"a": "c", "c": "d", "f": "g", "q": "r"},
}
print(json.dumps(old, indent=2, sort_keys=True))
print(json.dumps(new, indent=2, sort_keys=True))
a, b = make_diff(
json.dumps(old, indent=2, sort_keys=True), json.dumps(new, indent=2, sort_keys=True)
)
print(a)
print(b)
assert len(a) == len(b)
a_html = "\n".join([format_html(x, "red") for x in a])
b_html = "\n".join([format_html(x, "green") for x in b])
with open("diff.html", "w", encoding="utf-8") as filep:
filep.write(
f"""
<!DOCTYPE html>
<html>
<head>
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
<style>
* {{
box-sizing: border-box;
}}
/* Create two equal columns that floats next to each other */
.column {{
float: left;
width: 50%;
padding: 10px;
height: 100%;
}}
.row:after {{
content: \"\";
display: table;
clear: both;
}}
</style>
</head>
<body>
<h2>Two Equal Columns</h2>
<div class="row">
<div class="column" style="background-color:#aaa;">
<h2>Column 1</h2>
{a_html}
</div>
<div class="column" style="background-color:#bbb;">
<h2>Column 2</h2>
{b_html}
</div>
</div>
</body>
</html>
"""
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment