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 }