-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
entity.go
879 lines (821 loc) · 22.4 KB
/
entity.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
package mediawiki
import (
"bytes"
"fmt"
"math/big"
"regexp"
"strconv"
"time"
"gitlab.com/tozd/go/errors"
"gitlab.com/tozd/go/x"
"golang.org/x/text/unicode/norm"
)
var timeRegex = regexp.MustCompile(`^([+-]\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$`)
type EntityType int
const (
Item EntityType = iota
Property
MediaInfo
)
func (t EntityType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case Item:
buffer.WriteString("item")
case Property:
buffer.WriteString("property")
case MediaInfo:
buffer.WriteString("mediainfo")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (t *EntityType) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "item":
*t = Item
case "property":
*t = Property
case "mediainfo":
*t = MediaInfo
default:
errE := errors.WithMessage(ErrInvalidValue, "entity type")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type WikiBaseEntityType int
const (
ItemType WikiBaseEntityType = iota
PropertyType
LexemeType
FormType
SenseType
)
func (t WikiBaseEntityType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case ItemType:
buffer.WriteString("item")
case PropertyType:
buffer.WriteString("property")
case LexemeType:
buffer.WriteString("lexeme")
case FormType:
buffer.WriteString("form")
case SenseType:
buffer.WriteString("sense")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (t *WikiBaseEntityType) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "item":
*t = ItemType
case "property":
*t = PropertyType
case "lexeme":
*t = LexemeType
case "form":
*t = FormType
case "sense":
*t = SenseType
default:
errE := errors.WithMessage(ErrInvalidValue, "wikibase entity type")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type StatementType int
const (
StatementT StatementType = iota
)
func (t StatementType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t { //nolint:gocritic
case StatementT:
buffer.WriteString("statement")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (t *StatementType) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "statement":
*t = StatementT
default:
errE := errors.WithMessage(ErrInvalidValue, "statement type")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type StatementRank int
const (
Preferred StatementRank = iota
Normal
Deprecated
)
func (r StatementRank) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch r {
case Preferred:
buffer.WriteString("preferred")
case Normal:
buffer.WriteString("normal")
case Deprecated:
buffer.WriteString("deprecated")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (r *StatementRank) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "preferred":
*r = Preferred
case "normal":
*r = Normal
case "deprecated":
*r = Deprecated
default:
errE := errors.WithMessage(ErrInvalidValue, "statement rank")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type SnakType int
const (
Value SnakType = iota
SomeValue
NoValue
)
func (t SnakType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case Value:
buffer.WriteString("value")
case SomeValue:
buffer.WriteString("somevalue")
case NoValue:
buffer.WriteString("novalue")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (t *SnakType) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "value":
*t = Value
case "somevalue":
*t = SomeValue
case "novalue":
*t = NoValue
default:
errE := errors.WithMessage(ErrInvalidValue, "snak type")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type DataType int
const (
WikiBaseItem DataType = iota
ExternalID
String
Quantity
Time
GlobeCoordinate
CommonsMedia
MonolingualText
URL
GeoShape
WikiBaseLexeme
WikiBaseSense
WikiBaseProperty
Math
MusicalNotation
WikiBaseForm
TabularData
)
func (t DataType) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case WikiBaseItem:
buffer.WriteString("wikibase-item")
case ExternalID:
buffer.WriteString("external-id")
case String:
buffer.WriteString("string")
case Quantity:
buffer.WriteString("quantity")
case Time:
buffer.WriteString("time")
case GlobeCoordinate:
buffer.WriteString("globe-coordinate")
case CommonsMedia:
buffer.WriteString("commonsMedia")
case MonolingualText:
buffer.WriteString("monolingualtext")
case URL:
buffer.WriteString("url")
case GeoShape:
buffer.WriteString("geo-shape")
case WikiBaseLexeme:
buffer.WriteString("wikibase-lexeme")
case WikiBaseSense:
buffer.WriteString("wikibase-sense")
case WikiBaseProperty:
buffer.WriteString("wikibase-property")
case Math:
buffer.WriteString("math")
case MusicalNotation:
buffer.WriteString("musical-notation")
case WikiBaseForm:
buffer.WriteString("wikibase-form")
case TabularData:
buffer.WriteString("tabular-data")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
func (t *DataType) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "wikibase-item":
*t = WikiBaseItem
case "external-id":
*t = ExternalID
case "string":
*t = String
case "quantity":
*t = Quantity
case "time":
*t = Time
case "globe-coordinate":
*t = GlobeCoordinate
case "commonsMedia":
*t = CommonsMedia
case "monolingualtext":
*t = MonolingualText
case "url":
*t = URL
case "geo-shape":
*t = GeoShape
case "wikibase-lexeme":
*t = WikiBaseLexeme
case "wikibase-sense":
*t = WikiBaseSense
case "wikibase-property":
*t = WikiBaseProperty
case "math":
*t = Math
case "musical-notation":
*t = MusicalNotation
case "wikibase-form":
*t = WikiBaseForm
case "tabular-data":
*t = TabularData
default:
errE := errors.WithMessage(ErrInvalidValue, "data type")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
type TimePrecision int
const (
BillionYears TimePrecision = iota
HoundredMillionYears
TenMillionYears
MillionYears
HoundredMillenniums
TenMillenniums
Millennium
Century
Decade
Year
Month
Day
Hour
Minute
Second
)
type CalendarModel int
const (
Gregorian CalendarModel = iota
Julian
)
// MarshalJSON implements json.Marshaler interface for CalendarModel.
//
// Go enumeration values are converted to corresponding calendar Wikidata URIs.
// Those might be different (but equivalent) than what it was in the source dump.
func (t CalendarModel) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString(`"`)
switch t {
case Gregorian:
buffer.WriteString("https://www.wikidata.org/wiki/Q1985727")
case Julian:
buffer.WriteString("https://www.wikidata.org/wiki/Q1985786")
}
buffer.WriteString(`"`)
return buffer.Bytes(), nil
}
// UnmarshalJSON implements json.Unmarshaler interface for CalendarModel.
//
// It normalizes calendar Wikidata URIs to Go enumeration values.
func (t *CalendarModel) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
switch s {
case "https://www.wikidata.org/wiki/Q1985727", "http://www.wikidata.org/entity/Q1985727":
*t = Gregorian
case "https://www.wikidata.org/wiki/Q12138", "http://www.wikidata.org/entity/Q12138":
// Officially it should not be used, but it has been found in data.
*t = Gregorian
case "https://www.wikidata.org/wiki/Q1985786", "http://www.wikidata.org/entity/Q1985786":
*t = Julian
case "https://www.wikidata.org/wiki/Q11184", "http://www.wikidata.org/entity/Q11184":
// Officially it should not be used, but just in case it is used.
*t = Julian
default:
errE := errors.WithMessage(ErrInvalidValue, "calendar model")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
// ErrorValue represents an error with the value.
//
// When JSON representation contains an error, only error is provided
// as a Go value because any other field might be fail to parse.
type ErrorValue string
type StringValue string
type WikiBaseEntityIDValue struct {
Type WikiBaseEntityType `json:"entity-type"` //nolint:tagliatelle
ID string `json:"id"`
}
type GlobeCoordinateValue struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Precision float64 `json:"precision"`
Globe string `json:"globe"`
}
type MonolingualTextValue struct {
Language string `json:"language"`
Text string `json:"text"`
}
// Amount is an arbitrary precision number and extends big.Rat.
type Amount struct {
big.Rat
}
// MarshalJSON implements json.Marshaler interface for Amount.
func (a Amount) MarshalJSON() ([]byte, error) {
b := new(bytes.Buffer)
b.WriteString(`"`)
if a.Sign() >= 0 {
// Sign is required always.
b.WriteString("+")
}
b.WriteString(a.String())
b.WriteString(`"`)
return b.Bytes(), nil
}
// UnmarshalJSON implements json.Unmarshaler interface for Amount.
func (a *Amount) UnmarshalJSON(b []byte) error {
var s string
errE := x.Unmarshal(b, &s)
if errE != nil {
return errE
}
_, ok := a.SetString(s)
if !ok {
errE := errors.WithMessage(ErrInvalidValue, "amount")
errors.Details(errE)["value"] = s
return errE
}
return nil
}
func (a *Amount) String() string {
l, q := x.RatPrecision(&a.Rat)
return a.FloatString(l + q)
}
type QuantityValue struct {
Amount Amount `json:"amount"`
UpperBound *Amount `json:"upperBound,omitempty"` //nolint:tagliatelle
LowerBound *Amount `json:"lowerBound,omitempty"` //nolint:tagliatelle
Unit string `json:"unit"`
}
// TimeValue represents a time value.
//
// While Time is a regular time.Time struct with nanoseconds precision,
// its real precision is available by Precision.
//
// Note that Wikidata uses historical numbering, in which year 0 is undefined
// and 1 BCE is represented by -1, but time.Time uses astronomical numbering,
// in which 1 BCE is represented by 0.
type TimeValue struct {
Time time.Time `json:"time"`
Precision TimePrecision `json:"precision"`
Calendar CalendarModel `json:"calendar"`
}
// MarshalJSON implements json.Marshaler interface for TimeValue.
func (v TimeValue) MarshalJSON() ([]byte, error) {
type t struct {
Time string `json:"time"`
Precision TimePrecision `json:"precision"`
Calendar CalendarModel `json:"calendarmodel"`
}
formatedTime := formatTime(v.Time, v.Precision)
return x.MarshalWithoutEscapeHTML(t{
formatedTime,
v.Precision,
v.Calendar,
})
}
// UnmarshalJSON implements json.Unmarshaler interface for TimeValue.
func (v *TimeValue) UnmarshalJSON(b []byte) error {
type t struct {
Time string `json:"time"`
Precision TimePrecision `json:"precision"`
Calendar CalendarModel `json:"calendarmodel"`
}
var d t
errE := x.UnmarshalWithoutUnknownFields(b, &d)
if errE != nil {
return errE
}
v.Time, errE = parseTime(d.Time)
if errE != nil {
return errors.WithMessage(errE, "time value")
}
v.Precision = d.Precision
v.Calendar = d.Calendar
return nil
}
// DataValue provides parsed value as Go value in Value.
//
// Value can be one of ErrorValue, StringValue, WikiBaseEntityIDValue,
// GlobeCoordinateValue, MonolingualTextValue, QuantityValue, and TimeValue.
type DataValue struct {
Value interface{} `json:"value"`
}
func formatTime(t time.Time, p TimePrecision) string {
t = t.UTC()
year := t.Year()
if year < 1 {
// Wikidata uses historical numbering, in which year 0 is undefined,
// but Go uses astronomical numbering, so we subtract 1 here.
year--
}
month := t.Month()
if p < Month {
// Wikidata uses 0 when month is unknown or insignificant.
month = 0
}
day := t.Day()
if p < Day {
// Wikidata uses 0 when day is unknown or insignificant.
day = 0
}
return fmt.Sprintf("%+05d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, t.Hour(), t.Minute(), t.Second())
}
// MarshalJSON implements json.Marshaler interface for DataValue.
//
// JSON representation of Go values might be different (but equivalent)
// than what it was in the source dump.
func (v DataValue) MarshalJSON() ([]byte, error) {
switch value := v.Value.(type) {
case ErrorValue:
return x.MarshalWithoutEscapeHTML(struct {
Error ErrorValue `json:"error"`
}{value})
case StringValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value StringValue `json:"value"`
}{"string", value})
case WikiBaseEntityIDValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value WikiBaseEntityIDValue `json:"value"`
}{"wikibase-entityid", value})
case GlobeCoordinateValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value GlobeCoordinateValue `json:"value"`
}{"globecoordinate", value})
case MonolingualTextValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value MonolingualTextValue `json:"value"`
}{"monolingualtext", value})
case QuantityValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value QuantityValue `json:"value"`
}{"quantity", value})
case TimeValue:
return x.MarshalWithoutEscapeHTML(struct {
Type string `json:"type"`
Value TimeValue `json:"value"`
}{"time", value})
}
errE := errors.WithMessage(ErrUnexpectedType, "data value")
errors.Details(errE)["type"] = fmt.Sprintf("%T", v.Value)
return nil, errE
}
func parseTime(t string) (time.Time, errors.E) {
match := timeRegex.FindStringSubmatch(t)
if match == nil {
errE := errors.WithMessage(ErrInvalidValue, "time")
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
year, err := strconv.ParseInt(match[1], 10, 0)
if err != nil {
errE := errors.Errorf("year: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
if year < 0 {
// Wikidata uses historical numbering, in which year 0 is undefined,
// but Go uses astronomical numbering, so we add 1 here.
year++
} else if year == 0 {
errE := errors.Errorf("year: %w: cannot be 0", ErrInvalidValue)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
month, err := strconv.ParseInt(match[2], 10, 0)
if err != nil {
errE := errors.Errorf("month: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
if month == 0 {
// Wikidata uses 0 when month is unknown or insignificant.
// Go does not support this, so we set it to 1 here.
month = 1
}
day, err := strconv.ParseInt(match[3], 10, 0)
if err != nil {
errE := errors.Errorf("day: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
if day == 0 {
// Wikidata uses 0 when day is unknown or insignificant.
// Go does not support this, so we set it to 1 here.
day = 1
}
hour, err := strconv.ParseInt(match[4], 10, 0)
if err != nil {
errE := errors.Errorf("hour: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
minute, err := strconv.ParseInt(match[5], 10, 0)
if err != nil {
errE := errors.Errorf("minute: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
second, err := strconv.ParseInt(match[6], 10, 0)
if err != nil {
errE := errors.Errorf("second: %w: %w", ErrInvalidValue, err)
errors.Details(errE)["value"] = t
return time.Time{}, errE
}
return time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.UTC), nil
}
// UnmarshalJSON implements json.Unmarshaler interface for DataValue.
//
// It normalizes JSON representation to Go values.
func (v *DataValue) UnmarshalJSON(b []byte) error {
var t struct {
Type string `json:"type"`
Error string `json:"error"`
}
// We do not use UnmarshalWithoutUnknownFields because if there
// is no "error" field, there is "value" field.
errE := x.Unmarshal(b, &t)
if errE != nil {
return errE
}
if t.Error != "" {
v.Value = ErrorValue(norm.NFC.String(t.Error))
return nil
}
switch t.Type {
case "string":
var t struct {
Type string `json:"type"`
Value string `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
v.Value = StringValue(norm.NFC.String(t.Value))
case "wikibase-entityid":
var t struct {
Type string `json:"type"`
// We do not use WikiBaseEntityIDValue because of extra fields.
Value struct {
Type WikiBaseEntityType `json:"entity-type"` //nolint:tagliatelle
ID string `json:"id"`
// Not available for all entity types. Not recommended to be used. We ignore it.
NumericID int `json:"numeric-id"` //nolint:tagliatelle
} `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
v.Value = WikiBaseEntityIDValue{
Type: t.Value.Type,
ID: norm.NFC.String(t.Value.ID),
}
case "globecoordinate":
var t struct {
Type string `json:"type"`
// We do not use GlobeCoordinateValue because of extra fields.
Value struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
// Altitude is deprecated and no longer used. We ignore it.
Altitude float64 `json:"altitude"`
Precision float64 `json:"precision"`
Globe string `json:"globe"`
} `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
v.Value = GlobeCoordinateValue{
Latitude: t.Value.Latitude,
Longitude: t.Value.Longitude,
Precision: t.Value.Precision,
Globe: t.Value.Globe,
}
case "monolingualtext":
var t struct {
Type string `json:"type"`
Value MonolingualTextValue `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
t.Value.Text = norm.NFC.String(t.Value.Text)
v.Value = t.Value
case "quantity":
var t struct {
Type string `json:"type"`
Value QuantityValue `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
v.Value = t.Value
case "time":
var t struct {
Type string `json:"type"`
// We do not use TimeValue because of extra fields.
Value struct {
Time string `json:"time"`
Precision TimePrecision `json:"precision"`
Calendar CalendarModel `json:"calendarmodel"`
// Defined and declared not used, but sometimes still set. We ignore it.
Timezone int64 `json:"timezone"`
// Defined and declared not used, but sometimes still set. We ignore it.
Before int64 `json:"before"`
// Defined and declared not used, but sometimes still set. We ignore it.
After int64 `json:"after"`
} `json:"value"`
}
errE := x.UnmarshalWithoutUnknownFields(b, &t)
if errE != nil {
return errE
}
parsedTime, errE := parseTime(t.Value.Time)
if errE != nil {
v.Value = ErrorValue(fmt.Sprintf("%s: %s", errE.Error(), t.Value.Time))
} else {
v.Value = TimeValue{
Time: parsedTime,
Precision: t.Value.Precision,
Calendar: t.Value.Calendar,
}
}
default:
errE := errors.WithMessage(ErrInvalidValue, "data value")
errors.Details(errE)["value"] = t.Type
errors.Details(errE)["json"] = string(b)
return errE
}
return nil
}
type LanguageValue struct {
Language string `json:"language"`
Value string `json:"value"`
}
type SiteLink struct {
Site string `json:"site"`
Title string `json:"title"`
Badges []string `json:"badges,omitempty"`
URL string `json:"url,omitempty"`
}
type Snak struct {
Hash string `json:"hash,omitempty"`
SnakType SnakType `json:"snaktype"`
Property string `json:"property"`
DataType *DataType `json:"datatype,omitempty"`
DataValue *DataValue `json:"datavalue,omitempty"`
}
type Reference struct {
Hash string `json:"hash,omitempty"`
Snaks map[string][]Snak `json:"snaks,omitempty"`
SnaksOrder []string `json:"snaks-order,omitempty"` //nolint:tagliatelle
}
type Statement struct {
ID string `json:"id"`
Type StatementType `json:"type"`
MainSnak Snak `json:"mainsnak"`
Rank StatementRank `json:"rank"`
Qualifiers map[string][]Snak `json:"qualifiers,omitempty"`
QualifiersOrder []string `json:"qualifiers-order,omitempty"` //nolint:tagliatelle
References []Reference `json:"references,omitempty"`
}
// Entity is a Wikidata entities JSON dump entity.
type Entity struct {
ID string `json:"id"`
PageID int64 `json:"pageid"`
Namespace int `json:"ns"`
Title string `json:"title"`
Modified time.Time `json:"modified"`
Type EntityType `json:"type"`
DataType *DataType `json:"datatype,omitempty"`
Labels map[string]LanguageValue `json:"labels,omitempty"`
Descriptions map[string]LanguageValue `json:"descriptions,omitempty"`
Aliases map[string][]LanguageValue `json:"aliases,omitempty"`
Claims map[string][]Statement `json:"claims,omitempty"`
SiteLinks map[string]SiteLink `json:"sitelinks,omitempty"`
LastRevID int64 `json:"lastrevid"`
}
// CommonsEntity is a Wikimedia Commons entities JSON dump entity.
// The only difference is that it Claims are named "statements" in
// the JSON. We use it to parse JSON and then we cast it to Entity.
type commonsEntity struct {
ID string `json:"id"`
PageID int64 `json:"pageid"`
Namespace int `json:"ns"`
Title string `json:"title"`
Modified time.Time `json:"modified"`
Type EntityType `json:"type"`
DataType *DataType `json:"datatype,omitempty"`
Labels map[string]LanguageValue `json:"labels,omitempty"`
Descriptions map[string]LanguageValue `json:"descriptions,omitempty"`
Aliases map[string][]LanguageValue `json:"aliases,omitempty"`
Claims map[string][]Statement `json:"statements,omitempty"`
SiteLinks map[string]SiteLink `json:"sitelinks,omitempty"`
LastRevID int64 `json:"lastrevid"`
}