github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/copy.go (about) 1 package f 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "reflect" 9 "time" 10 ) 11 12 // CopyInterface for delegating copy process to type 13 type CopyInterface interface { 14 Copy() interface{} 15 } 16 17 // CloneInterface for delegating copy process to type 18 type CloneInterface interface { 19 Clone() interface{} 20 } 21 22 // Copy create a deep copy of whatever is passed to it and returns the copy 23 // in an interface{}. The returned value will need to be asserted to the correct type. 24 func Clone(src interface{}) interface{} { 25 if src == nil { 26 return nil 27 } 28 29 // Make the interface a reflect.Value 30 original := reflect.ValueOf(src) 31 32 // Make a copy of the same type as the original. 33 cpy := reflect.New(original.Type()).Elem() 34 35 // Recursively copy the original. 36 copyRecursive(original, cpy) 37 38 // Return the copy as an interface. 39 return cpy.Interface() 40 } 41 42 // copyRecursive does the actual copying of the interface. It currently has 43 // limited support for what it can handle. Add as needed. 44 func copyRecursive(original, cpy reflect.Value) { 45 // check for implement CloneInterface 46 if original.CanInterface() { 47 if copier, ok := original.Interface().(CopyInterface); ok { 48 cpy.Set(reflect.ValueOf(copier.Copy())) 49 return 50 } 51 if copier, ok := original.Interface().(CloneInterface); ok { 52 cpy.Set(reflect.ValueOf(copier.Clone())) 53 return 54 } 55 } 56 57 // handle according to original's Kind 58 switch original.Kind() { 59 case reflect.Ptr: 60 // GetHeader the actual value being pointed to. 61 originalValue := original.Elem() 62 63 // if it isn't valid, return. 64 if !originalValue.IsValid() { 65 return 66 } 67 cpy.Set(reflect.New(originalValue.Type())) 68 copyRecursive(originalValue, cpy.Elem()) 69 70 case reflect.Interface: 71 // If this is a nil, don't do anything 72 if original.IsNil() { 73 return 74 } 75 // GetHeader the value for the interface, not the pointer. 76 originalValue := original.Elem() 77 78 // GetHeader the value by calling Elem(). 79 copyValue := reflect.New(originalValue.Type()).Elem() 80 copyRecursive(originalValue, copyValue) 81 cpy.Set(copyValue) 82 83 case reflect.Struct: 84 t, ok := original.Interface().(time.Time) 85 if ok { 86 cpy.Set(reflect.ValueOf(t)) 87 return 88 } 89 // Go through each field of the struct and copy it. 90 for i := 0; i < original.NumField(); i++ { 91 // The Type's StructField for a given field is checked to see if StructField.PkgPath 92 // is set to determine if the field is exported or not because CanSet() returns false 93 // for settable fields. I'm not sure why. 94 if original.Type().Field(i).PkgPath != "" { 95 continue 96 } 97 copyRecursive(original.Field(i), cpy.Field(i)) 98 } 99 100 case reflect.Slice: 101 if original.IsNil() { 102 return 103 } 104 // Make a new slice and copy each element. 105 cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap())) 106 for i := 0; i < original.Len(); i++ { 107 copyRecursive(original.Index(i), cpy.Index(i)) 108 } 109 110 case reflect.Map: 111 if original.IsNil() { 112 return 113 } 114 cpy.Set(reflect.MakeMap(original.Type())) 115 for _, key := range original.MapKeys() { 116 originalValue := original.MapIndex(key) 117 copyValue := reflect.New(originalValue.Type()).Elem() 118 copyRecursive(originalValue, copyValue) 119 copyKey := Clone(key.Interface()) 120 cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue) 121 } 122 123 default: 124 cpy.Set(original) 125 } 126 } 127 128 // Copy recursively copies the file, directory or symbolic link at src 129 // to dst. The destination must not exist. Symbolic links are not 130 // followed. 131 // 132 // If the copy fails half way through, the destination might be left 133 // partially written. 134 func Copy(srcFile, dstFile string) error { 135 srcInfo, srcErr := os.Lstat(srcFile) 136 if srcErr != nil { 137 return srcErr 138 } 139 _, dstErr := os.Lstat(dstFile) 140 if dstErr == nil { 141 return fmt.Errorf("will not overwrite %q", dstFile) 142 } 143 if !os.IsNotExist(dstErr) { 144 return dstErr 145 } 146 switch mode := srcInfo.Mode(); mode & os.ModeType { 147 case os.ModeSymlink: 148 return CopySymLink(srcFile, dstFile) 149 case os.ModeDir: 150 return CopyDir(srcFile, dstFile, mode) 151 case 0: 152 return CopyFile(srcFile, dstFile, mode) 153 default: 154 return fmt.Errorf("cannot copy file with mode %v", mode) 155 } 156 } 157 158 func CopySymLink(srcFile, dstFile string) error { 159 target, err := os.Readlink(srcFile) 160 if err != nil { 161 return err 162 } 163 return os.Symlink(target, dstFile) 164 } 165 166 func CopyFile(src, dst string, mode os.FileMode) error { 167 srcFile, err := os.Open(src) 168 if err != nil { 169 return err 170 } 171 defer srcFile.Close() 172 dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm()) 173 if err != nil { 174 return err 175 } 176 defer dstFile.Close() 177 // Make the actual permissions match the source permissions 178 // even in the presence of umask. 179 if err := os.Chmod(dstFile.Name(), mode.Perm()); err != nil { 180 return err 181 } 182 if _, err := io.Copy(dstFile, srcFile); err != nil { 183 return fmt.Errorf("cannot copy %q to %q: %v", src, dst, err) 184 } 185 return nil 186 } 187 188 // CopyDir copy directory. 189 func CopyDir(src, dst string, mode os.FileMode) error { 190 srcFile, err := os.Open(src) 191 if err != nil { 192 return err 193 } 194 defer srcFile.Close() 195 if mode&0500 == 0 { 196 // The source directory doesn't have write permission, 197 // so give the new directory write permission anyway 198 // so that we have permission to create its contents. 199 // We'll make the permissions match at the end. 200 mode |= 0500 201 } 202 if err := os.Mkdir(dst, mode.Perm()); err != nil { 203 return err 204 } 205 for { 206 names, err := srcFile.Readdirnames(100) 207 for _, name := range names { 208 if err := Copy(filepath.Join(src, name), filepath.Join(dst, name)); err != nil { 209 return err 210 } 211 } 212 if err == io.EOF { 213 break 214 } 215 if err != nil { 216 return fmt.Errorf("error reading directory %q: %v", src, err) 217 } 218 } 219 if err := os.Chmod(dst, mode.Perm()); err != nil { 220 return err 221 } 222 return nil 223 }