convert.go (3168B)
1 // Copyright 2019 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package types
15
16 import (
17 "encoding/json"
18 "fmt"
19 "html/template"
20 "reflect"
21 "time"
22
23 "github.com/spf13/cast"
24 )
25
26 // ToDuration converts v to time.Duration.
27 // See ToDurationE if you need to handle errors.
28 func ToDuration(v any) time.Duration {
29 d, _ := ToDurationE(v)
30 return d
31 }
32
33 // ToDurationE converts v to time.Duration.
34 func ToDurationE(v any) (time.Duration, error) {
35 if n := cast.ToInt(v); n > 0 {
36 return time.Duration(n) * time.Millisecond, nil
37 }
38 d, err := time.ParseDuration(cast.ToString(v))
39 if err != nil {
40 return 0, fmt.Errorf("cannot convert %v to time.Duration", v)
41 }
42 return d, nil
43 }
44
45 // ToStringSlicePreserveString is the same as ToStringSlicePreserveStringE,
46 // but it never fails.
47 func ToStringSlicePreserveString(v any) []string {
48 vv, _ := ToStringSlicePreserveStringE(v)
49 return vv
50 }
51
52 // ToStringSlicePreserveStringE converts v to a string slice.
53 // If v is a string, it will be wrapped in a string slice.
54 func ToStringSlicePreserveStringE(v any) ([]string, error) {
55 if v == nil {
56 return nil, nil
57 }
58 if sds, ok := v.(string); ok {
59 return []string{sds}, nil
60 }
61 result, err := cast.ToStringSliceE(v)
62 if err == nil {
63 return result, nil
64 }
65
66 // Probably []int or similar. Fall back to reflect.
67 vv := reflect.ValueOf(v)
68
69 switch vv.Kind() {
70 case reflect.Slice, reflect.Array:
71 result = make([]string, vv.Len())
72 for i := 0; i < vv.Len(); i++ {
73 s, err := cast.ToStringE(vv.Index(i).Interface())
74 if err != nil {
75 return nil, err
76 }
77 result[i] = s
78 }
79 return result, nil
80 default:
81 return nil, fmt.Errorf("failed to convert %T to a string slice", v)
82 }
83
84 }
85
86 // TypeToString converts v to a string if it's a valid string type.
87 // Note that this will not try to convert numeric values etc.,
88 // use ToString for that.
89 func TypeToString(v any) (string, bool) {
90 switch s := v.(type) {
91 case string:
92 return s, true
93 case template.HTML:
94 return string(s), true
95 case template.CSS:
96 return string(s), true
97 case template.HTMLAttr:
98 return string(s), true
99 case template.JS:
100 return string(s), true
101 case template.JSStr:
102 return string(s), true
103 case template.URL:
104 return string(s), true
105 case template.Srcset:
106 return string(s), true
107 }
108
109 return "", false
110 }
111
112 // ToString converts v to a string.
113 func ToString(v any) string {
114 s, _ := ToStringE(v)
115 return s
116 }
117
118 // ToStringE converts v to a string.
119 func ToStringE(v any) (string, error) {
120 if s, ok := TypeToString(v); ok {
121 return s, nil
122 }
123
124 switch s := v.(type) {
125 case json.RawMessage:
126 return string(s), nil
127 default:
128 return cast.ToStringE(v)
129 }
130 }