├── doc.go ├── wire ├── doc.go ├── protocol_test.go └── protocol.go ├── README.md ├── .gitignore ├── LICENSE └── example.json /doc.go: -------------------------------------------------------------------------------- 1 | // Package keep provides an API wrapper for Google Keep. 2 | // 3 | // Note that Google Keep has no official API, so this package could break at any 4 | // time. Use with caution and lots of unit tests. 5 | package keep 6 | -------------------------------------------------------------------------------- /wire/doc.go: -------------------------------------------------------------------------------- 1 | // Package wire has types and methods to adapt the json objects used by Google 2 | // Keep. 3 | // 4 | // Note that Google Keep has no official API, and therefore this package could 5 | // break at any time. Use with caution and lots of unit tests. 6 | package wire 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | keep 2 | ==== 3 | 4 | 5 | Keep is a Go wrapper around the Google Keep API. This package is under heavy 6 | construction and should not be used for anything, really. 7 | 8 | The code is hosted at https://github.com/natefinch/keep but the import path is 9 | "npf.io/keep" and the package name is keep. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /wire/protocol_test.go: -------------------------------------------------------------------------------- 1 | package wire 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | ) 8 | 9 | func TestPeriodMarshalEmpty(t *testing.T) { 10 | type foo struct { 11 | When Period `json:",omitempty"` 12 | } 13 | 14 | b, err := json.Marshal(foo{}) 15 | if err != nil { 16 | t.Fatalf("Unexpected error: %v", err) 17 | } 18 | 19 | expect := []byte("{}") 20 | if !bytes.Equal(expect, b) { 21 | t.Fatalf("expected: %q, got: %q", expect, b) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nate Finch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestHeader": { 3 | "clientVersion": { 4 | "major": "3", 5 | "minor": "0", 6 | "build": "0", 7 | "revision": "0" 8 | }, 9 | "clientPlatform": "WEB", 10 | "capabilities": [ 11 | { 12 | "type": "EC" 13 | }, 14 | { 15 | "type": "TR" 16 | }, 17 | { 18 | "type": "SH" 19 | } 20 | ], 21 | "clientSessionId": "s--1416568654958--175204687", 22 | "clientLocale": "en" 23 | }, 24 | "targetVersion": "3d57c", 25 | "clientTimestamp": "2014-11-21T11:18:35.841Z", 26 | "nodes": [ 27 | { 28 | "id": "1414521559976.1475029363", 29 | "kind": "notes#node", 30 | "parentId": "root", 31 | "sortValue": 18874368, 32 | "timestamps": { 33 | "kind": "notes#timestamps", 34 | "created": "2014-10-28T18:39:19.976Z", 35 | "deleted": "1970-01-01T00:00:00.000Z", 36 | "trashed": "1970-01-01T00:00:00.000Z", 37 | "updated": "2014-11-21T11:18:34.934Z", 38 | "userEdited": "2014-11-21T11:18:34.934Z" 39 | }, 40 | "type": "LIST", 41 | "title": "Work Todos", 42 | "isArchived": false, 43 | "nodeSettings": { 44 | "checkedListItemsPolicy": "GRAVEYARD", 45 | "graveyardState": "EXPANDED", 46 | "newListItemPlacement": "BOTTOM" 47 | }, 48 | "baseVersion": "11", 49 | "serverId": "1FxjH8O4E1tYCkw3X_uy_X1dVRGaGAurtIi7w6D7bSbA-z-CwnppXjhHcyPeg6o0" 50 | }, 51 | { 52 | "id": "1416568708397.2112270797", 53 | "kind": "notes#node", 54 | "parentId": "1414521559976.1475029363", 55 | "sortValue": -1048576, 56 | "timestamps": { 57 | "kind": "notes#timestamps", 58 | "created": "2014-11-21T11:18:28.397Z", 59 | "deleted": "1970-01-01T00:00:00.000Z", 60 | "trashed": "1970-01-01T00:00:00.000Z", 61 | "updated": "2014-11-21T11:18:34.934Z", 62 | "userEdited": "1970-01-01T00:00:00.000Z" 63 | }, 64 | "type": "LIST_ITEM", 65 | "text": "New One here", 66 | "checked": false, 67 | "baseVersion": "1", 68 | "serverId": "1Q_3-dHMeS5cwCcD-7B1UvoLrB_tjX5mf2pyPjaesdp19iShqCuBdcH8fLFGcMOY", 69 | "parentServerId": "1FxjH8O4E1tYCkw3X_uy_X1dVRGaGAurtIi7w6D7bSbA-z-CwnppXjhHcyPeg6o0" 70 | } 71 | ] 72 | } 73 | 74 | { 75 | "requestHeader": { 76 | "clientVersion": { 77 | "major": "3", 78 | "minor": "0", 79 | "build": "0", 80 | "revision": "0" 81 | }, 82 | "clientPlatform": "WEB", 83 | "capabilities": [ 84 | { 85 | "type": "EC" 86 | }, 87 | { 88 | "type": "TR" 89 | }, 90 | { 91 | "type": "SH" 92 | } 93 | ], 94 | "clientSessionId": "s--1416601996534--722747297", 95 | "clientLocale": "en" 96 | }, 97 | "targetVersion": "3d589", 98 | "clientTimestamp": "2014-11-21T20:37:21.711Z", 99 | "nodes": [ 100 | { 101 | "id": "1416601996803.283217142", 102 | "kind": "notes#node", 103 | "parentId": "root", 104 | "sortValue": 23068672, 105 | "timestamps": { 106 | "kind": "notes#timestamps", 107 | "created": "2014-11-21T20:33:16.804Z", 108 | "deleted": "1970-01-01T00:00:00.000Z", 109 | "trashed": "1970-01-01T00:00:00.000Z", 110 | "updated": "2014-11-21T20:37:21.543Z", 111 | "userEdited": "2014-11-21T20:37:21.543Z" 112 | }, 113 | "type": "NOTE", 114 | "title": "", 115 | "text": "", 116 | "isArchived": false, 117 | "nodeSettings": { 118 | "checkedListItemsPolicy": "GRAVEYARD", 119 | "graveyardState": "EXPANDED", 120 | "newListItemPlacement": "BOTTOM" 121 | } 122 | }, 123 | { 124 | "id": "1416601996805.176009976", 125 | "kind": "notes#node", 126 | "parentId": "1416601996803.283217142", 127 | "sortValue": -1048576, 128 | "timestamps": { 129 | "kind": "notes#timestamps", 130 | "created": "2014-11-21T20:33:16.805Z", 131 | "deleted": "1970-01-01T00:00:00.000Z", 132 | "trashed": "1970-01-01T00:00:00.000Z", 133 | "updated": "2014-11-21T20:37:21.543Z", 134 | "userEdited": "1970-01-01T00:00:00.000Z" 135 | }, 136 | "type": "LIST_ITEM", 137 | "text": "UnmarshalJSON", 138 | "checked": false 139 | } 140 | ] 141 | } 142 | 143 | 144 | { 145 | "requestHeader": { 146 | "clientVersion": { 147 | "major": "3", 148 | "minor": "0", 149 | "build": "0", 150 | "revision": "0" 151 | }, 152 | "clientPlatform": "WEB", 153 | "capabilities": [ 154 | { 155 | "type": "EC" 156 | }, 157 | { 158 | "type": "TR" 159 | }, 160 | { 161 | "type": "SH" 162 | } 163 | ], 164 | "clientSessionId": "s--1416848191527--1981143408", 165 | "clientLocale": "en" 166 | }, 167 | "targetVersion": "3d5bc", 168 | "clientTimestamp": "2014-11-24T16:57:45.142Z", 169 | "nodes": [ 170 | { 171 | "id": "13de54348bc.8dbb26d848cd094c", 172 | "kind": "notes#node", 173 | "parentId": "root", 174 | "sortValue": 1048576, 175 | "timestamps": { 176 | "kind": "notes#timestamps", 177 | "created": "2013-04-07T16:15:59.319Z", 178 | "deleted": "1970-01-01T00:00:00.000Z", 179 | "trashed": "1970-01-01T00:00:00.000Z", 180 | "updated": "2014-11-24T16:57:44.986Z", 181 | "userEdited": "2014-11-24T16:57:44.986Z" 182 | }, 183 | "type": "LIST", 184 | "title": "Groceries ", 185 | "isArchived": false, 186 | "color": "DEFAULT", 187 | "reminders": [ 188 | { 189 | "state": "DISMISSED", 190 | "description": "Groceries - Banana ; Coke zero; cfc0 +48 more", 191 | "due": { 192 | "year": 2014, 193 | "month": 11, 194 | "day": 17, 195 | "period": "MORNING" 196 | }, 197 | "serverId": "KEEP/v2/13H74DoNrozbnflgojAaW2889wb0u_lhJdoA-6217bM2r66enCdtybZ-Z88X_xUQqZgoS/R79f4bee" 198 | } 199 | ], 200 | "nodeSettings": { 201 | "checkedListItemsPolicy": "GRAVEYARD", 202 | "graveyardState": "EXPANDED", 203 | "newListItemPlacement": "BOTTOM" 204 | }, 205 | "baseVersion": "19", 206 | "serverId": "13H74DoNrozbnflgojAaW2889wb0u_lhJdoA-6217bM2r66enCdtybZ-Z88X_xUQqZgoS" 207 | }, 208 | { 209 | "id": "141d2212de1.85ccfc64aa40808e", 210 | "kind": "notes#node", 211 | "parentId": "13de54348bc.8dbb26d848cd094c", 212 | "sortValue": 0, 213 | "timestamps": { 214 | "kind": "notes#timestamps", 215 | "created": "2013-10-19T19:11:31.809Z", 216 | "deleted": "1970-01-01T00:00:00.000Z", 217 | "trashed": "1970-01-01T00:00:00.000Z", 218 | "updated": "2014-11-24T16:57:44.986Z", 219 | "userEdited": "1970-01-01T00:00:00.000Z" 220 | }, 221 | "type": "LIST_ITEM", 222 | "text": "Pita chips", 223 | "checked": true, 224 | "baseVersion": "1", 225 | "serverId": "1h_hyNRxMe0vzsA9Pu7ifIefZAkmHPDiDfxL6alUAmPmGf0zuca1bZHIJKL1A6_HUTHEg", 226 | "parentServerId": "13H74DoNrozbnflgojAaW2889wb0u_lhJdoA-6217bM2r66enCdtybZ-Z88X_xUQqZgoS" 227 | } 228 | ] 229 | } 230 | 231 | 232 | times { 233 | 234 | "MORNING": "9am", 235 | "AFTERNOON" : "1pm", 236 | "EVENING" : "5pm", 237 | "NIGHT" : "8pm" 238 | } -------------------------------------------------------------------------------- /wire/protocol.go: -------------------------------------------------------------------------------- 1 | package wire 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | var ( 10 | // wire values for kind fields 11 | nodekind = []byte(`"notes#node"`) 12 | tskind = []byte(`"notes#timestamps"`) 13 | 14 | // wire values for nodes' (static) type field 15 | notetype = []byte(`"NOTE"`) 16 | listtype = []byte(`"LIST"`) 17 | itemtype = []byte(`"LIST_ITEM"`) 18 | 19 | // wire value for the zero time 20 | tszero = []byte(`"1970-01-01T00:00:00.000Z"`) 21 | 22 | // wire values for reminder.state 23 | notDismissedVal = []byte(`"INITIAL"`) 24 | dismissedVal = []byte(`"DISMISSED"`) 25 | 26 | // wire values for reminder.period 27 | morningVal = []byte(`"MORNING"`) 28 | afternoonVal = []byte(`"AFTERNOON"`) 29 | eveningVal = []byte(`"EVENING"`) 30 | nightVal = []byte(`"NIGHT"`) 31 | 32 | // wire values for node colors 33 | defaultVal = []byte(`"DEFAULT"`) 34 | redVal = []byte(`"RED"`) 35 | orangeVal = []byte(`"ORANGE"`) 36 | yellowVal = []byte(`"YELLOW"`) 37 | greenVal = []byte(`"GREEN"`) 38 | tealVal = []byte(`"TEAL"`) 39 | blueVal = []byte(`"BLUE"`) 40 | grayVal = []byte(`"GRAY"`) 41 | ) 42 | 43 | const tsLayout = "2006-01-02T15:04:05.000Z" 44 | 45 | // These are values for the Period field of Reminders. 46 | const ( 47 | SpecificTime Period = iota // user-specified time 48 | Morning // 9am 49 | Afternoon // 1pm 50 | Evening // 5pm 51 | Night // 8pm 52 | ) 53 | 54 | // These are values for the Color field of notes and lists. 55 | const ( 56 | DefaultColor Color = iota 57 | Red 58 | Orange 59 | Yellow 60 | Green 61 | Teal 62 | Blue 63 | Gray 64 | ) 65 | 66 | // Note represents a textual entry in google keep. Notes have a single child 67 | // node that contains the text of the note. 68 | type Note struct { 69 | ParentNode 70 | Type NoteType `json:"type"` 71 | } 72 | 73 | // List represents a list of items in google keep. Lists have one or more child 74 | // nodes that are the items in the list. 75 | type List struct { 76 | ParentNode 77 | Type ListType `json:"type"` 78 | } 79 | 80 | // Item represents a textual entry in google keep. 81 | type Item struct { 82 | Node 83 | Type ItemType `json:"type"` 84 | Checked bool `json:"checked"` 85 | Text string `json:"text"` 86 | } 87 | 88 | // ParentNode is the base representation for the top-level node for a list or 89 | // note. 90 | type ParentNode struct { 91 | Node 92 | Title string `json:"title"` 93 | Archived bool `json:"isArchived"` 94 | Color Color `json:"color"` 95 | Reminders []Reminder `json:"reminders,omitempty"` 96 | } 97 | 98 | // Node represents an identity of an item of data in google keep. 99 | type Node struct { 100 | ID string `json:"id"` 101 | Kind NodeKind `json:"kind"` 102 | ParentID string `json:"parentId"` 103 | SortValue int `json:"sortValue"` 104 | Timestamps Timestamps `json:"timestamps"` 105 | } 106 | 107 | // Timestamps is a collection of time-related data about a node. 108 | type Timestamps struct { 109 | Kind TSKind `json:"kind"` 110 | Created Timestamp `json:"created"` 111 | Deleted Timestamp `json:"deleted"` 112 | Trashed Timestamp `json:"trashed"` 113 | Updated Timestamp `json:"updated"` 114 | UserEdited Timestamp `json:"userEdited"` 115 | } 116 | 117 | // Reminder contains a time and message to tell the user about a note or list. 118 | type Reminder struct { 119 | Dismissed Dismissed `json:"state"` 120 | Description string `json:"description"` 121 | Due Time `json:"due"` 122 | } 123 | 124 | // Time represents when a Reminder should notify the user. The year, month, and 125 | // day are always specified. The time of day is specified either by a generic 126 | // Period (i.e. Morning, Afternoon, etc), or by a specific time stored in the 127 | // Hour, Minute, and Second fields. 128 | type Time struct { 129 | Year int `json:"year"` 130 | Month int `json:"month"` 131 | Day int `json:"day"` 132 | 133 | Period Period `json:"period,omitempty"` 134 | 135 | Hour int `json:"hour,omitempty"` 136 | Minute int `json:"minute,omitempty"` 137 | Second int `json:"second,omitempty"` 138 | } 139 | 140 | // Time returns the time.Time value represented by this struct. 141 | func (t *Time) Time() time.Time { 142 | var h, m, s int 143 | switch t.Period { 144 | case SpecificTime: 145 | h, m, s = t.Hour, t.Minute, t.Second 146 | case Morning: 147 | h = 9 148 | case Afternoon: 149 | h = 13 150 | case Evening: 151 | h = 17 152 | case Night: 153 | h = 20 154 | } 155 | return time.Date(t.Year, time.Month(t.Month), t.Day, h, m, s, 0, time.Now().Location()) 156 | } 157 | 158 | // Dismissed is a boolean type with custom JSON serialization. 159 | type Dismissed bool 160 | 161 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 162 | func (d Dismissed) MarshalJSON() ([]byte, error) { 163 | if d { 164 | return dismissedVal, nil 165 | } 166 | return notDismissedVal, nil 167 | } 168 | 169 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 170 | func (d *Dismissed) UnmarshalJSON(b []byte) error { 171 | if bytes.Equal(dismissedVal, b) { 172 | *d = true 173 | return nil 174 | } 175 | if bytes.Equal(notDismissedVal, b) { 176 | *d = true 177 | return nil 178 | } 179 | 180 | return fmt.Errorf("expected %q or %q, got %q", dismissedVal, notDismissedVal, b) 181 | } 182 | 183 | // Period defines how the time of a Reminder is specified. See the Time type to 184 | // see how Period is used. 185 | type Period int 186 | 187 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 188 | func (p Period) MarshalJSON() ([]byte, error) { 189 | // note, Period should always be omitempty, so we don't support serializing 190 | // the default value here. 191 | switch p { 192 | case Morning: 193 | return morningVal, nil 194 | case Afternoon: 195 | return afternoonVal, nil 196 | case Evening: 197 | return eveningVal, nil 198 | case Night: 199 | return nightVal, nil 200 | } 201 | return nil, fmt.Errorf("unsupported period value %d", p) 202 | } 203 | 204 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 205 | func (p *Period) UnmarshalJSON(b []byte) error { 206 | // note, Period should always be omitempty, so we don't support 207 | // deserializing the default value here. 208 | switch { 209 | case bytes.Equal(morningVal, b): 210 | *p = Morning 211 | case bytes.Equal(afternoonVal, b): 212 | *p = Afternoon 213 | case bytes.Equal(eveningVal, b): 214 | *p = Evening 215 | case bytes.Equal(nightVal, b): 216 | *p = Night 217 | default: 218 | return fmt.Errorf("unexpected Period value %q", b) 219 | } 220 | return nil 221 | } 222 | 223 | // Color represents the color of a list or node. 224 | type Color int 225 | 226 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 227 | func (c Color) MarshalJSON() ([]byte, error) { 228 | switch c { 229 | case DefaultColor: 230 | return defaultVal, nil 231 | case Red: 232 | return redVal, nil 233 | case Orange: 234 | return orangeVal, nil 235 | case Yellow: 236 | return yellowVal, nil 237 | case Green: 238 | return greenVal, nil 239 | case Teal: 240 | return tealVal, nil 241 | case Blue: 242 | return blueVal, nil 243 | case Gray: 244 | return grayVal, nil 245 | } 246 | return nil, fmt.Errorf("unsupported color value %d", c) 247 | } 248 | 249 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 250 | func (c *Color) UnmarshalJSON(b []byte) error { 251 | switch { 252 | case bytes.Equal(defaultVal, b): 253 | *c = DefaultColor 254 | case bytes.Equal(redVal, b): 255 | *c = Red 256 | case bytes.Equal(orangeVal, b): 257 | *c = Orange 258 | case bytes.Equal(yellowVal, b): 259 | *c = Yellow 260 | case bytes.Equal(greenVal, b): 261 | *c = Green 262 | case bytes.Equal(tealVal, b): 263 | *c = Teal 264 | case bytes.Equal(blueVal, b): 265 | *c = Blue 266 | case bytes.Equal(grayVal, b): 267 | *c = Gray 268 | default: 269 | return fmt.Errorf("unexpected Color value %q", b) 270 | } 271 | return nil 272 | } 273 | 274 | // Timestamp is a time that serializes to a string where time zero = 1970. 275 | type Timestamp time.Time 276 | 277 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 278 | func (t Timestamp) MarshalJSON() ([]byte, error) { 279 | tm := time.Time(t) 280 | if tm.IsZero() { 281 | return tszero, nil 282 | } 283 | return []byte(tm.Format(tsLayout)), nil 284 | } 285 | 286 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 287 | func (t *Timestamp) UnmarshalJSON(b []byte) error { 288 | if bytes.Equal(tszero, b) { 289 | *t = Timestamp{} 290 | return nil 291 | } 292 | tm, err := time.Parse(tsLayout, string(b)) 293 | if err != nil { 294 | return err 295 | } 296 | *t = Timestamp(tm) 297 | return nil 298 | } 299 | 300 | // TSKind outputs the correct json for the kind field of the timestamps struct. 301 | type TSKind struct{} 302 | 303 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 304 | func (TSKind) MarshalJSON() ([]byte, error) { 305 | return tskind, nil 306 | } 307 | 308 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 309 | func (*TSKind) UnmarshalJSON(b []byte) error { 310 | if !bytes.Equal(tskind, b) { 311 | return fmt.Errorf("expected %q got %q", tskind, b) 312 | } 313 | return nil 314 | } 315 | 316 | // NodeKind outputs the correct json for the kind field of the timestamps struct. 317 | type NodeKind struct{} 318 | 319 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 320 | func (NodeKind) MarshalJSON() ([]byte, error) { 321 | return nodekind, nil 322 | } 323 | 324 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 325 | func (*NodeKind) UnmarshalJSON(b []byte) error { 326 | if !bytes.Equal(nodekind, b) { 327 | return fmt.Errorf("expected %q got %q", nodekind, b) 328 | } 329 | return nil 330 | } 331 | 332 | // NoteType outputs the correct json for the type field of the note struct. 333 | type NoteType struct{} 334 | 335 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 336 | func (NoteType) MarshalJSON() ([]byte, error) { 337 | return notetype, nil 338 | } 339 | 340 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 341 | func (*NoteType) UnmarshalJSON(b []byte) error { 342 | if !bytes.Equal(notetype, b) { 343 | return fmt.Errorf("expected %q got %q", notetype, b) 344 | } 345 | return nil 346 | } 347 | 348 | // ListType outputs the correct json for the type field of the list struct. 349 | type ListType struct{} 350 | 351 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 352 | func (ListType) MarshalJSON() ([]byte, error) { 353 | return listtype, nil 354 | } 355 | 356 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 357 | func (*ListType) UnmarshalJSON(b []byte) error { 358 | if !bytes.Equal(listtype, b) { 359 | return fmt.Errorf("expected %q got %q", listtype, b) 360 | } 361 | return nil 362 | } 363 | 364 | // ItemType outputs the correct json for the type field of the list struct. 365 | type ItemType struct{} 366 | 367 | // MarshalJSON implements json.Marhsaler.MarshalJSON. 368 | func (ItemType) MarshalJSON() ([]byte, error) { 369 | return itemtype, nil 370 | } 371 | 372 | // UnmarshalJSON implements json.Unmarhsaler.UnmarshalJSON. 373 | func (*ItemType) UnmarshalJSON(b []byte) error { 374 | if !bytes.Equal(itemtype, b) { 375 | return fmt.Errorf("expected %q got %q", itemtype, b) 376 | } 377 | return nil 378 | } 379 | --------------------------------------------------------------------------------