1 /*
2 	 -------------------------------------------------------------------
3 
4 	 Copyright (C) 2014, Edwin van Leeuwen
5 
6 	 This file is part of todod todo list manager.
7 
8 	 Todod is free software; you can redistribute it and/or modify
9 	 it under the terms of the GNU General Public License as published by
10 	 the Free Software Foundation; either version 3 of the License, or
11 	 (at your option) any later version.
12 
13 	 Todod is distributed in the hope that it will be useful,
14 	 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 	 GNU General Public License for more details.
17 
18 	 You should have received a copy of the GNU General Public License
19 	 along with Todod. If not, see <http://www.gnu.org/licenses/>.
20 
21 	 -------------------------------------------------------------------
22 	 */
23 
24 module todod.set;
25 import std.algorithm;
26 import std.array;
27 import std.container;
28 import std.json;
29 
30 /// Set implemented on top of ranges. Unique and ordered (at time of adding)
31 struct Set(T) {
32 	/// Add an element to the set
33 	void add( E : T )( E element ) {
34 		auto elements = _array.find( element  );
35 		if ( elements.empty ) {
36 			_array ~= element;
37 			sort( _array );
38 		} else {
39             // Bit of a hack to make sure we the ids match up
40 			if (!element.id.empty)
41 				elements[0].id = element.id;
42 		}
43 	}
44 
45 	/// Add a range of elements
46 	void add(RANGE)(RANGE elements ) {
47 		// TODO optimize this since both ranges are sorted
48 		foreach (element; elements)
49 			add( element );
50 	}
51 
52 	/// Remove an element from the set
53 	void remove(E : T)( E element ) {
54 		auto i = countUntil( _array, element );
55 		if (i != -1)
56 			_array = _array[0..i] ~ _array[i+1..$];
57 	}
58 
59 	/// Remove a range of elements
60 	void remove(RANGE)( RANGE elements ) {
61 		// TODO optimize this since both ranges are sorted
62 		foreach (element; elements)
63 			remove( element );
64 	}
65 
66 	/// Returns the front/first element of the set
67 	T front() {
68 		return _array.front;
69 	}
70 
71 	/// Removes the front element from the set
72 	void popFront() {
73 		_array.popFront;
74 	}
75 
76 	/// Returns true when the set is empty
77 	bool empty() const {
78 		if (_array.length > 0)
79 			return false;
80 		return true;
81 	}
82 
83 	/// Return an array version of the set
84 	T[] array() {
85 		return _array;
86 	}
87 
88 	/// Number of elements in the set
89 	size_t length() const {
90 		return _array.length;
91 	}
92 
93 	/// Access by id. 
94 	T opIndex(size_t id) {
95 		return _array[id];
96 	}
97 
98 	private:
99 		T[] _array;
100 }
101 
102 unittest {
103 	import std.uuid;
104 	class Test {
105 		UUID id;
106 		this() {
107 			id = randomUUID;
108 		}
109 
110 		override int opCmp( Object t ) const {
111 			return this.id < (cast(Test)(t)).id;
112 		}
113 	}
114 
115 	Set!Test set;
116 	set.add( new Test );
117 	assert( set.length == 1 );
118 	set.add( [ new Test, new Test ] );
119 	assert( set.length == 3 );
120 }
121 
122 
123 /// Convert set to json array. The element needs to implement a toJSON function
124 JSONValue[] toJSON(T)( Set!T set ) {
125 	JSONValue[] json;
126 	foreach (t; set) 
127 		json ~= t.toJSON;
128 	return json;	
129 }
130 
131 /// Load set from JSON, needs delegate to convert json into the element type
132 Set!T jsonToSet(T)( in JSONValue json, T delegate( in JSONValue ) convert ) {
133 	Set!T set;
134 	assert( json.type == JSON_TYPE.ARRAY );
135 	foreach ( js; json.array )
136 		set.add( convert( js ) );
137 	return set;
138 }
139 
140 /// Convert Set to JSON given a function to convert the element type to JSON
141 JSONValue setToJSON( T )( Set!T set, JSONValue delegate( in T  ) convert ) {
142 	JSONValue[] json;
143 	foreach ( el; set ) {
144 		json ~= convert( el );
145 	}
146 	return JSONValue(json);
147 }