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  }