1 module vision.json.patch.diff; 2 3 import std.conv: to; 4 import vision.json.patch.commons; 5 import vision.json.patch.operation; 6 7 8 /** 9 * Generate diff between two Json documents 10 * Naive implementation without any optimization 11 */ 12 DiffOperation[] diff(const ref JsonItem source, const ref JsonItem target, 13 const string path = "") 14 { 15 DiffOperation[] result; 16 17 if (source == target) 18 return result; 19 20 if (source.type() != target.type()) 21 result ~= new ReplaceOperation(path, target); 22 else 23 switch (source.type()) 24 { 25 case JSON_TYPE.ARRAY: 26 int i = 0; 27 while (i < source.array.length && i < target.array.length) 28 { 29 result ~= diff(source[i], target[i], path ~ "/" ~ i.to!string); 30 ++i; 31 } 32 33 DiffOperation[] removes; 34 while (i < source.array.length) 35 { 36 removes ~= new RemoveOperation(path ~ "/" ~ i.to!string); 37 ++i; 38 } 39 40 import std.range: retro; 41 import std.array: array; 42 43 result ~= removes.retro.array; 44 45 while (i < target.array.length) 46 { 47 result ~= new AddOperation(path ~ "/" ~ i.to!string, target[i]); 48 ++i; 49 } 50 break; 51 52 case JSON_TYPE.OBJECT: 53 foreach (key, ref value; source.object) 54 if (key in target.object) 55 result ~= diff(value, target.object[key], path ~ "/" ~ key); 56 else 57 result ~= new RemoveOperation(path ~ "/" ~ key); 58 59 foreach (key, ref value; target.object) 60 if (key !in source.object) 61 result ~= new AddOperation(path ~ "/" ~ key, value); 62 63 break; 64 65 default: 66 result ~= new ReplaceOperation(path, target); 67 break; 68 } 69 70 return result; 71 } 72 73 /// Convert array of DiffOperations to JSON format 74 JsonItem toJson(DiffOperation[] d) 75 { 76 import std.json; 77 import std.algorithm: map, each; 78 79 int[] emptyArray; 80 auto output = JsonItem(emptyArray); 81 82 d.map!(op => op.toJson).each!(op => output.array ~= op); 83 84 return output; 85 86 }