1 module scorpion.gem.crud;
2 
3 import std.conv : to;
4 import std..string : join;
5 import std.traits : hasUDA;
6 import std.typetuple : TypeTuple;
7 
8 import scorpion.repository;
9 
10 import shark : PrimaryKey;
11 
12 /**
13  * Generates an interface with methods for creating,
14  * reading, updating and deleting the given entity.
15  * Example:
16  * ---
17  * @Entity("example")
18  * class Example {
19  * 
20  *    @PrimaryKey
21  *    Integer a;
22  * 
23  *    String b;
24  * 
25  * }
26  * 
27  * Example entity = new Example();
28  * entity.b = "test";
29  * repository.insert(entity);
30  * assert(repository.select(entity.a).b == "test");
31  * ---
32  */
33 interface CrudRepository(T) : Repository!T if(Ids!T.length) {
34 
35 	/**
36 	 * Selects and entity using the entity's primary key(s).
37 	 * Example:
38 	 * ---
39 	 * Example entity = repository.select(12);
40 	 * if(entity !is null) {
41 	 *    // do something
42 	 * }
43 	 * ---
44 	 */
45 	@Select
46 	@Where(generateWhere!T)
47 	T select(Ids!T args);
48 
49 	/**
50 	 * Selects every entity in the table.
51 	 * Example:
52 	 * ---
53 	 * foreach(entity ; repository.selectAll()) {
54 	 *    // do something
55 	 * }
56 	 * ---
57 	 */
58 	@Select
59 	T[] selectAll();
60 
61 	/**
62 	 * Inserts a new entity in the table.
63 	 * Example:
64 	 * ---
65 	 * Example entity = new Example();
66 	 * entity.b = "Hello!";
67 	 * repository.insert(entity);
68 	 * ---
69 	 */
70 	@Insert
71 	void insert(T entity);
72 
73 	/**
74 	 * Updates an existing entity's fields.
75 	 * Example:
76 	 * ---
77 	 * if(auto entity = repository.select(1)) {
78 	 *    entity.b = "Updated";
79 	 *    repository.update(entity);
80 	 * }
81 	 * ---
82 	 */
83 	@Update
84 	void update(T entity);
85 
86 	/**
87 	 * Removes an entity from the table.
88 	 * Example:
89 	 * ---
90 	 * Example entity = new Example();
91 	 * entity.a = 12;
92 	 * repository.remove(entity);
93 	 * ---
94 	 */
95 	@Remove
96 	void remove(T entity);
97 
98 	/**
99 	 * Removes an entity from the table using its primary key(s).
100 	 * Example:
101 	 * ---
102 	 * repository.removeById(55);
103 	 * ---
104 	 */
105 	@Remove
106 	@Where(generateWhere!T)
107 	void removeById(Ids!T args);
108 
109 }
110 
111 private template Ids(T) {
112 
113 	mixin(idsImpl!T);
114 
115 }
116 
117 private string idsImpl(T)() {
118 	string[] ret;
119 	foreach(immutable member ; __traits(allMembers, T)) {
120 		static if(hasUDA!(__traits(getMember, T, member), PrimaryKey)) {
121 			ret ~= "typeof(__traits(getMember, T, \"" ~ member ~ "\"))";
122 		}
123 	}
124 	return "alias Ids=TypeTuple!(" ~ ret.join(",") ~ ");";
125 }
126 
127 private string generateWhere(T)() {
128 	size_t counter = 0;
129 	string[] ret;
130 	foreach(immutable member ; __traits(allMembers, T)) {
131 		static if(hasUDA!(__traits(getMember, T, member), PrimaryKey)) {
132 			ret ~= member ~ "=$" ~ to!string(counter++);
133 		}
134 	}
135 	return ret.join(" and ");
136 }