-
Notifications
You must be signed in to change notification settings - Fork 0
/
remotize.go
149 lines (132 loc) · 4.24 KB
/
remotize.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2010 Jose Luis Vázquez González [email protected]
// Use of this source code is governed by a BSD-style
// The remotize package wraps rpc calls so you don't have to.
//
// This root remotize package is the only dependency your programs needs to
// import. The tool that remotizes you code is on separated packages that won't be linked to your
// executable unless, of course, you import it especifically for some reason.
//
package remotize
import (
"fmt"
"rpc"
"reflect"
"strings"
"sync"
)
// Remotized Registry
var registry = make(map[string]interface{})
// Registry's lock
var lock sync.RWMutex
// BuildService builds a service wrapper for an interface on a given RpcServer.
//
// Users DON'T need to care about this, as it is done for them by the
// autogenerated code and will be invoked as appropiate when calling NewService.
type BuildService func(interface{}) interface{}
// BuildRemote builds a local reference to a remote interface reachable through
// a given RpcClient.
//
// Users DON'T need to care about this, as it is done for them by the
// autogenerated code and will be invoked as appropiate when calling NewRemote.
type BuildRemote func(*rpc.Client) interface{}
// Please does nothing. It's just a marker that tells the remotize tool
// (goremote) that i interface must, "please", be remotized:
//
// import remotize
// ...
// remotize.Please(new(somepackage.UrlStorer))
//
func Please(i interface{}) {
// Nothing to do, just a marker
}
// NewService returns a new service wrapper to serve calls to 'ifaceimpl' from remote rpc clients.
func NewService(ifaceimpl interface{}) interface{} {
return NewServiceWith(ifaceimpl, ifaceimpl)
}
// NewServiceWith returns a new service wrapper to call 'impl', with interface 'iface'.
func NewServiceWith(iface interface{}, impl interface{}) interface{} {
p := RegistryFind(searchName("", nameFor(iface)) + "Service")
if p == nil {
return nil
}
return p.(BuildService)(impl)
}
// NewRemote returns a proxy to a remote interface of type iface,
// reachable through c RpcClient.
func NewRemote(c *rpc.Client, iface interface{}) interface{} {
p := RegistryFind(searchName("Remote", nameFor(iface)))
if p == nil {
return nil
}
return p.(BuildRemote)(c)
}
// nameFor returns the name of the given underliying type. Pointers are followed
// up to the final referenced type.
func nameFor(i interface{}) string {
t := reflect.TypeOf(i)
for t.Kind() == reflect.Ptr {
t = (t).Elem()
}
return fmt.Sprintf("%v", t)
}
// searchName will search a prefix and name on the registry.
func searchName(prefix, ifacename string) string {
parts := strings.Split(ifacename, ".")
if len(parts) == 2 {
p := ""
if !strings.HasPrefix(parts[1], prefix) {
p = prefix
}
return parts[0] + "." + p + parts[1] + ifaceSuffix(ifacename)
}
return ifacename + ifaceSuffix(ifacename)
}
// Register will record a local reference to a remote interface 'r',
// its builder 'br', the corresponding service 's' and its builder, so that they
// can be retrieved later by NewRemote or NewService calls respectively.
//
// Users DON'T need to care about this registration, as it is done by the
// autogenerated code for them.
func Register(r interface{}, br BuildRemote, s interface{}, bs BuildService) {
cname := fmt.Sprintf("%v", reflect.TypeOf(r))
sname := fmt.Sprintf("%v", reflect.TypeOf(s))
lock.Lock()
defer lock.Unlock()
registry[cname] = br
registry[sname] = bs
}
// RegistryDump dumps the contents of the registry for debugging purposes.
func RegistryDump() string {
var s string
fmt.Sprintf(s, "%v", registry)
return s
}
// RegistryFind will find a registered name in the remotize registry.
func RegistryFind(name string) interface{} {
lock.Lock()
defer lock.Unlock()
return registry[name]
}
// IfaceSuffix will return the proper "r" or "er" or "" ending as an interface for
// the given 'name'.
func ifaceSuffix(name string) string {
s := ""
if !strings.HasSuffix(name, "er") {
if endsWithVowel(name) {
s = "r"
} else {
s = "er"
}
}
return s
}
// endsWithVowel returns true if str ends with an ASCII vowel (a,e,i,o,u).
func endsWithVowel(str string) bool {
vowels := []string{"a", "e", "i", "o", "u"}
for _, v := range vowels {
if strings.HasSuffix(str, v) {
return true
}
}
return false
}