package mem import ( "bytes" "encoding/json" "fmt" "io" "os" "path/filepath" "strings" "time" "github.com/markbates/pkger/here" "github.com/markbates/pkger/internal/maps" "github.com/markbates/pkger/pkging" ) var _ pkging.Pkger = &Pkger{} func WithInfo(fx *Pkger, infos ...here.Info) { for _, info := range infos { fx.infos.Store(info.ImportPath, info) } } func New(info here.Info) (*Pkger, error) { f := &Pkger{ infos: &maps.Infos{}, files: &maps.Files{}, current: info, } f.infos.Store(info.ImportPath, info) f.MkdirAll("/", 0755) return f, nil } type Pkger struct { infos *maps.Infos files *maps.Files current here.Info } type jay struct { Infos *maps.Infos `json:"infos"` Files map[string]*File `json:"files"` Current here.Info `json:"current"` } func (p *Pkger) MarshalJSON() ([]byte, error) { files := map[string]*File{} p.files.Range(func(key here.Path, file pkging.File) bool { f, ok := file.(*File) if !ok { return true } files[key.String()] = f return true }) return json.Marshal(jay{ Infos: p.infos, Files: files, Current: p.current, }) } func (p *Pkger) UnmarshalJSON(b []byte) error { y := jay{} if err := json.Unmarshal(b, &y); err != nil { return err } p.current = y.Current p.infos = y.Infos p.files = &maps.Files{} for k, v := range y.Files { pt, err := p.Parse(k) if err != nil { return err } p.files.Store(pt, v) } return nil } func (f *Pkger) Abs(p string) (string, error) { pt, err := f.Parse(p) if err != nil { return "", err } return f.AbsPath(pt) } func (f *Pkger) AbsPath(pt here.Path) (string, error) { return pt.String(), nil } func (f *Pkger) Current() (here.Info, error) { return f.current, nil } func (f *Pkger) Info(p string) (here.Info, error) { info, ok := f.infos.Load(p) if !ok { return info, fmt.Errorf("no such package %q", p) } return info, nil } func (f *Pkger) Parse(p string) (here.Path, error) { return f.current.Parse(p) } func (fx *Pkger) Remove(name string) error { pt, err := fx.Parse(name) if err != nil { return err } if _, ok := fx.files.Load(pt); !ok { return &os.PathError{"remove", pt.String(), fmt.Errorf("no such file or directory")} } fx.files.Delete(pt) return nil } func (fx *Pkger) RemoveAll(name string) error { pt, err := fx.Parse(name) if err != nil { return err } fx.files.Range(func(key here.Path, file pkging.File) bool { if strings.HasPrefix(key.Name, pt.Name) { fx.files.Delete(key) } return true }) return nil } func (fx *Pkger) Add(f pkging.File) error { fx.MkdirAll("/", 0755) info, err := f.Stat() if err != nil { return err } if f.Path().Pkg == fx.current.ImportPath { if err := fx.MkdirAll(filepath.Dir(f.Name()), 0755); err != nil { return err } } mf := &File{ her: f.Info(), info: pkging.NewFileInfo(info), path: f.Path(), pkging: fx, } if !info.IsDir() { bb := &bytes.Buffer{} _, err = io.Copy(bb, f) if err != nil { return err } mf.data = bb.Bytes() } fx.files.Store(mf.Path(), mf) return nil } // func (fx *Pkger) Add(info os.FileInfo, r io.Reader) error { // dir := filepath.Dir(info.Name()) // fx.MkdirAll(dir, 0755) // // f, err := fx.Create(info.Name()) // if err != nil { // return err // } // // mf, ok := f.(*File) // if !ok { // return fmt.Errorf("could not add %T", f) // } // fmt.Println(">>>TODO pkging/mem/mem.go:162: mf.Path() ", mf.Path()) // // mf.info = pkging.NewFileInfo(info) // // if !mf.info.IsDir() { // b, err := ioutil.ReadAll(r) // if err != nil { // return err // } // mf.data = b // } // // pf, ok := r.(pkging.File) // if ok { // fmt.Println(">>>TODO pkging/mem/mem.go:176: pf.Pkg, pf.Name ", pf.Path().Pkg, pf.Path().Name) // mf.path = pf.Path() // mf.her = pf.Info() // } // // fx.files.Store(f.Path(), f) // return nil // } func (fx *Pkger) Create(name string) (pkging.File, error) { fx.MkdirAll("/", 0755) pt, err := fx.Parse(name) if err != nil { return nil, err } her, err := fx.Info(pt.Pkg) if err != nil { return nil, err } dir := filepath.Dir(pt.Name) if _, err := fx.Stat(dir); err != nil { return nil, err } f := &File{ path: pt, her: her, info: &pkging.FileInfo{ Details: pkging.Details{ Name: pt.Name, Mode: 0644, ModTime: pkging.ModTime(time.Now()), }, }, pkging: fx, } fx.files.Store(pt, f) return f, nil } func (fx *Pkger) MkdirAll(p string, perm os.FileMode) error { path, err := fx.Parse(p) if err != nil { return err } root := path.Name cur, err := fx.Current() if err != nil { return err } for root != "" { pt := here.Path{ Pkg: path.Pkg, Name: root, } if _, ok := fx.files.Load(pt); ok { root = filepath.Dir(root) if root == "/" || root == "\\" { break } continue } f := &File{ pkging: fx, path: pt, her: cur, info: &pkging.FileInfo{ Details: pkging.Details{ Name: pt.Name, Mode: perm, ModTime: pkging.ModTime(time.Now()), }, }, } if err != nil { return err } f.info.Details.IsDir = true f.info.Details.Mode = perm if err := f.Close(); err != nil { return err } fx.files.Store(pt, f) root = filepath.Dir(root) } return nil } func (fx *Pkger) Open(name string) (pkging.File, error) { pt, err := fx.Parse(name) if err != nil { return nil, err } fmt.Println(">>>TODO pkging/mem/mem.go:309: pt ", pt) fl, ok := fx.files.Load(pt) if !ok { return nil, fmt.Errorf("could not open %s", name) } f, ok := fl.(*File) if !ok { return nil, fmt.Errorf("could not open %s", name) } nf := &File{ pkging: fx, info: pkging.WithName(f.info.Name(), f.info), path: f.path, data: f.data, her: f.her, } fmt.Println(">>>TODO pkging/mem/mem.go:326: nf.info ", nf.info) return nf, nil } func (fx *Pkger) Stat(name string) (os.FileInfo, error) { pt, err := fx.Parse(name) if err != nil { return nil, err } f, ok := fx.files.Load(pt) if ok { return f.Stat() } return nil, fmt.Errorf("could not stat %s", pt) } func (f *Pkger) Walk(p string, wf filepath.WalkFunc) error { keys := f.files.Keys() pt, err := f.Parse(p) if err != nil { return err } skip := "!" for _, k := range keys { if !strings.HasPrefix(k.Name, pt.Name) { continue } if strings.HasPrefix(k.Name, skip) { continue } fl, ok := f.files.Load(k) if !ok { return fmt.Errorf("could not find %s", k) } fi, err := fl.Stat() if err != nil { return err } fi = pkging.WithName(strings.TrimPrefix(k.Name, pt.Name), fi) err = wf(k.String(), fi, nil) if err == filepath.SkipDir { skip = k.Name continue } if err != nil { return err } } return nil }