extra argument checking because users can be careless
[lwes-ruby.git] / ext / lwes / emitter.c
blobd57a7f15ca685c9e24b1f49c61ac90c1c0fd9acf
1 #include "lwes_ruby.h"
3 static VALUE cLWES_Emitter;
4 static ID id_TYPE_DB, id_TYPE_LIST, id_NAME;
5 static ID id_new;
7 /* the underlying struct for LWES::Emitter */
8 struct _rb_lwes_emitter {
9 struct lwes_emitter *emitter;
12 /* gets the _rb_lwes_emitter struct pointer from self */
13 static struct _rb_lwes_emitter * _rle(VALUE self)
15 struct _rb_lwes_emitter *rle;
17 Data_Get_Struct(self, struct _rb_lwes_emitter, rle);
19 return rle;
22 /* GC automatically calls this when object is finalized */
23 static void rle_free(void *ptr)
25 struct _rb_lwes_emitter *rle = ptr;
27 if (rle->emitter)
28 lwes_emitter_destroy(rle->emitter);
29 rle->emitter = NULL;
32 /* called by the GC when object is allocated */
33 static VALUE rle_alloc(VALUE klass)
35 struct _rb_lwes_emitter *rle;
37 return Data_Make_Struct(klass, struct _rb_lwes_emitter,
38 NULL, rle_free, rle);
42 * kv - Array:
43 * key => String,
44 * key => [ numeric_type, Numeric ],
45 * key => true,
46 * key => false,
47 * memo - lwes_event pointer
49 static VALUE event_hash_iter_i(VALUE kv, VALUE memo)
51 VALUE *tmp;
52 VALUE v;
53 struct lwes_event *event = (struct lwes_event *)memo;
54 LWES_CONST_SHORT_STRING name;
55 int rv = 0;
57 assert(TYPE(kv) == T_ARRAY &&
58 "hash iteration not giving key-value pairs");
59 tmp = RARRAY_PTR(kv);
60 name = RSTRING_PTR(rb_obj_as_string(tmp[0]));
62 v = tmp[1];
63 switch (TYPE(v)) {
64 case T_TRUE:
65 rv = lwes_event_set_BOOLEAN(event, name, TRUE);
66 break;
67 case T_FALSE:
68 rv = lwes_event_set_BOOLEAN(event, name, FALSE);
69 break;
70 case T_ARRAY:
71 rv = lwesrb_event_set_numeric(event, name, v);
72 break;
73 case T_STRING:
74 rv = lwes_event_set_STRING(event, name, RSTRING_PTR(v));
75 break;
77 if (rv > 0)
78 return Qnil;
79 if (rv == 0)
80 rb_raise(rb_eArgError, "unhandled type %s=%s for event=%s",
81 name, RSTRING_PTR(rb_inspect(v)), event->eventName);
82 /* rv < 0 */
83 rb_raise(rb_eRuntimeError, "failed to set %s=%s for event=%s",
84 name, RSTRING_PTR(rb_inspect(v)), event->eventName);
85 return Qfalse;
88 static VALUE _emit_hash(VALUE _tmp)
90 VALUE *tmp = (VALUE *)_tmp;
91 VALUE self = tmp[0];
92 VALUE _event = tmp[1];
93 struct lwes_event *event = (struct lwes_event *)tmp[2];
95 rb_iterate(rb_each, _event, event_hash_iter_i, (VALUE)event);
96 if (lwes_emitter_emit(_rle(self)->emitter, event) < 0)
97 rb_raise(rb_eRuntimeError, "failed to emit event");
99 return _event;
102 static int set_field(
103 struct lwes_event *event,
104 LWES_CONST_SHORT_STRING name,
105 LWES_TYPE type,
106 VALUE val)
108 switch (type) {
109 case LWES_TYPE_BOOLEAN:
110 if (val == Qfalse)
111 return lwes_event_set_BOOLEAN(event, name, FALSE);
112 else if (val == Qtrue)
113 return lwes_event_set_BOOLEAN(event, name, TRUE);
114 else
115 rb_raise(rb_eTypeError, "non-boolean set for %s: %s",
116 name, RSTRING_PTR(rb_inspect(val)));
117 case LWES_TYPE_STRING:
118 if (TYPE(val) != T_STRING)
119 rb_raise(rb_eTypeError, "non-String set for %s: %s",
120 name, RSTRING_PTR(rb_inspect(val)));
121 return lwes_event_set_STRING(event, name, RSTRING_PTR(val));
122 default:
123 return lwesrb_event_set_num(event, name, type, val);
125 assert(0 && "you should never get here (set_field)");
126 return -1;
129 static VALUE _emit_struct(VALUE _argv)
131 VALUE *argv = (VALUE *)_argv;
132 VALUE self = argv[0];
133 VALUE _event = argv[1];
134 struct lwes_event *event = (struct lwes_event *)argv[2];
135 VALUE type_list = argv[3];
136 long i;
137 VALUE *tmp;
139 if (TYPE(type_list) != T_ARRAY)
140 rb_raise(rb_eArgError, "could not get TYPE_LIST const");
142 i = RARRAY_LEN(type_list);
143 for (tmp = RARRAY_PTR(type_list); --i >= 0; tmp++) {
144 /* inner: [ :field_sym, "field_name", type ] */
145 VALUE *inner = RARRAY_PTR(*tmp);
146 VALUE val = rb_struct_aref(_event, inner[0]);
147 int rv;
148 LWES_CONST_SHORT_STRING name;
149 LWES_TYPE type;
151 if (NIL_P(val))
152 continue;
154 name = RSTRING_PTR(inner[1]);
155 type = NUM2INT(inner[2]);
156 rv = set_field(event, name, type, val);
157 if (rv > 0)
158 continue;
160 rb_raise(rb_eRuntimeError,
161 "failed to set %s=%s for event=%s (error: %d)",
162 name, RSTRING_PTR(rb_inspect(val)),
163 event->eventName, rv);
166 if (lwes_emitter_emit(_rle(self)->emitter, event) < 0)
167 rb_raise(rb_eRuntimeError, "failed to emit event");
169 return _event;
172 static VALUE _destroy_event(VALUE _event)
174 struct lwes_event *event = (struct lwes_event *)_event;
176 assert(event && "destroying NULL event");
177 lwes_event_destroy(event);
179 return Qnil;
182 static VALUE emit_hash(VALUE self, VALUE name, VALUE _event)
184 VALUE tmp[3];
185 struct lwes_event *event = lwes_event_create(NULL, RSTRING_PTR(name));
187 if (!event)
188 rb_raise(rb_eRuntimeError, "failed to create lwes_event");
190 tmp[0] = self;
191 tmp[1] = _event;
192 tmp[2] = (VALUE)event;
193 rb_ensure(_emit_hash, (VALUE)&tmp, _destroy_event, (VALUE)event);
195 return _event;
198 static struct lwes_event_type_db * get_type_db(VALUE event)
200 VALUE type_db = rb_const_get(CLASS_OF(event), id_TYPE_DB);
202 if (CLASS_OF(type_db) != cLWES_TypeDB)
203 rb_raise(rb_eArgError, "class does not have valid TYPE_DB");
205 return lwesrb_get_type_db(type_db);
208 static VALUE emit_struct(VALUE self, VALUE _event)
210 VALUE argv[4];
211 struct lwes_event_type_db *db = get_type_db(_event);
212 struct lwes_event *event;
213 VALUE event_class = CLASS_OF(_event);
214 VALUE name = rb_const_get(event_class, id_NAME);
215 VALUE type_list = rb_const_get(event_class, id_TYPE_LIST);
217 if (TYPE(name) != T_STRING || TYPE(type_list) != T_ARRAY)
218 rb_raise(rb_eArgError,
219 "could not get class NAME or TYPE_LIST from: %s",
220 RSTRING_PTR(rb_inspect(_event)));
222 event = lwes_event_create(db, RSTRING_PTR(name));
223 if (!event)
224 rb_raise(rb_eRuntimeError, "failed to create lwes_event");
226 argv[0] = self;
227 argv[1] = _event;
228 argv[2] = (VALUE)event;
229 argv[3] = type_list;
230 rb_ensure(_emit_struct, (VALUE)&argv, _destroy_event, (VALUE)event);
232 return _event;
235 static VALUE emitter_ltlt(VALUE self, VALUE event)
237 if (TYPE(event) != T_STRUCT)
238 rb_raise(rb_eArgError,
239 "Must be a Struct: %s",
240 RSTRING_PTR(rb_inspect(event)));
242 return emit_struct(self, event);
246 * call-seq:
247 * emitter = LWES::Emitter.new
249 * emitter.emit("EventName", :foo => "HI")
251 * emitter.emit(EventStruct, :foo => "HI")
253 * struct = EventStruct.new
254 * struct.foo = "HI"
255 * emitter.emit(struct)
257 static VALUE emitter_emit(int argc, VALUE *argv, VALUE self)
259 VALUE name = Qnil;
260 VALUE event = Qnil;
261 argc = rb_scan_args(argc, argv, "11", &name, &event);
263 switch (TYPE(name)) {
264 case T_STRING:
265 if (TYPE(event) == T_HASH)
266 return emit_hash(self, name, event);
267 rb_raise(rb_eArgError,
268 "second argument must be a hash when first "
269 "is a String");
270 case T_STRUCT:
271 if (argc >= 2)
272 rb_raise(rb_eArgError,
273 "second argument not allowed when first"
274 " is a Struct");
275 event = name;
276 return emit_struct(self, event);
277 case T_CLASS:
278 if (TYPE(event) != T_HASH)
279 rb_raise(rb_eArgError,
280 "second argument must be a Hash when first"
281 " is a Class");
284 * we can optimize this so there's no intermediate
285 * struct created
287 event = rb_funcall(name, id_new, 1, event);
288 return emit_struct(self, event);
289 default:
290 rb_raise(rb_eArgError,
291 "bad argument: %s, must be a String, Struct or Class",
292 RSTRING_PTR(rb_inspect(name)));
295 assert(0 && "should never get here");
296 return event;
300 * Destroys the associated lwes_emitter and the associated socket. This
301 * method is rarely needed as Ruby garbage collection will take care of
302 * closing for you, but may be useful in odd cases when it is desirable
303 * to release file descriptors ASAP.
305 static VALUE emitter_close(VALUE self)
307 rle_free(_rle(self));
309 return Qnil;
312 /* should only used internally by #initialize */
313 static VALUE _create(VALUE self, VALUE options)
315 struct _rb_lwes_emitter *rle = _rle(self);
316 VALUE address, iface, port, heartbeat, ttl;
317 LWES_CONST_SHORT_STRING _address, _iface;
318 LWES_U_INT_32 _port; /* odd, uint16 would be enough here */
319 LWES_BOOLEAN _emit_heartbeat = FALSE;
320 LWES_INT_16 _freq = 0;
321 LWES_U_INT_32 _ttl = UINT32_MAX; /* nobody sets a ttl this long, right? */
323 if (rle->emitter)
324 rb_raise(rb_eRuntimeError, "already created lwes_emitter");
325 if (TYPE(options) != T_HASH)
326 rb_raise(rb_eTypeError, "options must be a hash");
328 address = rb_hash_aref(options, ID2SYM(rb_intern("address")));
329 if (TYPE(address) != T_STRING)
330 rb_raise(rb_eTypeError, ":address must be a string");
331 _address = RSTRING_PTR(address);
333 iface = rb_hash_aref(options, ID2SYM(rb_intern("iface")));
334 if (TYPE(iface) != T_STRING)
335 rb_raise(rb_eTypeError, ":iface must be a string");
336 _iface = RSTRING_PTR(address);
338 port = rb_hash_aref(options, ID2SYM(rb_intern("port")));
339 if (TYPE(port) != T_FIXNUM)
340 rb_raise(rb_eTypeError, ":port must be a Fixnum");
341 _port = NUM2UINT(port);
343 heartbeat = rb_hash_aref(options, ID2SYM(rb_intern("heartbeat")));
344 if (TYPE(heartbeat) == T_FIXNUM) {
345 int tmp = NUM2INT(heartbeat);
346 if (tmp > INT16_MAX)
347 rb_raise(rb_eArgError,":heartbeat > INT16_MAX seconds");
348 _emit_heartbeat = TRUE;
349 _freq = (LWES_INT_16)tmp;
350 } else if (NIL_P(heartbeat)) { /* do nothing, use defaults */
351 } else
352 rb_raise(rb_eTypeError, ":heartbeat must be a Fixnum or nil");
354 ttl = rb_hash_aref(options, ID2SYM(rb_intern("ttl")));
355 if (TYPE(ttl) == T_FIXNUM) {
356 unsigned LONG_LONG tmp = NUM2ULL(ttl);
357 if (tmp >= UINT32_MAX)
358 rb_raise(rb_eArgError, ":ttl >= UINT32_MAX seconds");
359 _ttl = (LWES_U_INT_32)tmp;
360 } else if (NIL_P(ttl)) { /* do nothing, no ttl */
361 } else
362 rb_raise(rb_eTypeError, ":ttl must be a Fixnum or nil");
364 if (_ttl == UINT32_MAX)
365 rle->emitter = lwes_emitter_create(
366 _address, _iface, _port, _emit_heartbeat, _freq);
367 else
368 rle->emitter = lwes_emitter_create_with_ttl(
369 _address, _iface, _port, _emit_heartbeat, _freq, _ttl);
371 if (!rle->emitter)
372 rb_raise(rb_eRuntimeError, "failed to create LWES emitter");
374 return self;
377 /* Init_lwes_ext will call this */
378 void lwesrb_init_emitter(void)
380 VALUE mLWES = rb_define_module("LWES");
381 cLWES_Emitter = rb_define_class_under(mLWES, "Emitter", rb_cObject);
383 rb_define_method(cLWES_Emitter, "<<", emitter_ltlt, 1);
384 rb_define_method(cLWES_Emitter, "emit", emitter_emit, -1);
385 rb_define_method(cLWES_Emitter, "_create", _create, 1);
386 rb_define_method(cLWES_Emitter, "close", emitter_close, 0);
387 rb_define_alloc_func(cLWES_Emitter, rle_alloc);
388 LWESRB_MKID(TYPE_DB);
389 LWESRB_MKID(TYPE_LIST);
390 LWESRB_MKID(NAME);
391 LWESRB_MKID(new);