github.com/falafeljan/pkger@v0.18.0/pkging/mem/mem.go (about) 1 package mem 2 3 import ( 4 "fmt" 5 "os" 6 "path" 7 "path/filepath" 8 "strings" 9 "time" 10 11 "github.com/markbates/pkger/here" 12 "github.com/markbates/pkger/internal/maps" 13 "github.com/markbates/pkger/pkging" 14 ) 15 16 var _ pkging.Pkger = &Pkger{} 17 18 // New returns *Pkger for the provided here.Info 19 func New(info here.Info) (*Pkger, error) { 20 f := &Pkger{ 21 infos: &maps.Infos{}, 22 files: &maps.Files{}, 23 Here: info, 24 } 25 f.infos.Store(info.ImportPath, info) 26 return f, nil 27 } 28 29 type Pkger struct { 30 Here here.Info 31 infos *maps.Infos 32 files *maps.Files 33 } 34 35 // Current returns the here.Info representing the current Pkger implementation. 36 func (f *Pkger) Current() (here.Info, error) { 37 return f.Here, nil 38 } 39 40 // Info returns the here.Info of the here.Path 41 func (f *Pkger) Info(p string) (here.Info, error) { 42 info, ok := f.infos.Load(p) 43 if !ok { 44 return info, fmt.Errorf("no such package %q", p) 45 } 46 47 return info, nil 48 } 49 50 // Parse the string in here.Path format. 51 func (f *Pkger) Parse(p string) (here.Path, error) { 52 return f.Here.Parse(p) 53 } 54 55 // Remove removes the named file or (empty) directory. 56 func (fx *Pkger) Remove(name string) error { 57 pt, err := fx.Parse(name) 58 if err != nil { 59 return err 60 } 61 62 if _, ok := fx.files.Load(pt); !ok { 63 return &os.PathError{"remove", pt.String(), fmt.Errorf("no such file or directory")} 64 } 65 66 fx.files.Delete(pt) 67 return nil 68 } 69 70 // RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). 71 func (fx *Pkger) RemoveAll(name string) error { 72 pt, err := fx.Parse(name) 73 if err != nil { 74 return err 75 } 76 77 fx.files.Range(func(key here.Path, file pkging.File) bool { 78 if strings.HasPrefix(key.Name, pt.Name) { 79 fx.files.Delete(key) 80 } 81 return true 82 }) 83 84 return nil 85 } 86 87 // Create creates the named file with mode 0666 (before umask) - It's actually 0644, truncating it if it already exists. If successful, methods on the returned File can be used for I/O; the associated file descriptor has mode O_RDWR. 88 func (fx *Pkger) Create(name string) (pkging.File, error) { 89 fx.MkdirAll("/", 0755) 90 pt, err := fx.Parse(name) 91 if err != nil { 92 return nil, err 93 } 94 95 her, err := fx.Info(pt.Pkg) 96 if err != nil { 97 return nil, err 98 } 99 100 dir := filepath.Dir(pt.Name) 101 if dir != "/" { 102 if _, err := fx.Stat(dir); err != nil { 103 return nil, err 104 } 105 } 106 107 f := &File{ 108 Here: her, 109 path: pt, 110 info: &pkging.FileInfo{ 111 Details: pkging.Details{ 112 Name: filepath.Base(name), 113 Mode: 0644, 114 ModTime: pkging.ModTime(time.Now()), 115 }, 116 }, 117 pkging: fx, 118 } 119 120 fx.files.Store(pt, f) 121 122 return f, nil 123 } 124 125 // MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm (before umask) are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil. 126 func (fx *Pkger) MkdirAll(p string, perm os.FileMode) error { 127 pt, err := fx.Parse(p) 128 if err != nil { 129 return err 130 } 131 dir, name := path.Split(pt.Name) 132 133 if dir != "/" { 134 if err := fx.MkdirAll(dir, perm); err != nil { 135 return err 136 } 137 } 138 139 if dir == "/" && name == "" { 140 dir = filepath.Base(fx.Here.Dir) 141 } 142 143 f := &File{ 144 Here: fx.Here, 145 pkging: fx, 146 path: pt, 147 info: &pkging.FileInfo{ 148 Details: pkging.Details{ 149 IsDir: true, 150 Name: name, 151 Mode: perm, 152 ModTime: pkging.ModTime(time.Now()), 153 }, 154 }, 155 } 156 if err := f.Close(); err != nil { 157 return err 158 } 159 fx.files.Store(pt, f) 160 return nil 161 162 } 163 164 // Open opens the named file for reading. If successful, methods on the returned file can be used for reading; the associated file descriptor has mode O_RDONLY. 165 func (fx *Pkger) Open(name string) (pkging.File, error) { 166 pt, err := fx.Parse(name) 167 if err != nil { 168 return nil, &os.PathError{ 169 Op: "open", 170 Path: name, 171 Err: err, 172 } 173 } 174 175 fl, ok := fx.files.Load(pt) 176 if !ok { 177 return nil, os.ErrNotExist 178 } 179 f, ok := fl.(*File) 180 if !ok { 181 return nil, os.ErrNotExist 182 } 183 nf := &File{ 184 pkging: fx, 185 info: pkging.NewFileInfo(f.info), 186 path: f.path, 187 data: f.data, 188 Here: f.Here, 189 } 190 191 return nf, nil 192 } 193 194 // Stat returns a FileInfo describing the named file. 195 func (fx *Pkger) Stat(name string) (os.FileInfo, error) { 196 pt, err := fx.Parse(name) 197 if err != nil { 198 return nil, err 199 } 200 f, ok := fx.files.Load(pt) 201 if ok { 202 return f.Stat() 203 } 204 return nil, fmt.Errorf("could not stat %s", pt) 205 } 206 207 // Walk walks the file tree rooted at root, calling walkFn for each file or directory in the tree, including root. All errors that arise visiting files and directories are filtered by walkFn. The files are walked in lexical order, which makes the output deterministic but means that for very large directories Walk can be inefficient. Walk does not follow symbolic links. - That is from the standard library. I know. Their grammar teachers can not be happy with them right now. 208 func (f *Pkger) Walk(p string, wf filepath.WalkFunc) error { 209 keys := f.files.Keys() 210 211 pt, err := f.Parse(p) 212 if err != nil { 213 return err 214 } 215 216 skip := "!" 217 for _, k := range keys { 218 if k.Pkg != pt.Pkg { 219 continue 220 } 221 if !strings.HasPrefix(k.Name, pt.Name) { 222 continue 223 } 224 if strings.HasPrefix(k.Name, skip) { 225 continue 226 } 227 228 fl, ok := f.files.Load(k) 229 if !ok { 230 return os.ErrNotExist 231 } 232 233 fi, err := fl.Stat() 234 if err != nil { 235 return err 236 } 237 238 fi = pkging.NewFileInfo(fi) 239 240 err = wf(k.String(), fi, nil) 241 if err == filepath.SkipDir { 242 243 skip = k.Name 244 continue 245 } 246 247 if err != nil { 248 return err 249 } 250 } 251 return nil 252 }