@@ -19,8 +19,11 @@ package gowarc
1919import (
2020 "fmt"
2121 "io"
22+ "net/http"
2223 "sort"
24+ "strconv"
2325 "strings"
26+ "time"
2427)
2528
2629type nameValue struct {
@@ -32,21 +35,64 @@ func (n *nameValue) String() string {
3235 return n .Name + ": " + n .Value
3336}
3437
38+ // WarcFields represents the key value pairs in a WARC-record header.
39+ //
40+ // It is also used for representing the record block of records with content-type "application/warc-fields".
41+ //
42+ // All key-manipulating functions take case-insensitive keys and modify them to their canonical form.
3543type WarcFields []* nameValue
3644
37- // Get gets the first value associated with the given key. It is case insensitive.
38- // If the key doesn't exist or there are no values associated with the key, Get returns "".
45+ func (wf * WarcFields ) CanonicalHeaderKey (s string ) string {
46+
47+ return http .CanonicalHeaderKey (s )
48+ }
49+
50+ // Get gets the first value associated with the given key. It is case-insensitive.
51+ // If the key doesn't exist or there are no values associated with the key, Get returns the empty string.
3952// To access multiple values of a key, use GetAll.
40- func (wf * WarcFields ) Get (name string ) string {
53+ func (wf * WarcFields ) Get (key string ) string {
54+ key , _ = normalizeName (key )
4155 for _ , nv := range * wf {
42- if nv .Name == name {
56+ if nv .Name == key {
4357 return nv .Value
4458 }
4559 }
4660 return ""
4761}
4862
63+ // GetInt is like Get, but converts the field value to int.
64+ func (wf * WarcFields ) GetInt (key string ) (int , error ) {
65+ if ! wf .Has (key ) {
66+ return 0 , fmt .Errorf ("missing field %s" , key )
67+ }
68+ return strconv .Atoi (wf .Get (key ))
69+ }
70+
71+ // GetInt64 is like Get, but converts the field value to int64.
72+ func (wf * WarcFields ) GetInt64 (name string ) (int64 , error ) {
73+ if ! wf .Has (name ) {
74+ return 0 , fmt .Errorf ("missing field %s" , name )
75+ }
76+ return strconv .ParseInt (wf .Get (name ), 10 , 64 )
77+ }
78+
79+ // GetTime is like Get, but converts the field value to time.Time.
80+ // The field is expected to be in RFC 3339 format.
81+ func (wf * WarcFields ) GetTime (name string ) (time.Time , error ) {
82+ if ! wf .Has (name ) {
83+ return time.Time {}, fmt .Errorf ("missing field %s" , name )
84+ }
85+ return time .Parse (time .RFC3339 , wf .Get (name ))
86+ }
87+
88+ // GetId is like Get, but removes the surrounding '<' and '>' from the field value.
89+ func (wf * WarcFields ) GetId (name string ) string {
90+ return strings .Trim (wf .Get (name ), "<>" )
91+ }
92+
93+ // GetAll returns all values associated with the given key. It is case-insensitive.
4994func (wf * WarcFields ) GetAll (name string ) []string {
95+ name , _ = normalizeName (name )
5096 var result []string
5197 for _ , nv := range * wf {
5298 if nv .Name == name {
@@ -56,7 +102,10 @@ func (wf *WarcFields) GetAll(name string) []string {
56102 return result
57103}
58104
105+ // Has returns true if field exists.
106+ // This can be used to separate a missing field from a field for which value is the empty string.
59107func (wf * WarcFields ) Has (name string ) bool {
108+ name , _ = normalizeName (name )
60109 for _ , nv := range * wf {
61110 if nv .Name == name {
62111 return true
@@ -65,11 +114,51 @@ func (wf *WarcFields) Has(name string) bool {
65114 return false
66115}
67116
117+ // Add adds the key, value pair to the header.
118+ // It appends to any existing values associated with key. The key is case-insensitive.
68119func (wf * WarcFields ) Add (name string , value string ) {
120+ name , _ = normalizeName (name )
69121 * wf = append (* wf , & nameValue {Name : name , Value : value })
70122}
71123
124+ // AddInt adds the key, value pair to the header.
125+ // It appends to any existing values associated with key. The key is case-insensitive.
126+ func (wf * WarcFields ) AddInt (name string , value int ) {
127+ wf .Add (name , strconv .Itoa (value ))
128+ }
129+
130+ // AddInt64 adds the key, value pair to the header.
131+ // It appends to any existing values associated with key. The key is case-insensitive.
132+ func (wf * WarcFields ) AddInt64 (name string , value int64 ) {
133+ wf .Add (name , strconv .FormatInt (value , 10 ))
134+ }
135+
136+ // AddTime adds the key, value pair to the header.
137+ // It appends to any existing values associated with key. The key is case-insensitive.
138+ //
139+ // The value is converted to RFC 3339 format.
140+ func (wf * WarcFields ) AddTime (name string , value time.Time ) {
141+ wf .Add (name , value .UTC ().Format (time .RFC3339 ))
142+ }
143+
144+ // AddId adds the key, value pair to the header.
145+ // It appends to any existing values associated with key. The key is case-insensitive.
146+ //
147+ // The value is surrounded with '<' and '>' if not already present.
148+ func (wf * WarcFields ) AddId (name , value string ) {
149+ if len (value ) == 0 {
150+ return
151+ }
152+ if value [0 ] != '<' && value [len (value )- 1 ] != '>' {
153+ value = "<" + value + ">"
154+ }
155+ wf .Add (name , value )
156+ }
157+
158+ // Set sets the header entries associated with key to the single element value.
159+ // It replaces any existing values associated with key. The key is case-insensitive
72160func (wf * WarcFields ) Set (name string , value string ) {
161+ name , _ = normalizeName (name )
73162 isSet := false
74163 for idx , nv := range * wf {
75164 if nv .Name == name {
@@ -86,22 +175,62 @@ func (wf *WarcFields) Set(name string, value string) {
86175 }
87176}
88177
89- func (wf * WarcFields ) Delete (name string ) {
178+ // SetInt sets the header entries associated with key to the single element value.
179+ // It replaces any existing values associated with key. The key is case-insensitive
180+ func (wf * WarcFields ) SetInt (name string , value int ) {
181+ wf .Set (name , strconv .Itoa (value ))
182+ }
183+
184+ // SetInt64 sets the header entries associated with key to the single element value.
185+ // It replaces any existing values associated with key. The key is case-insensitive
186+ func (wf * WarcFields ) SetInt64 (name string , value int64 ) {
187+ wf .Set (name , strconv .FormatInt (value , 10 ))
188+ }
189+
190+ // SetTime sets the header entries associated with key to the single element value.
191+ // It replaces any existing values associated with key. The key is case-insensitive
192+ //
193+ // The value is converted to RFC 3339 format.
194+ func (wf * WarcFields ) SetTime (name string , value time.Time ) {
195+ wf .Set (name , value .UTC ().Format (time .RFC3339 ))
196+ }
197+
198+ // SetId sets the header entries associated with key to the single element value.
199+ // It replaces any existing values associated with key. The key is case-insensitive
200+ //
201+ // The value is surrounded with '<' and '>' if not already present.
202+ func (wf * WarcFields ) SetId (name , value string ) {
203+ if len (value ) == 0 {
204+ return
205+ }
206+ if value [0 ] != '<' && value [len (value )- 1 ] != '>' {
207+ value = "<" + value + ">"
208+ }
209+ wf .Set (name , value )
210+ }
211+
212+ // Delete deletes the values associated with key. The key is case-insensitive.
213+ func (wf * WarcFields ) Delete (key string ) {
214+ key , _ = normalizeName (key )
90215 var result []* nameValue
91216 for _ , nv := range * wf {
92- if nv .Name != name {
217+ if nv .Name != key {
93218 result = append (result , nv )
94219 }
95220 }
96221 * wf = result
97222}
98223
224+ // Sort sorts the fields in lexicographical order.
225+ //
226+ // Only field names are sorted. Order of values for a repeated field is kept as is.
99227func (wf * WarcFields ) Sort () {
100228 sort .SliceStable (* wf , func (i , j int ) bool {
101229 return (* wf )[i ].Name < (* wf )[j ].Name
102230 })
103231}
104232
233+ // Write implements the io.Writer interface.
105234func (wf * WarcFields ) Write (w io.Writer ) (bytesWritten int64 , err error ) {
106235 var n int
107236 for _ , field := range * wf {
@@ -122,6 +251,7 @@ func (wf *WarcFields) String() string {
122251 return sb .String ()
123252}
124253
254+ // clone creates a new deep copy.
125255func (wf WarcFields ) clone () * WarcFields {
126256 r := WarcFields {}
127257 for _ , p := range wf {
0 commit comments