Created
May 26, 2024 16:33
-
-
Save JCWasmx86/95f449abf2a8958b22d0e48279b07ba7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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