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 }