feat(encoding): Encoder/Decoder registry implementations
Mark Sagi-Kazar authored 4 years ago
Márk Sági-Kazár committed 2 years ago
0 | package encoding | |
1 | ||
2 | import ( | |
3 | "sync" | |
4 | ) | |
5 | ||
6 | // Decoder decodes the contents of b into a v representation. | |
7 | // It's primarily used for decoding contents of a file into a map[string]interface{}. | |
8 | type Decoder interface { | |
9 | Decode(b []byte, v interface{}) error | |
10 | } | |
11 | ||
12 | const ( | |
13 | // ErrDecoderNotFound is returned when there is no decoder registered for a format. | |
14 | ErrDecoderNotFound = encodingError("decoder not found for this format") | |
15 | ||
16 | // ErrDecoderFormatAlreadyRegistered is returned when an decoder is already registered for a format. | |
17 | ErrDecoderFormatAlreadyRegistered = encodingError("decoder already registered for this format") | |
18 | ) | |
19 | ||
20 | // DecoderRegistry can choose an appropriate Decoder based on the provided format. | |
21 | type DecoderRegistry struct { | |
22 | decoders map[string]Decoder | |
23 | ||
24 | mu sync.RWMutex | |
25 | } | |
26 | ||
27 | // NewDecoderRegistry returns a new, initialized DecoderRegistry. | |
28 | func NewDecoderRegistry() *DecoderRegistry { | |
29 | return &DecoderRegistry{ | |
30 | decoders: make(map[string]Decoder), | |
31 | } | |
32 | } | |
33 | ||
34 | // RegisterDecoder registers a Decoder for a format. | |
35 | // Registering a Decoder for an already existing format is not supported. | |
36 | func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error { | |
37 | e.mu.Lock() | |
38 | defer e.mu.Unlock() | |
39 | ||
40 | if _, ok := e.decoders[format]; ok { | |
41 | return ErrDecoderFormatAlreadyRegistered | |
42 | } | |
43 | ||
44 | e.decoders[format] = enc | |
45 | ||
46 | return nil | |
47 | } | |
48 | ||
49 | // Decode calls the underlying Decoder based on the format. | |
50 | func (e *DecoderRegistry) Decode(format string, b []byte, v interface{}) error { | |
51 | e.mu.RLock() | |
52 | decoder, ok := e.decoders[format] | |
53 | e.mu.RUnlock() | |
54 | ||
55 | if !ok { | |
56 | return ErrDecoderNotFound | |
57 | } | |
58 | ||
59 | return decoder.Decode(b, v) | |
60 | } |
0 | package encoding | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ) | |
5 | ||
6 | type decoder struct { | |
7 | v interface{} | |
8 | } | |
9 | ||
10 | func (d decoder) Decode(_ []byte, v interface{}) error { | |
11 | rv := v.(*string) | |
12 | *rv = d.v.(string) | |
13 | ||
14 | return nil | |
15 | } | |
16 | ||
17 | func TestDecoderRegistry_RegisterDecoder(t *testing.T) { | |
18 | t.Run("OK", func(t *testing.T) { | |
19 | registry := NewDecoderRegistry() | |
20 | ||
21 | err := registry.RegisterDecoder("myformat", decoder{}) | |
22 | if err != nil { | |
23 | t.Fatal(err) | |
24 | } | |
25 | }) | |
26 | ||
27 | t.Run("AlreadyRegistered", func(t *testing.T) { | |
28 | registry := NewDecoderRegistry() | |
29 | ||
30 | err := registry.RegisterDecoder("myformat", decoder{}) | |
31 | if err != nil { | |
32 | t.Fatal(err) | |
33 | } | |
34 | ||
35 | err = registry.RegisterDecoder("myformat", decoder{}) | |
36 | if err != ErrDecoderFormatAlreadyRegistered { | |
37 | t.Fatalf("expected ErrDecoderFormatAlreadyRegistered, got: %v", err) | |
38 | } | |
39 | }) | |
40 | } | |
41 | ||
42 | func TestDecoderRegistry_Decode(t *testing.T) { | |
43 | t.Run("OK", func(t *testing.T) { | |
44 | registry := NewDecoderRegistry() | |
45 | decoder := decoder{ | |
46 | v: "decoded value", | |
47 | } | |
48 | ||
49 | err := registry.RegisterDecoder("myformat", decoder) | |
50 | if err != nil { | |
51 | t.Fatal(err) | |
52 | } | |
53 | ||
54 | var v string | |
55 | ||
56 | err = registry.Decode("myformat", []byte("some value"), &v) | |
57 | if err != nil { | |
58 | t.Fatal(err) | |
59 | } | |
60 | ||
61 | if v != "decoded value" { | |
62 | t.Fatalf("expected 'decoded value', got: %#v", v) | |
63 | } | |
64 | }) | |
65 | ||
66 | t.Run("DecoderNotFound", func(t *testing.T) { | |
67 | registry := NewDecoderRegistry() | |
68 | ||
69 | var v string | |
70 | ||
71 | err := registry.Decode("myformat", []byte("some value"), &v) | |
72 | if err != ErrDecoderNotFound { | |
73 | t.Fatalf("expected ErrDecoderNotFound, got: %v", err) | |
74 | } | |
75 | }) | |
76 | } |
0 | package encoding | |
1 | ||
2 | import ( | |
3 | "sync" | |
4 | ) | |
5 | ||
6 | // Encoder encodes the contents of v into a byte representation. | |
7 | // It's primarily used for encoding a map[string]interface{} into a file format. | |
8 | type Encoder interface { | |
9 | Encode(v interface{}) ([]byte, error) | |
10 | } | |
11 | ||
12 | const ( | |
13 | // ErrEncoderNotFound is returned when there is no encoder registered for a format. | |
14 | ErrEncoderNotFound = encodingError("encoder not found for this format") | |
15 | ||
16 | // ErrEncoderFormatAlreadyRegistered is returned when an encoder is already registered for a format. | |
17 | ErrEncoderFormatAlreadyRegistered = encodingError("encoder already registered for this format") | |
18 | ) | |
19 | ||
20 | // EncoderRegistry can choose an appropriate Encoder based on the provided format. | |
21 | type EncoderRegistry struct { | |
22 | encoders map[string]Encoder | |
23 | ||
24 | mu sync.RWMutex | |
25 | } | |
26 | ||
27 | // NewEncoderRegistry returns a new, initialized EncoderRegistry. | |
28 | func NewEncoderRegistry() *EncoderRegistry { | |
29 | return &EncoderRegistry{ | |
30 | encoders: make(map[string]Encoder), | |
31 | } | |
32 | } | |
33 | ||
34 | // RegisterEncoder registers an Encoder for a format. | |
35 | // Registering a Encoder for an already existing format is not supported. | |
36 | func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error { | |
37 | e.mu.Lock() | |
38 | defer e.mu.Unlock() | |
39 | ||
40 | if _, ok := e.encoders[format]; ok { | |
41 | return ErrEncoderFormatAlreadyRegistered | |
42 | } | |
43 | ||
44 | e.encoders[format] = enc | |
45 | ||
46 | return nil | |
47 | } | |
48 | ||
49 | func (e *EncoderRegistry) Encode(format string, v interface{}) ([]byte, error) { | |
50 | e.mu.RLock() | |
51 | encoder, ok := e.encoders[format] | |
52 | e.mu.RUnlock() | |
53 | ||
54 | if !ok { | |
55 | return nil, ErrEncoderNotFound | |
56 | } | |
57 | ||
58 | return encoder.Encode(v) | |
59 | } |
0 | package encoding | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ) | |
5 | ||
6 | type encoder struct { | |
7 | b []byte | |
8 | } | |
9 | ||
10 | func (e encoder) Encode(_ interface{}) ([]byte, error) { | |
11 | return e.b, nil | |
12 | } | |
13 | ||
14 | func TestEncoderRegistry_RegisterEncoder(t *testing.T) { | |
15 | t.Run("OK", func(t *testing.T) { | |
16 | registry := NewEncoderRegistry() | |
17 | ||
18 | err := registry.RegisterEncoder("myformat", encoder{}) | |
19 | if err != nil { | |
20 | t.Fatal(err) | |
21 | } | |
22 | }) | |
23 | ||
24 | t.Run("AlreadyRegistered", func(t *testing.T) { | |
25 | registry := NewEncoderRegistry() | |
26 | ||
27 | err := registry.RegisterEncoder("myformat", encoder{}) | |
28 | if err != nil { | |
29 | t.Fatal(err) | |
30 | } | |
31 | ||
32 | err = registry.RegisterEncoder("myformat", encoder{}) | |
33 | if err != ErrEncoderFormatAlreadyRegistered { | |
34 | t.Fatalf("expected ErrEncoderFormatAlreadyRegistered, got: %v", err) | |
35 | } | |
36 | }) | |
37 | } | |
38 | ||
39 | func TestEncoderRegistry_Decode(t *testing.T) { | |
40 | t.Run("OK", func(t *testing.T) { | |
41 | registry := NewEncoderRegistry() | |
42 | encoder := encoder{ | |
43 | b: []byte("encoded value"), | |
44 | } | |
45 | ||
46 | err := registry.RegisterEncoder("myformat", encoder) | |
47 | if err != nil { | |
48 | t.Fatal(err) | |
49 | } | |
50 | ||
51 | b, err := registry.Encode("myformat", "some value") | |
52 | if err != nil { | |
53 | t.Fatal(err) | |
54 | } | |
55 | ||
56 | if string(b) != "encoded value" { | |
57 | t.Fatalf("expected 'encoded value', got: %#v", string(b)) | |
58 | } | |
59 | }) | |
60 | ||
61 | t.Run("EncoderNotFound", func(t *testing.T) { | |
62 | registry := NewEncoderRegistry() | |
63 | ||
64 | _, err := registry.Encode("myformat", "some value") | |
65 | if err != ErrEncoderNotFound { | |
66 | t.Fatalf("expected ErrEncoderNotFound, got: %v", err) | |
67 | } | |
68 | }) | |
69 | } |