github.com/mithrandie/csvq@v1.18.1/lib/file/handler.go (about)

     1  package file
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/mithrandie/go-file/v2"
    10  )
    11  
    12  type OpenType int
    13  
    14  const (
    15  	ForRead OpenType = iota
    16  	ForCreate
    17  	ForUpdate
    18  )
    19  
    20  type Handler struct {
    21  	path string
    22  	fp   *os.File
    23  
    24  	openType OpenType
    25  
    26  	rlockFile *ControlFile
    27  	lockFile  *ControlFile
    28  	tempFile  *ControlFile
    29  
    30  	closed bool
    31  }
    32  
    33  func NewHandlerWithoutLock(ctx context.Context, path string, defaultWaitTimeout time.Duration, retryDelay time.Duration) (*Handler, error) {
    34  	tctx, cancel := GetTimeoutContext(ctx, defaultWaitTimeout)
    35  	defer cancel()
    36  
    37  	h := &Handler{
    38  		path:     path,
    39  		openType: ForRead,
    40  	}
    41  
    42  	if !Exists(h.path) {
    43  		return h, NewNotExistError(fmt.Sprintf("file %s does not exist", h.path))
    44  	}
    45  
    46  	fp, err := file.OpenToReadContext(tctx, retryDelay, h.path)
    47  	if err != nil {
    48  		return h, closeIsolatedHandler(h, err)
    49  	}
    50  	h.fp = fp
    51  	return h, nil
    52  }
    53  
    54  func NewHandlerForRead(ctx context.Context, path string, defaultWaitTimeout time.Duration, retryDelay time.Duration) (*Handler, error) {
    55  	tctx, cancel := GetTimeoutContext(ctx, defaultWaitTimeout)
    56  	defer cancel()
    57  
    58  	h := &Handler{
    59  		path:     path,
    60  		openType: ForRead,
    61  	}
    62  
    63  	if !Exists(h.path) {
    64  		return h, NewNotExistError(fmt.Sprintf("file %s does not exist", h.path))
    65  	}
    66  
    67  	if err := h.CreateControlFileContext(tctx, RLock, retryDelay); err != nil {
    68  		return h, closeIsolatedHandler(h, err)
    69  	}
    70  
    71  	fp, err := file.OpenToReadContext(tctx, retryDelay, h.path)
    72  	if err != nil {
    73  		return h, closeIsolatedHandler(h, err)
    74  	}
    75  	h.fp = fp
    76  	return h, nil
    77  }
    78  
    79  func newHandlerForCreate(_ context.Context, path string, _ time.Duration, _ time.Duration) (*Handler, error) {
    80  	return NewHandlerForCreate(path)
    81  }
    82  
    83  func NewHandlerForCreate(path string) (*Handler, error) {
    84  	h := &Handler{
    85  		path:     path,
    86  		openType: ForCreate,
    87  	}
    88  
    89  	if Exists(h.path) {
    90  		return h, NewAlreadyExistError(fmt.Sprintf("file %s already exists", h.path))
    91  	}
    92  
    93  	if h.lockFile != nil {
    94  		return nil, NewLockError(fmt.Sprintf("%s file for %s is already created", Lock, h.path))
    95  	}
    96  	lockFile, err := TryCreateLockFile(h.path)
    97  	if err != nil {
    98  		return h, closeIsolatedHandler(h, err)
    99  	}
   100  	h.lockFile = lockFile
   101  
   102  	fp, err := file.Create(h.path)
   103  	if err != nil {
   104  		return h, closeIsolatedHandler(h, err)
   105  	}
   106  	h.fp = fp
   107  	return h, nil
   108  }
   109  
   110  func NewHandlerForUpdate(ctx context.Context, path string, defaultWaitTimeout time.Duration, retryDelay time.Duration) (*Handler, error) {
   111  	tctx, cancel := GetTimeoutContext(ctx, defaultWaitTimeout)
   112  	defer cancel()
   113  
   114  	h := &Handler{
   115  		path:     path,
   116  		openType: ForUpdate,
   117  	}
   118  
   119  	if !Exists(h.path) {
   120  		return h, NewNotExistError(fmt.Sprintf("file %s does not exist", h.path))
   121  	}
   122  
   123  	if err := h.CreateControlFileContext(tctx, Lock, retryDelay); err != nil {
   124  		return h, closeIsolatedHandler(h, err)
   125  	}
   126  
   127  	fp, err := file.OpenToUpdateContext(tctx, retryDelay, path)
   128  	if err != nil {
   129  		return h, closeIsolatedHandler(h, err)
   130  	}
   131  	h.fp = fp
   132  
   133  	if err := h.CreateControlFileContext(tctx, Temporary, retryDelay); err != nil {
   134  		return h, closeIsolatedHandler(h, err)
   135  	}
   136  	return h, nil
   137  }
   138  
   139  func closeIsolatedHandler(h *Handler, err error) error {
   140  	return NewCompositeError(ParseError(err), h.closeWithErrors())
   141  }
   142  
   143  func (h *Handler) Path() string {
   144  	return h.path
   145  }
   146  
   147  func (h *Handler) File() *os.File {
   148  	return h.fp
   149  }
   150  
   151  func (h *Handler) FileForUpdate() (*os.File, error) {
   152  	switch h.openType {
   153  	case ForUpdate:
   154  		return h.tempFile.fp, nil
   155  	case ForCreate:
   156  		return h.fp, nil
   157  	}
   158  	return nil, fmt.Errorf("file %s cannot be updated", h.path)
   159  }
   160  
   161  func (h *Handler) close() error {
   162  	if h.closed {
   163  		return nil
   164  	}
   165  
   166  	if h.fp != nil {
   167  		if err := file.Close(h.fp); err != nil {
   168  			return err
   169  		}
   170  		h.fp = nil
   171  	}
   172  
   173  	if h.openType == ForCreate && Exists(h.path) {
   174  		if err := os.Remove(h.path); err != nil {
   175  			return err
   176  		}
   177  	}
   178  
   179  	if err := h.tempFile.Close(); err != nil {
   180  		return err
   181  	}
   182  	h.tempFile = nil
   183  
   184  	if err := h.lockFile.Close(); err != nil {
   185  		return err
   186  	}
   187  	h.lockFile = nil
   188  
   189  	if err := h.rlockFile.Close(); err != nil {
   190  		return err
   191  	}
   192  	h.rlockFile = nil
   193  
   194  	h.closed = true
   195  	return nil
   196  }
   197  
   198  func (h *Handler) commit() error {
   199  	if h.closed {
   200  		return nil
   201  	}
   202  
   203  	if h.fp != nil {
   204  		if err := file.Close(h.fp); err != nil {
   205  			return err
   206  		}
   207  		h.fp = nil
   208  	}
   209  
   210  	if h.openType == ForUpdate {
   211  		if h.tempFile.fp != nil {
   212  			if err := file.Close(h.tempFile.fp); err != nil {
   213  				return err
   214  			}
   215  			h.tempFile.fp = nil
   216  		}
   217  
   218  		if Exists(h.path) {
   219  			if err := os.Remove(h.path); err != nil {
   220  				return err
   221  			}
   222  		}
   223  
   224  		if err := os.Rename(h.tempFile.path, h.path); err != nil {
   225  			return err
   226  		}
   227  	} else {
   228  		if err := h.tempFile.Close(); err != nil {
   229  			return err
   230  		}
   231  		h.tempFile = nil
   232  	}
   233  
   234  	if err := h.lockFile.Close(); err != nil {
   235  		return err
   236  	}
   237  	h.lockFile = nil
   238  
   239  	if err := h.rlockFile.Close(); err != nil {
   240  		return err
   241  	}
   242  	h.rlockFile = nil
   243  
   244  	h.closed = true
   245  	return nil
   246  }
   247  
   248  func (h *Handler) closeWithErrors() error {
   249  	if h.closed {
   250  		return nil
   251  	}
   252  
   253  	var errs []error
   254  
   255  	if h.fp != nil {
   256  		if err := file.Close(h.fp); err != nil {
   257  			errs = append(errs, err)
   258  		} else {
   259  			h.fp = nil
   260  		}
   261  	}
   262  
   263  	if h.openType == ForCreate && Exists(h.path) {
   264  		if err := os.Remove(h.path); err != nil {
   265  			errs = append(errs, err)
   266  		}
   267  	}
   268  
   269  	if cerrs := h.tempFile.CloseWithErrors(); cerrs != nil {
   270  		errs = append(errs, cerrs...)
   271  	} else {
   272  		h.tempFile = nil
   273  	}
   274  
   275  	if cerrs := h.lockFile.CloseWithErrors(); cerrs != nil {
   276  		errs = append(errs, cerrs...)
   277  	} else {
   278  		h.lockFile = nil
   279  	}
   280  
   281  	if cerrs := h.rlockFile.CloseWithErrors(); cerrs != nil {
   282  		errs = append(errs, cerrs...)
   283  	} else {
   284  		h.rlockFile = nil
   285  	}
   286  
   287  	return NewForcedUnlockError(errs)
   288  }
   289  
   290  func (h *Handler) CreateControlFileContext(ctx context.Context, fileType ControlFileType, retryDelay time.Duration) error {
   291  	switch fileType {
   292  	case Lock:
   293  		if h.lockFile != nil {
   294  			return NewLockError(fmt.Sprintf("%s file for %s is already created", Lock, h.path))
   295  		}
   296  	case Temporary:
   297  		if h.tempFile != nil {
   298  			return NewLockError(fmt.Sprintf("%s file for %s is already created", Temporary, h.path))
   299  		}
   300  	default: //RLock
   301  		if h.rlockFile != nil {
   302  			return NewLockError(fmt.Sprintf("%s file for %s is already created", RLock, h.path))
   303  		}
   304  	}
   305  
   306  	f, err := CreateControlFileContext(ctx, h.path, fileType, retryDelay)
   307  	if err != nil {
   308  		return err
   309  	}
   310  
   311  	switch fileType {
   312  	case Lock:
   313  		h.lockFile = f
   314  	case Temporary:
   315  		h.tempFile = f
   316  	default: //RLock
   317  		h.rlockFile = f
   318  	}
   319  	return nil
   320  }