1package continuity
2
3type resourceUpdate struct {
4	Original Resource
5	Updated  Resource
6}
7
8type resourceListDifference struct {
9	Additions []Resource
10	Deletions []Resource
11	Updates   []resourceUpdate
12}
13
14func (l resourceListDifference) HasDiff() bool {
15	return len(l.Additions) > 0 || len(l.Deletions) > 0 || len(l.Updates) > 0
16}
17
18// diffManifest compares two resource lists and returns the list
19// of adds updates and deletes, resource lists are not reordered
20// before doing difference.
21func diffResourceList(r1, r2 []Resource) resourceListDifference {
22	i1 := 0
23	i2 := 0
24	var d resourceListDifference
25
26	for i1 < len(r1) && i2 < len(r2) {
27		p1 := r1[i1].Path()
28		p2 := r2[i2].Path()
29		switch {
30		case p1 < p2:
31			d.Deletions = append(d.Deletions, r1[i1])
32			i1++
33		case p1 == p2:
34			if !compareResource(r1[i1], r2[i2]) {
35				d.Updates = append(d.Updates, resourceUpdate{
36					Original: r1[i1],
37					Updated:  r2[i2],
38				})
39			}
40			i1++
41			i2++
42		case p1 > p2:
43			d.Additions = append(d.Additions, r2[i2])
44			i2++
45		}
46	}
47
48	for i1 < len(r1) {
49		d.Deletions = append(d.Deletions, r1[i1])
50		i1++
51
52	}
53	for i2 < len(r2) {
54		d.Additions = append(d.Additions, r2[i2])
55		i2++
56	}
57
58	return d
59}
60
61func compareResource(r1, r2 Resource) bool {
62	if r1.Path() != r2.Path() {
63		return false
64	}
65	if r1.Mode() != r2.Mode() {
66		return false
67	}
68	if r1.UID() != r2.UID() {
69		return false
70	}
71	if r1.GID() != r2.GID() {
72		return false
73	}
74
75	// TODO(dmcgowan): Check if is XAttrer
76
77	switch t1 := r1.(type) {
78	case RegularFile:
79		t2, ok := r2.(RegularFile)
80		if !ok {
81			return false
82		}
83		return compareRegularFile(t1, t2)
84	case Directory:
85		t2, ok := r2.(Directory)
86		if !ok {
87			return false
88		}
89		return compareDirectory(t1, t2)
90	case SymLink:
91		t2, ok := r2.(SymLink)
92		if !ok {
93			return false
94		}
95		return compareSymLink(t1, t2)
96	case NamedPipe:
97		t2, ok := r2.(NamedPipe)
98		if !ok {
99			return false
100		}
101		return compareNamedPipe(t1, t2)
102	case Device:
103		t2, ok := r2.(Device)
104		if !ok {
105			return false
106		}
107		return compareDevice(t1, t2)
108	default:
109		// TODO(dmcgowan): Should this panic?
110		return r1 == r2
111	}
112}
113
114func compareRegularFile(r1, r2 RegularFile) bool {
115	if r1.Size() != r2.Size() {
116		return false
117	}
118	p1 := r1.Paths()
119	p2 := r2.Paths()
120	if len(p1) != len(p2) {
121		return false
122	}
123	for i := range p1 {
124		if p1[i] != p2[i] {
125			return false
126		}
127	}
128	d1 := r1.Digests()
129	d2 := r2.Digests()
130	if len(d1) != len(d2) {
131		return false
132	}
133	for i := range d1 {
134		if d1[i] != d2[i] {
135			return false
136		}
137	}
138
139	return true
140}
141
142func compareSymLink(r1, r2 SymLink) bool {
143	return r1.Target() == r2.Target()
144}
145
146func compareDirectory(r1, r2 Directory) bool {
147	return true
148}
149
150func compareNamedPipe(r1, r2 NamedPipe) bool {
151	return true
152}
153
154func compareDevice(r1, r2 Device) bool {
155	return r1.Major() == r2.Major() && r1.Minor() == r2.Minor()
156}
157