URI: 
       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 }