github.com/code-to-go/safepool.lib@v0.0.0-20221205180519-ee25e63c226e/transport/local.go (about)

     1  package transport
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/fs"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/code-to-go/safepool.lib/core"
    15  )
    16  
    17  type LocalConfig struct {
    18  	Base string `json:"base" yaml:"base"`
    19  }
    20  
    21  type Local struct {
    22  	base  string
    23  	url   string
    24  	touch map[string]time.Time
    25  }
    26  
    27  func NewLocal(config LocalConfig) (Exchanger, error) {
    28  	base := config.Base
    29  	if base == "" {
    30  		base = "/"
    31  	}
    32  	return &Local{base, "file://" + base, map[string]time.Time{}}, nil
    33  }
    34  
    35  func (l *Local) Touched(name string) bool {
    36  	touchFile := path.Join(l.base, fmt.Sprintf("%s.touch", name))
    37  	stat, err := l.Stat(touchFile)
    38  	touched := err != nil || stat.ModTime().After(l.touch[name])
    39  	if touched {
    40  		if !core.IsErr(l.Write(touchFile, &bytes.Buffer{}), "cannot write touch file: %v") {
    41  			l.touch[name] = stat.ModTime()
    42  		}
    43  	}
    44  	return touched
    45  }
    46  
    47  func (l *Local) Read(name string, rang *Range, dest io.Writer) error {
    48  	f, err := os.Open(path.Join(l.base, name))
    49  	if core.IsErr(err, "cannot open file on %v:%v", l) {
    50  		return err
    51  	}
    52  
    53  	if rang == nil {
    54  		_, err = io.Copy(dest, f)
    55  	} else {
    56  		left := rang.To - rang.From
    57  		f.Seek(rang.From, 0)
    58  		var b [4096]byte
    59  
    60  		for left > 0 && err == nil {
    61  			var sz int64
    62  			if rang.From-rang.To > 4096 {
    63  				sz = 4096
    64  			} else {
    65  				sz = rang.From - rang.To
    66  			}
    67  			_, err = f.Read(b[0:sz])
    68  			dest.Write(b[0:sz])
    69  			left -= sz
    70  		}
    71  	}
    72  	if core.IsErr(err, "cannot read from %s/%s:%v", l, name) {
    73  		return err
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func createDir(n string) error {
    80  	return os.MkdirAll(filepath.Dir(n), 0755)
    81  }
    82  
    83  func (l *Local) Write(name string, source io.Reader) error {
    84  	n := filepath.Join(l.base, name)
    85  	err := createDir(n)
    86  	if core.IsErr(err, "cannot create parent of %s: %v", n) {
    87  		return err
    88  	}
    89  
    90  	f, err := os.Create(n)
    91  	if core.IsErr(err, "cannot create file on %v:%v", l) {
    92  		return err
    93  	}
    94  	defer f.Close()
    95  
    96  	_, err = io.Copy(f, source)
    97  	core.IsErr(err, "cannot copy file on %v:%v", l)
    98  	return err
    99  }
   100  
   101  func (l *Local) ReadDir(name string, opts ListOption) ([]fs.FileInfo, error) {
   102  	var dir, prefix string
   103  	if opts&IsPrefix > 0 {
   104  		dir, prefix = filepath.Split(filepath.Join(l.base, name))
   105  	} else {
   106  		dir = filepath.Join(l.base, name)
   107  	}
   108  	result, err := os.ReadDir(dir)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	var infos []fs.FileInfo
   114  	for _, item := range result {
   115  		if strings.HasPrefix(item.Name(), prefix) {
   116  			info, err := item.Info()
   117  			if err == nil {
   118  				infos = append(infos, info)
   119  			}
   120  		}
   121  	}
   122  
   123  	return infos, nil
   124  }
   125  
   126  func (l *Local) Stat(name string) (os.FileInfo, error) {
   127  	return os.Stat(path.Join(l.base, name))
   128  }
   129  
   130  func (l *Local) Rename(old, new string) error {
   131  	return os.Rename(path.Join(l.base, old), path.Join(l.base, new))
   132  }
   133  
   134  func (l *Local) Delete(name string) error {
   135  	return os.Remove(path.Join(l.base, name))
   136  }
   137  
   138  func (l *Local) Close() error {
   139  	return nil
   140  }
   141  
   142  func (l *Local) String() string {
   143  	return l.url
   144  }