mirror of
https://github.com/nabbar/golib.git
synced 2025-12-24 11:51:02 +08:00
[file/bandwidth] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [file/perm] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases - ADD function to parse form "rwx-wxr-x" or "-rwx-w-r-x" - ADD function to ParseFileMode to convert os.FileMode to file.Perm [file/progress] - ADD documentation: add enhanced README and TESTING guidelines - ADD tests: complete test suites with benchmarks, concurrency, and edge cases [ioutils/...] - UPDATE documentation: update enhanced README and TESTING guidelines - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases [logger/...] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - UPDATE config: remove FileBufferSize from OptionFile (rework hookfile) - UPDATE fields: expose Store function in interface - REWORK hookfile: rework package, use aggregator to allow multi write and single file - FIX hookstderr: fix bug with NonColorable - FIX hookstdout: fix bug with NonColorable - FIX hookwriter: fix bug with NonColorable [network/protocol] - ADD function IsTCP, IsUDP, IsUnixLike to check type of protocol [runner] - FIX typo [socket] - UPDATE documentation: update enhanced README and TESTING guidelines - ADD documentation: add enhanced README and TESTING guidelines for sub packages - UPDATE tests: complete test suites with benchmarks, concurrency, and edge cases - REWORK server: use context compatible io.reader, io.writer, io.closer instead of reader / writer - REWORK server: simplify, optimize server - REMOVE reader, writer type - ADD context: add new interface in root socket interface to expose context interface that extend context, io reader/writer/closer, dediacted function to server (IsConnected, ...)
390 lines
13 KiB
Go
390 lines
13 KiB
Go
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2025 Nicolas JUHEL
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
// Package progress provides file I/O operations with real-time progress tracking and callbacks.
|
|
//
|
|
// This package wraps standard file operations with integrated progress monitoring capabilities,
|
|
// enabling applications to track and respond to file I/O events through registered callbacks.
|
|
// It implements all standard Go I/O interfaces while adding progress tracking functionality.
|
|
//
|
|
// Key features:
|
|
// - Progress tracking with callbacks (increment, reset, EOF)
|
|
// - Full io.Reader, io.Writer, io.Seeker interface implementation
|
|
// - Configurable buffer sizes for optimal performance
|
|
// - Temporary and unique file creation
|
|
// - File position tracking (BOF/EOF)
|
|
// - Thread-safe atomic operations
|
|
//
|
|
// Example usage:
|
|
//
|
|
// import (
|
|
// "io"
|
|
// "github.com/nabbar/golib/file/progress"
|
|
// )
|
|
//
|
|
// // Open file with progress tracking
|
|
// p, err := progress.Open("largefile.dat")
|
|
// if err != nil {
|
|
// panic(err)
|
|
// }
|
|
// defer p.Close()
|
|
//
|
|
// // Register progress callback
|
|
// p.RegisterFctIncrement(func(bytes int64) {
|
|
// fmt.Printf("Progress: %d bytes\n", bytes)
|
|
// })
|
|
//
|
|
// // Register EOF callback
|
|
// p.RegisterFctEOF(func() {
|
|
// fmt.Println("Reading complete!")
|
|
// })
|
|
//
|
|
// // Read file - callbacks will be invoked
|
|
// io.Copy(io.Discard, p)
|
|
package progress
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sync/atomic"
|
|
)
|
|
|
|
const DefaultBuffSize = 32 * 1024 // see io.copyBuffer
|
|
|
|
type FctIncrement func(size int64)
|
|
type FctReset func(size, current int64)
|
|
type FctEOF func()
|
|
|
|
type GenericIO interface {
|
|
io.ReadCloser
|
|
io.ReadSeeker
|
|
io.ReadWriteCloser
|
|
io.ReadWriteSeeker
|
|
io.WriteCloser
|
|
io.WriteSeeker
|
|
io.Reader
|
|
io.ReaderFrom
|
|
io.ReaderAt
|
|
io.Writer
|
|
io.WriterAt
|
|
io.WriterTo
|
|
io.Seeker
|
|
io.StringWriter
|
|
io.Closer
|
|
io.ByteReader
|
|
io.ByteWriter
|
|
}
|
|
|
|
type File interface {
|
|
// CloseDelete closes and deletes the file if it is a regular file.
|
|
//
|
|
// It returns an error if the file is not a regular file, or if the file
|
|
// cannot be closed or deleted.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a
|
|
// symbolic link, the target of the symbolic link will not be deleted.
|
|
// If you want to delete the target of the symbolic link, you should use the
|
|
// os.Readlink function to get the target path and then the os.Remove function to
|
|
// delete the target.
|
|
CloseDelete() error
|
|
|
|
// Path returns the path of the file.
|
|
Path() string
|
|
// Stat returns the file information as os.FileInfo for the given file or an error.
|
|
//
|
|
// If the file is not a regular file, or if the file information cannot be retrieved,
|
|
// the function will return an error.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link,
|
|
// the target of the symbolic link will not be retrieved. If you want to retrieve the
|
|
// target of the symbolic link, you should use the os.Readlink function to get the target path
|
|
// and then the os.Stat function to retrieve the file information.
|
|
Stat() (os.FileInfo, error)
|
|
|
|
// SizeBOF returns the size of the file before the end of file (BOF) and an error if the size cannot be retrieved.
|
|
//
|
|
// The size is returned as an int64 and the error is returned as an error interface.
|
|
//
|
|
// If the file is not a regular file, or if the file information cannot be retrieved, the function will return an error.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link, the target of the
|
|
// symbolic link will not be retrieved. If you want to retrieve the target of the symbolic link, you should use
|
|
// the os.Readlink function to get the target path and then the os.Stat function to retrieve the file information.
|
|
SizeBOF() (size int64, err error)
|
|
// SizeEOF returns the size of the file from the current position to the end of the file (EOF)
|
|
// and an error if the size cannot be retrieved.
|
|
//
|
|
// The size is returned as an int64 and the error is returned as an error interface.
|
|
//
|
|
// If the file is not a regular file, or if the file information cannot be retrieved, the function will return an error.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link, the target of the
|
|
// symbolic link will not be retrieved. If you want to retrieve the target of the symbolic link, you should use
|
|
// the os.Readlink function to get the target path and then the os.Stat function to retrieve the file information.
|
|
SizeEOF() (size int64, err error)
|
|
|
|
// Truncate will truncate the file at the given size.
|
|
//
|
|
// The function will return an error if the file cannot be truncated at the given size.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link, the target of the
|
|
// symbolic link will not be truncated. If you want to truncate the target of the symbolic link, you should use
|
|
// the os.Readlink function to get the target path and then the os.Truncate function to truncate the target file.
|
|
Truncate(size int64) error
|
|
// Sync calls the Sync method on the underlying file.
|
|
//
|
|
// It returns an error if the Sync method cannot be called or if it returns an error.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link, the target of the
|
|
// symbolic link will not be synced. If you want to sync the target of the symbolic link, you should use
|
|
// the os.Readlink function to get the target path and then the os.File.Sync function to sync the target file.
|
|
Sync() error
|
|
}
|
|
|
|
type TempFile interface {
|
|
IsTemp() bool
|
|
}
|
|
|
|
type Progress interface {
|
|
GenericIO
|
|
File
|
|
TempFile
|
|
|
|
// RegisterFctIncrement registers a function to be called when the progress of
|
|
// a file being read or written reaches a certain number of bytes. The
|
|
// function will be called with the number of bytes that have been read or
|
|
// written from the start of the file. The function is called even if the
|
|
// registered progress is not reached (i.e. if the file is smaller than
|
|
// the registered progress). The function is called with the current
|
|
// progress when the file is closed (i.e. when io.Copy returns io.EOF).
|
|
//
|
|
// The function is called with the following signature:
|
|
//
|
|
// func(size int64)
|
|
//
|
|
// If the function is nil, it is simply ignored.
|
|
RegisterFctIncrement(fct FctIncrement)
|
|
// RegisterFctReset registers a function to be called when the progress of a
|
|
// file being read or written is reset. The function will be called with the
|
|
// maximum progress that has been reached and the current progress when
|
|
// the file is closed (i.e. when io.Copy returns io.EOF).
|
|
//
|
|
// The function is called with the following signature:
|
|
//
|
|
// func(size, current int64)
|
|
//
|
|
// If the function is nil, it is simply ignored.
|
|
RegisterFctReset(fct FctReset)
|
|
// RegisterFctEOF registers a function to be called when the end of a file is reached.
|
|
// The function will be called with no arguments and will be called even if the
|
|
// registered progress is not reached (i.e. if the file is smaller than
|
|
// the registered progress).
|
|
//
|
|
// If the function is nil, it is simply ignored.
|
|
RegisterFctEOF(fct FctEOF)
|
|
// SetBufferSize sets the buffer size for the progress.
|
|
//
|
|
// The buffer size is used for the io.Copy function when reading from or writing to a file.
|
|
//
|
|
// The buffer size should be a power of two and greater or equal to 1.
|
|
//
|
|
// If the buffer size is less than 1 or if it is not a power of two, the function will return an error.
|
|
//
|
|
// The function returns an error if the buffer size cannot be set.
|
|
//
|
|
// Note that this method does not follow symlinks. Therefore, if the file is a symbolic link, the target of the
|
|
// symbolic link will not be affected. If you want to set the buffer size of the target of the symbolic link, you
|
|
// should use the os.Readlink function to get the target path and then the os.File.SetBufferSize function to set the
|
|
// buffer size of the target file.
|
|
SetBufferSize(size int32)
|
|
// SetRegisterProgress sets the progress of the file.
|
|
//
|
|
// It takes a Progress interface as argument and sets the progress of the file to the given progress.
|
|
//
|
|
// The progress is used when the io.Copy function is called to read from or write to the file.
|
|
//
|
|
// The progress is used to call the functions registered with RegisterFctIncrement, RegisterFctReset and RegisterFctEOF.
|
|
//
|
|
// If the progress is nil, it is simply ignored.
|
|
//
|
|
// The function returns an error if the progress cannot be set.
|
|
SetRegisterProgress(f Progress)
|
|
|
|
// Reset resets the progress of the file to the given maximum progress.
|
|
//
|
|
// It is used to reset the progress of the file after it has been closed.
|
|
//
|
|
// If the maximum progress is less than 1, the function will return an error.
|
|
//
|
|
// The function returns an error if the progress cannot be reset.
|
|
Reset(max int64)
|
|
}
|
|
|
|
// New opens a file with the given name, flags and permissions and returns a Progress interface.
|
|
//
|
|
// The Progress interface is used to track the progress of the file when it is being read from or written to.
|
|
//
|
|
// The function returns an error if the file cannot be opened.
|
|
func New(name string, flags int, perm os.FileMode) (Progress, error) {
|
|
r, e := os.OpenRoot(filepath.Dir(name))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
f, e := r.OpenFile(filepath.Base(name), flags, perm)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
return &progress{
|
|
r: r,
|
|
f: f,
|
|
t: false,
|
|
b: new(atomic.Int32),
|
|
fi: new(atomic.Value),
|
|
fe: new(atomic.Value),
|
|
fr: new(atomic.Value),
|
|
}, nil
|
|
}
|
|
|
|
// Unique creates a new unique file in the given base path with the given pattern.
|
|
//
|
|
// The pattern should follow the rules of the os.CreateTemp function.
|
|
//
|
|
// The function returns a Progress interface and an error. The Progress interface is used to track the
|
|
// progress of the file when it is being read from or written to.
|
|
//
|
|
// The function returns an error if the file cannot be created.
|
|
func Unique(basePath, pattern string) (Progress, error) {
|
|
f, e := os.CreateTemp(basePath, pattern)
|
|
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
return &progress{
|
|
r: nil,
|
|
f: f,
|
|
t: false,
|
|
b: new(atomic.Int32),
|
|
fi: new(atomic.Value),
|
|
fe: new(atomic.Value),
|
|
fr: new(atomic.Value),
|
|
}, nil
|
|
}
|
|
|
|
// Temp creates a new temporary file with the given pattern and returns a Progress interface.
|
|
//
|
|
// The pattern should follow the rules of the os.CreateTemp function.
|
|
//
|
|
// The function returns an error if the file cannot be created.
|
|
//
|
|
// The Progress interface is used to track the progress of the file when it is being read from or written to.
|
|
//
|
|
// The function returns an error if the progress cannot be set.
|
|
//
|
|
// The function is used to create temporary files that are automatically deleted when they are closed.
|
|
func Temp(pattern string) (Progress, error) {
|
|
f, e := os.CreateTemp("", pattern)
|
|
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
return &progress{
|
|
r: nil,
|
|
f: f,
|
|
t: true,
|
|
b: new(atomic.Int32),
|
|
fi: new(atomic.Value),
|
|
fe: new(atomic.Value),
|
|
fr: new(atomic.Value),
|
|
}, nil
|
|
}
|
|
|
|
// Open opens the file with the given name and returns a Progress interface.
|
|
//
|
|
// The function returns an error if the file cannot be opened.
|
|
//
|
|
// The Progress interface is used to track the progress of the file when it is being read from or written to.
|
|
//
|
|
// The function returns an error if the progress cannot be set.
|
|
//
|
|
// The function is used to open existing files. If the file does not exist, the function will return an error.
|
|
func Open(name string) (Progress, error) {
|
|
r, e := os.OpenRoot(filepath.Dir(name))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
f, e := r.Open(filepath.Base(name))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
return &progress{
|
|
r: r,
|
|
f: f,
|
|
t: false,
|
|
b: new(atomic.Int32),
|
|
fi: new(atomic.Value),
|
|
fe: new(atomic.Value),
|
|
fr: new(atomic.Value),
|
|
}, nil
|
|
}
|
|
|
|
// Create creates a new file with the given name and returns a Progress interface.
|
|
//
|
|
// The function returns an error if the file cannot be created.
|
|
//
|
|
// The Progress interface is used to track the progress of the file when it is being read from or written to.
|
|
//
|
|
// The function returns an error if the progress cannot be set.
|
|
//
|
|
// The function is used to create new files. If the file already exists, the function will return an error.
|
|
func Create(name string) (Progress, error) {
|
|
r, e := os.OpenRoot(filepath.Dir(name))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
f, e := r.Create(filepath.Base(name))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
|
|
return &progress{
|
|
r: r,
|
|
f: f,
|
|
t: false,
|
|
b: new(atomic.Int32),
|
|
fi: new(atomic.Value),
|
|
fe: new(atomic.Value),
|
|
fr: new(atomic.Value),
|
|
}, nil
|
|
}
|