Skip to content
This repository was archived by the owner on Oct 29, 2021. It is now read-only.

Commit 0a7f972

Browse files
committed
Merge pull request #1 from Rican7/feature/mutable-and-immutable
Feature - Mutability and Immutability
2 parents b5a53cf + cfd9ff2 commit 0a7f972

7 files changed

Lines changed: 540 additions & 212 deletions

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,37 @@ contentTypeString := "application/vnd.google-earth.kml+xml; charset=utf-8"
1919

2020
mediaType, _ := mediatype.Parse(contentTypeString)
2121

22-
mediaType.FullType() // "application/vnd.google-earth.kml+xml"
23-
mediaType.Parameters() // ["charset": "utf-8"]
2422
mediaType.MainType() // "application"
2523
mediaType.SubType() // "kml"
2624
mediaType.Trees() // ["vnd", "google-earth"]
2725
mediaType.Prefix() // "vnd"
2826
mediaType.Suffix() // "xml"
27+
mediaType.Parameters() // ["charset": "utf-8"]
28+
29+
mediaType.FullType() // "application/vnd.google-earth.kml+xml"
2930
mediaType.String() // "application/vnd.google-earth.kml+xml; charset=utf-8"
3031
```
3132

33+
## Mutability and Immutability
34+
35+
```go
36+
mutable := &mediatype.Mutable{
37+
Main: "application",
38+
Sub: "json",
39+
}
40+
41+
mutable.String() // "application/json"
42+
43+
mutable.Sub = "xhtml"
44+
mutable.Suffix = "xml"
45+
mutable.String() // "application/xhtml+xml"
46+
47+
48+
immutable := mutable.Immutable()
49+
mutable.Main = "image"
50+
immutable.String() // "application/xhtml+xml"
51+
```
52+
3253

3354

3455

mediatype.go

Lines changed: 8 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
package mediatype
1010

1111
import (
12+
"fmt"
1213
"mime"
13-
"strings"
1414
)
1515

1616
/**
@@ -40,120 +40,28 @@ const (
4040
// print out the reconstructed parts of the MediaType model in a standards
4141
// compliant manner, following the `mime.FormatMediaType()` output
4242
type MediaType interface {
43-
FullType() string
44-
Parameters() map[string]string
43+
fmt.Stringer
44+
4545
MainType() string
4646
SubType() string
4747
Trees() []string
4848
Prefix() string
4949
Suffix() string
50+
Parameters() map[string]string
5051

51-
String() string
52-
}
53-
54-
type mediaType struct {
55-
fullType string
56-
params map[string]string
57-
58-
mainType string
59-
trees []string
60-
subType string
61-
suffix string
52+
FullType() string
6253
}
6354

64-
/**
65-
* Functions
66-
*/
67-
6855
// Parse a raw media type string into a MediaType interface compatible struct
6956
func Parse(raw string) (MediaType, error) {
70-
normalized, params, err := mime.ParseMediaType(raw)
57+
normalizedFullType, params, err := mime.ParseMediaType(raw)
7158

7259
if nil != err {
7360
return nil, err
7461
}
7562

76-
// Build from the raw
77-
mediaType := &mediaType{
78-
fullType: normalized,
79-
params: params,
80-
}
81-
82-
mediaType.splitTypes()
63+
mediaType := splitTypes(normalizedFullType)
64+
mediaType.Params = params
8365

8466
return mediaType, nil
8567
}
86-
87-
// Get the normalized type and sub-type as a string
88-
func (m *mediaType) FullType() string {
89-
return m.fullType
90-
}
91-
92-
// Get the defined parameters of the media type
93-
func (m *mediaType) Parameters() map[string]string {
94-
return m.params
95-
}
96-
97-
// Get the "main" (top-level) type as a string
98-
func (m *mediaType) MainType() string {
99-
return m.mainType
100-
}
101-
102-
// Get the "sub" type as a string
103-
func (m *mediaType) SubType() string {
104-
return m.subType
105-
}
106-
107-
// Get the split "sub" type as an array of strings split by the namespace separator
108-
func (m *mediaType) Trees() []string {
109-
return m.trees
110-
}
111-
112-
// Get the prefix of the type's trees
113-
func (m *mediaType) Prefix() string {
114-
if 0 < len(m.trees) {
115-
return m.trees[0]
116-
}
117-
118-
return ""
119-
}
120-
121-
// Get the "suffix" of the type as a string
122-
func (m *mediaType) Suffix() string {
123-
return m.suffix
124-
}
125-
126-
// Get a string representation conforming to RFC 2045 and RFC 2616
127-
func (m *mediaType) String() string {
128-
return mime.FormatMediaType(m.fullType, m.params)
129-
}
130-
131-
// Split the full type string into parts and assign those values to our struct
132-
func (m *mediaType) splitTypes() {
133-
// Split the main/sub types
134-
mainSubSplit := strings.Split(m.fullType, MainSubSplitCharacter)
135-
136-
m.mainType = mainSubSplit[0]
137-
138-
// If we got more than one part, we must have a sub-type
139-
if 1 < len(mainSubSplit) {
140-
// Split the remaining main/sub split from a possible suffix
141-
subSuffixSplit := strings.Split(mainSubSplit[1], SuffixCharacter)
142-
143-
// If we got more than one part, we must have a suffix
144-
if 1 < len(subSuffixSplit) {
145-
m.suffix = subSuffixSplit[1]
146-
}
147-
148-
// Split the sub-type split into the possibly different trees
149-
treeSubSplit := strings.Split(subSuffixSplit[0], TreeSeparatorCharacter)
150-
treeSubSplitLength := len(treeSubSplit)
151-
152-
m.subType = treeSubSplit[treeSubSplitLength-1]
153-
154-
// If we got more than one part, we must have tree definitions
155-
if 1 < treeSubSplitLength {
156-
m.trees = treeSubSplit[0 : treeSubSplitLength-1]
157-
}
158-
}
159-
}

mediatype_immutable.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* MediaType
3+
*
4+
* Copyright © 2014 Trevor N. Suarez (Rican7)
5+
*/
6+
7+
// This file provides provides an immutable implementation of the MediaType interface
8+
9+
package mediatype
10+
11+
/**
12+
* Types
13+
*/
14+
15+
// Immutable is an immutable encapsulation of a Media Type
16+
type Immutable struct {
17+
contained Mutable
18+
}
19+
20+
/**
21+
* Functions
22+
*/
23+
24+
// NewImmutable returns a new empty instance of a Immutable struct
25+
func NewImmutable() MediaType {
26+
return &Immutable{}
27+
}
28+
29+
// NewImmutableAsContainer returns a new instance of a Immutable struct
30+
// with the values matching the values of the passed Mutable struct
31+
func NewImmutableAsContainer(mutable Mutable) MediaType {
32+
return &Immutable{mutable}
33+
}
34+
35+
// MainType returns the "main" (top-level) type as a string
36+
func (m *Immutable) MainType() string {
37+
return m.contained.MainType()
38+
}
39+
40+
// SubType returns the "sub" type as a string
41+
func (m *Immutable) SubType() string {
42+
return m.contained.SubType()
43+
}
44+
45+
// Trees returns the split "sub" type as an array of strings split by the namespace separator
46+
func (m *Immutable) Trees() []string {
47+
return m.contained.Trees()
48+
}
49+
50+
// Prefix returns the prefix of the type's trees
51+
func (m *Immutable) Prefix() string {
52+
return m.contained.Prefix()
53+
}
54+
55+
// Suffix returns the "suffix" of the type as a string
56+
func (m *Immutable) Suffix() string {
57+
return m.contained.Suffix()
58+
}
59+
60+
// Parameters returns the defined parameters of the media type
61+
func (m *Immutable) Parameters() map[string]string {
62+
return m.contained.Parameters()
63+
}
64+
65+
// FullType returns the normalized full type as a string
66+
func (m *Immutable) FullType() string {
67+
return m.contained.FullType()
68+
}
69+
70+
// Get a string representation conforming to RFC 2045 and RFC 2616
71+
func (m *Immutable) String() string {
72+
return m.contained.String()
73+
}
74+
75+
// Mutable returns a mutable version of this structure
76+
func (m *Immutable) Mutable() Mutable {
77+
return m.contained
78+
}

mediatype_immutable_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* MediaType
3+
*
4+
* Copyright © 2014 Trevor N. Suarez (Rican7)
5+
*/
6+
7+
// Tests for the mediatype immutable structure
8+
9+
package mediatype
10+
11+
import (
12+
"testing"
13+
)
14+
15+
/**
16+
* Helper functions
17+
*/
18+
19+
func parseAndConvertToImmutable(raw string) (*Immutable, error) {
20+
mt, err := Parse(validComplexMediaType)
21+
22+
if nil != err {
23+
return nil, err
24+
}
25+
26+
return mt.(*Mutable).Immutable(), nil
27+
}
28+
29+
/**
30+
* Tests functions
31+
*/
32+
33+
func TestNewImmutable(t *testing.T) {
34+
mt := NewImmutable()
35+
36+
if _, ok := mt.(MediaType); !ok {
37+
t.Errorf("%+v doesn't satisfy the MediaType interface", mt)
38+
}
39+
40+
if _, ok := mt.(*Immutable); !ok {
41+
t.Errorf("MediaType %+v isn't an immutable", mt)
42+
}
43+
}
44+
45+
func TestNewImmutableAsContainer(t *testing.T) {
46+
mutable := &Mutable{Main: "application", Sub: "doge"}
47+
48+
mt := NewImmutableAsContainer(*mutable)
49+
50+
if _, ok := mt.(MediaType); !ok {
51+
t.Errorf("%+v doesn't satisfy the MediaType interface", mt)
52+
}
53+
54+
if _, ok := mt.(*Immutable); !ok {
55+
t.Errorf("MediaType %+v isn't an immutable", mt)
56+
}
57+
58+
mutable.Sub = "nope"
59+
60+
if mt.String() == mutable.String() {
61+
t.Errorf("Immutable was mutated for %+v", mt)
62+
}
63+
}
64+
65+
func TestMutable(t *testing.T) {
66+
mt, err := parseAndConvertToImmutable(validComplexMediaType)
67+
68+
if nil != err {
69+
t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
70+
} else {
71+
mutable := mt.Mutable()
72+
73+
if mt.String() != mutable.String() {
74+
t.Errorf("Mutable doesn't match for %+v", mt)
75+
}
76+
77+
mutable.Main = "asd"
78+
79+
if mt.String() == mutable.String() {
80+
t.Errorf("Immutable was mutated for %+v", mt)
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)