append.go - hugo - [fork] hugo port for 9front
HTML git clone https://git.drkhsh.at/hugo.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
append.go (3878B)
---
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 collections
15
16 import (
17 "fmt"
18 "reflect"
19 )
20
21 // Append appends from to a slice to and returns the resulting slice.
22 // If length of from is one and the only element is a slice of same type as to,
23 // it will be appended.
24 func Append(to any, from ...any) (any, error) {
25 if len(from) == 0 {
26 return to, nil
27 }
28 tov, toIsNil := indirect(reflect.ValueOf(to))
29
30 toIsNil = toIsNil || to == nil
31 var tot reflect.Type
32
33 if !toIsNil {
34 if tov.Kind() == reflect.Slice {
35 // Create a copy of tov, so we don't modify the original.
36 c := reflect.MakeSlice(tov.Type(), tov.Len(), tov.Len()+len(from))
37 reflect.Copy(c, tov)
38 tov = c
39 }
40
41 if tov.Kind() != reflect.Slice {
42 return nil, fmt.Errorf("expected a slice, got %T", to)
43 }
44
45 tot = tov.Type().Elem()
46 if tot.Kind() == reflect.Slice {
47 totvt := tot.Elem()
48 fromvs := make([]reflect.Value, len(from))
49 for i, f := range from {
50 fromv := reflect.ValueOf(f)
51 fromt := fromv.Type()
52 if fromt.Kind() == reflect.Slice {
53 fromt = fromt.Elem()
54 }
55 if totvt != fromt {
56 return nil, fmt.Errorf("cannot append slice of %s to slice of %s", fromt, totvt)
57 } else {
58 fromvs[i] = fromv
59 }
60 }
61 return reflect.Append(tov, fromvs...).Interface(), nil
62
63 }
64
65 toIsNil = tov.Len() == 0
66
67 if len(from) == 1 {
68 fromv := reflect.ValueOf(from[0])
69 if !fromv.IsValid() {
70 // from[0] is nil
71 return appendToInterfaceSliceFromValues(tov, fromv)
72 }
73 fromt := fromv.Type()
74 if fromt.Kind() == reflect.Slice {
75 fromt = fromt.Elem()
76 }
77 if fromv.Kind() == reflect.Slice {
78 if toIsNil {
79 // If we get nil []string, we just return the []string
80 return from[0], nil
81 }
82
83 // If we get []string []string, we append the from slice to to
84 if tot == fromt {
85 return reflect.AppendSlice(tov, fromv).Interface(), nil
86 } else if !fromt.AssignableTo(tot) {
87 // Fall back to a []interface{} slice.
88 return appendToInterfaceSliceFromValues(tov, fromv)
89 }
90
91 }
92 }
93 }
94
95 if toIsNil {
96 return Slice(from...), nil
97 }
98
99 for _, f := range from {
100 fv := reflect.ValueOf(f)
101 if !fv.IsValid() || !fv.Type().AssignableTo(tot) {
102 // Fall back to a []interface{} slice.
103 tov, _ := indirect(reflect.ValueOf(to))
104 return appendToInterfaceSlice(tov, from...)
105 }
106 tov = reflect.Append(tov, fv)
107 }
108
109 return tov.Interface(), nil
110 }
111
112 func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]any, error) {
113 var tos []any
114
115 for _, slice := range []reflect.Value{slice1, slice2} {
116 if !slice.IsValid() {
117 tos = append(tos, nil)
118 continue
119 }
120 for i := range slice.Len() {
121 tos = append(tos, slice.Index(i).Interface())
122 }
123 }
124
125 return tos, nil
126 }
127
128 func appendToInterfaceSlice(tov reflect.Value, from ...any) ([]any, error) {
129 var tos []any
130
131 for i := range tov.Len() {
132 tos = append(tos, tov.Index(i).Interface())
133 }
134
135 tos = append(tos, from...)
136
137 return tos, nil
138 }
139
140 // indirect is borrowed from the Go stdlib: 'text/template/exec.go'
141 // TODO(bep) consolidate
142 func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
143 for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
144 if v.IsNil() {
145 return v, true
146 }
147 if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
148 break
149 }
150 }
151 return v, false
152 }