github.com/chadwpetersen/migrate@v1.0.1/source/httpfs/partial_driver.go (about)

     1  package httpfs
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"net/http"
     7  	"os"
     8  	"path"
     9  	"strconv"
    10  
    11  	"github.com/chadwpetersen/migrate/source"
    12  )
    13  
    14  // PartialDriver is a helper service for creating new source drivers working with
    15  // http.FileSystem instances. It implements all source.Driver interface methods
    16  // except for Open(). New driver could embed this struct and add missing Open()
    17  // method.
    18  //
    19  // To prepare PartialDriver for use Init() function.
    20  type PartialDriver struct {
    21  	migrations *source.Migrations
    22  	fs         http.FileSystem
    23  	path       string
    24  }
    25  
    26  // Init prepares not initialized PartialDriver instance to read migrations from a
    27  // http.FileSystem instance and a relative path.
    28  func (p *PartialDriver) Init(fs http.FileSystem, path string) error {
    29  	root, err := fs.Open(path)
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	files, err := root.Readdir(0)
    35  	if err != nil {
    36  		_ = root.Close()
    37  		return err
    38  	}
    39  	if err = root.Close(); err != nil {
    40  		return err
    41  	}
    42  
    43  	ms := source.NewMigrations()
    44  	for _, file := range files {
    45  		if file.IsDir() {
    46  			continue
    47  		}
    48  
    49  		m, err := source.DefaultParse(file.Name())
    50  		if err != nil {
    51  			continue // ignore files that we can't parse
    52  		}
    53  
    54  		if !ms.Append(m) {
    55  			return source.ErrDuplicateMigration{
    56  				Migration: *m,
    57  				FileInfo:  file,
    58  			}
    59  		}
    60  	}
    61  
    62  	p.fs = fs
    63  	p.path = path
    64  	p.migrations = ms
    65  	return nil
    66  }
    67  
    68  // Close is part of source.Driver interface implementation. This is a no-op.
    69  func (p *PartialDriver) Close() error {
    70  	return nil
    71  }
    72  
    73  // First is part of source.Driver interface implementation.
    74  func (p *PartialDriver) First() (version uint, err error) {
    75  	if version, ok := p.migrations.First(); ok {
    76  		return version, nil
    77  	}
    78  	return 0, &os.PathError{
    79  		Op:   "first",
    80  		Path: p.path,
    81  		Err:  os.ErrNotExist,
    82  	}
    83  }
    84  
    85  // Prev is part of source.Driver interface implementation.
    86  func (p *PartialDriver) Prev(version uint) (prevVersion uint, err error) {
    87  	if version, ok := p.migrations.Prev(version); ok {
    88  		return version, nil
    89  	}
    90  	return 0, &os.PathError{
    91  		Op:   "prev for version " + strconv.FormatUint(uint64(version), 10),
    92  		Path: p.path,
    93  		Err:  os.ErrNotExist,
    94  	}
    95  }
    96  
    97  // Next is part of source.Driver interface implementation.
    98  func (p *PartialDriver) Next(version uint) (nextVersion uint, err error) {
    99  	if version, ok := p.migrations.Next(version); ok {
   100  		return version, nil
   101  	}
   102  	return 0, &os.PathError{
   103  		Op:   "next for version " + strconv.FormatUint(uint64(version), 10),
   104  		Path: p.path,
   105  		Err:  os.ErrNotExist,
   106  	}
   107  }
   108  
   109  // ReadUp is part of source.Driver interface implementation.
   110  func (p *PartialDriver) ReadUp(version uint) (r io.ReadCloser, identifier string, err error) {
   111  	if m, ok := p.migrations.Up(version); ok {
   112  		body, err := p.open(path.Join(p.path, m.Raw))
   113  		if err != nil {
   114  			return nil, "", err
   115  		}
   116  		return body, m.Identifier, nil
   117  	}
   118  	return nil, "", &os.PathError{
   119  		Op:   "read up for version " + strconv.FormatUint(uint64(version), 10),
   120  		Path: p.path,
   121  		Err:  os.ErrNotExist,
   122  	}
   123  }
   124  
   125  // ReadDown is part of source.Driver interface implementation.
   126  func (p *PartialDriver) ReadDown(version uint) (r io.ReadCloser, identifier string, err error) {
   127  	if m, ok := p.migrations.Down(version); ok {
   128  		body, err := p.open(path.Join(p.path, m.Raw))
   129  		if err != nil {
   130  			return nil, "", err
   131  		}
   132  		return body, m.Identifier, nil
   133  	}
   134  	return nil, "", &os.PathError{
   135  		Op:   "read down for version " + strconv.FormatUint(uint64(version), 10),
   136  		Path: p.path,
   137  		Err:  os.ErrNotExist,
   138  	}
   139  }
   140  
   141  func (p *PartialDriver) open(path string) (http.File, error) {
   142  	f, err := p.fs.Open(path)
   143  	if err == nil {
   144  		return f, nil
   145  	}
   146  	// Some non-standard file systems may return errors that don't include the path, that
   147  	// makes debugging harder.
   148  	if !errors.As(err, new(*os.PathError)) {
   149  		err = &os.PathError{
   150  			Op:   "open",
   151  			Path: path,
   152  			Err:  err,
   153  		}
   154  	}
   155  	return nil, err
   156  }