github.com/c0olix/migrate@v3.5.4+incompatible/source/driver.go (about)

     1  // Package source provides the Source interface.
     2  // All source drivers must implement this interface, register themselves,
     3  // optionally provide a `WithInstance` function and pass the tests
     4  // in package source/testing.
     5  package source
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	nurl "net/url"
    11  	"sync"
    12  )
    13  
    14  var driversMu sync.RWMutex
    15  var drivers = make(map[string]Driver)
    16  
    17  // Driver is the interface every source driver must implement.
    18  //
    19  // How to implement a source driver?
    20  //   1. Implement this interface.
    21  //   2. Optionally, add a function named `WithInstance`.
    22  //      This function should accept an existing source instance and a Config{} struct
    23  //      and return a driver instance.
    24  //   3. Add a test that calls source/testing.go:Test()
    25  //   4. Add own tests for Open(), WithInstance() (when provided) and Close().
    26  //      All other functions are tested by tests in source/testing.
    27  //      Saves you some time and makes sure all source drivers behave the same way.
    28  //   5. Call Register in init().
    29  //
    30  // Guidelines:
    31  //   * All configuration input must come from the URL string in func Open()
    32  //     or the Config{} struct in WithInstance. Don't os.Getenv().
    33  //   * Drivers are supposed to be read only.
    34  //   * Ideally don't load any contents (into memory) in Open or WithInstance.
    35  type Driver interface {
    36  	// Open returns a a new driver instance configured with parameters
    37  	// coming from the URL string. Migrate will call this function
    38  	// only once per instance.
    39  	Open(url string) (Driver, error)
    40  
    41  	// Close closes the underlying source instance managed by the driver.
    42  	// Migrate will call this function only once per instance.
    43  	Close() error
    44  
    45  	// First returns the very first migration version available to the driver.
    46  	// Migrate will call this function multiple times.
    47  	// If there is no version available, it must return os.ErrNotExist.
    48  	First() (version uint, err error)
    49  
    50  	// Prev returns the previous version for a given version available to the driver.
    51  	// Migrate will call this function multiple times.
    52  	// If there is no previous version available, it must return os.ErrNotExist.
    53  	Prev(version uint) (prevVersion uint, err error)
    54  
    55  	// Next returns the next version for a given version available to the driver.
    56  	// Migrate will call this function multiple times.
    57  	// If there is no next version available, it must return os.ErrNotExist.
    58  	Next(version uint) (nextVersion uint, err error)
    59  
    60  	// ReadUp returns the UP migration body and an identifier that helps
    61  	// finding this migration in the source for a given version.
    62  	// If there is no up migration available for this version,
    63  	// it must return os.ErrNotExist.
    64  	// Do not start reading, just return the ReadCloser!
    65  	ReadUp(version uint) (r io.ReadCloser, identifier string, err error)
    66  
    67  	// ReadDown returns the DOWN migration body and an identifier that helps
    68  	// finding this migration in the source for a given version.
    69  	// If there is no down migration available for this version,
    70  	// it must return os.ErrNotExist.
    71  	// Do not start reading, just return the ReadCloser!
    72  	ReadDown(version uint) (r io.ReadCloser, identifier string, err error)
    73  }
    74  
    75  // Open returns a new driver instance.
    76  func Open(url string) (Driver, error) {
    77  	u, err := nurl.Parse(url)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	if u.Scheme == "" {
    83  		return nil, fmt.Errorf("source driver: invalid URL scheme")
    84  	}
    85  
    86  	driversMu.RLock()
    87  	d, ok := drivers[u.Scheme]
    88  	driversMu.RUnlock()
    89  	if !ok {
    90  		return nil, fmt.Errorf("source driver: unknown driver %v (forgotten import?)", u.Scheme)
    91  	}
    92  
    93  	return d.Open(url)
    94  }
    95  
    96  // Register globally registers a driver.
    97  func Register(name string, driver Driver) {
    98  	driversMu.Lock()
    99  	defer driversMu.Unlock()
   100  	if driver == nil {
   101  		panic("Register driver is nil")
   102  	}
   103  	if _, dup := drivers[name]; dup {
   104  		panic("Register called twice for driver " + name)
   105  	}
   106  	drivers[name] = driver
   107  }
   108  
   109  // List lists the registered drivers
   110  func List() []string {
   111  	driversMu.RLock()
   112  	defer driversMu.RUnlock()
   113  	names := make([]string, 0, len(drivers))
   114  	for n := range drivers {
   115  		names = append(names, n)
   116  	}
   117  	return names
   118  }