|
| 1 | +import v.reflection |
| 2 | + |
| 3 | +const provide_key = 'provide' |
| 4 | + |
| 5 | +type InjectCb = fn () ! |
| 6 | + |
| 7 | +pub interface Object {} |
| 8 | + |
| 9 | +pub struct Service { |
| 10 | + typ int @[required] |
| 11 | + inject InjectCb @[required] |
| 12 | + name string @[required] |
| 13 | + originalptr voidptr @[required] |
| 14 | +mut: |
| 15 | + instance &Object @[required] |
| 16 | +} |
| 17 | + |
| 18 | +pub struct Module { |
| 19 | +mut: |
| 20 | + services map[int]Service = map[int]Service{} |
| 21 | +} |
| 22 | + |
| 23 | +pub fn (mut self Module) inject_to_object[T](mut new_service T) ! { |
| 24 | + $for field in T.fields { |
| 25 | + mut service := self.get_service_from_field(field, T.name)! |
| 26 | + $if field.indirections > 0 { |
| 27 | + if mut service is Service { |
| 28 | + if service.typ != field.typ { |
| 29 | + return error("Type of property '${field.name}' in '${T.name}' must be ${service.name} as Reference") |
| 30 | + } |
| 31 | + unsafe { |
| 32 | + new_service.$(field.name) = service.originalptr |
| 33 | + } |
| 34 | + } |
| 35 | + } $else { |
| 36 | + if service is Service { |
| 37 | + println('Warning: field ${field.name} must be a reference to enable autoinject') |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +pub fn (mut self Module) register[T]() &T { |
| 44 | + mut new_service := &T{} |
| 45 | + typ_idx := typeof[T]().idx |
| 46 | + typ := reflection.get_type(typ_idx) or { panic('Type not found ${T.name}') } |
| 47 | + |
| 48 | + if typ_idx in self.services { |
| 49 | + panic('Type ${typ.name} has been already registered') |
| 50 | + } |
| 51 | + |
| 52 | + self.services[typ_idx] = Service{ |
| 53 | + name: T.name |
| 54 | + typ: typ_idx |
| 55 | + instance: new_service |
| 56 | + originalptr: new_service |
| 57 | + inject: fn [mut self, mut new_service] [T]() ! { |
| 58 | + self.inject_to_object[T](mut new_service)! |
| 59 | + } |
| 60 | + } |
| 61 | + return new_service |
| 62 | +} |
| 63 | + |
| 64 | +fn get_key(attrs []string) string { |
| 65 | + return attrs[0] or { 'key' } |
| 66 | +} |
| 67 | + |
| 68 | +type ServiceOrNone = Service | bool |
| 69 | + |
| 70 | +fn (mut self Module) get_service_from_field(field FieldData, t_name string) !ServiceOrNone { |
| 71 | + if 'inject' in field.attrs { |
| 72 | + return self.get_service(field.typ) or { |
| 73 | + return error('Invalid injection type for field ${field.name} in ${t_name}') |
| 74 | + } |
| 75 | + } |
| 76 | + return false |
| 77 | +} |
| 78 | + |
| 79 | +fn (mut self Module) get_service(service_idx int) !Service { |
| 80 | + return self.services[service_idx] or { |
| 81 | + service_info := reflection.get_type(service_idx) or { return err } |
| 82 | + return error('Service with name ${service_info.name} not available, see available: ${self.services.keys()}') |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +struct Something { |
| 87 | + count int |
| 88 | +} |
| 89 | + |
| 90 | +struct SomethingDifferent { |
| 91 | + text string |
| 92 | +} |
| 93 | + |
| 94 | +fn test_main() { |
| 95 | + mut mod := Module{} |
| 96 | + mod.register[SomethingDifferent]() |
| 97 | + mod.register[Something]() |
| 98 | + assert true |
| 99 | +} |
0 commit comments