// Copyright 2024 Matthew Rich . All rights reserved. package transport import ( _ "errors" "path/filepath" "io" "io/fs" "os" "net/url" "strings" "fmt" "compress/gzip" "log/slog" "decl/internal/ext" ) type File struct { uri *url.URL path string exttype string fileext string readHandle *os.File writeHandle *os.File gzip bool gzipWriter io.WriteCloser gzipReader io.ReadCloser } type FileReader struct { *File readHandle *os.File gzipReader io.ReadCloser } type FileWriter struct { *File writeHandle *os.File gzipWriter io.WriteCloser } func FilePath(u *url.URL) string { return filepath.Join(u.Hostname(), u.Path) } func FileExists(u *url.URL) bool { _, err := os.Stat(FilePath(u)) return !os.IsNotExist(err) } func NewFileReader(u *url.URL) (f *FileReader, err error) { f = &FileReader { File: &File { uri: u, path: FilePath(u), }, } f.extension() f.DetectGzip() exists := FileExists(u) slog.Info("transport.NewFileReader()", "uri", u, "path", f.Path(), "file", f, "error", err, "exists", exists) if f.Path() == "" || f.Path() == "-" { f.readHandle = os.Stdin } else { if f.readHandle, err = os.Open(f.Path()); err == nil { var fi fs.FileInfo fi, err = f.readHandle.Stat() if fi.IsDir() { f.readHandle.Close() f.readHandle = nil err = fmt.Errorf("is a directory") } } if err != nil { slog.Info("transport.NewFileReader()", "file", f, "path", f.Path(), "error", err) return } } if f.Gzip() { if exists { if f.gzipReader, err = gzip.NewReader(f.readHandle); err != nil { return } } } slog.Info("transport.NewFileReader() - created reader transport", "uri", u, "file", f, "error", err) return } func NewFileWriter(u *url.URL) (f *FileWriter, err error) { f = &FileWriter { File: &File { uri: u, path: FilePath(u), }, } f.extension() f.DetectGzip() slog.Info("transport.NewFileWriter()", "file", f.File) exists := FileExists(u) slog.Info("transport.NewFileWriter()", "file", f, "error", err, "exists", exists) if f.Path() == "" || f.Path() == "-" { f.writeHandle = os.Stdout } else { if f.writeHandle, err = os.OpenFile(f.Path(), os.O_RDWR|os.O_CREATE, 0644); err != nil { slog.Info("transport.NewFileWriter()", "file", f, "path", f.Path(), "error", err) return } } if f.Gzip() { f.gzipWriter = ext.WriteAddCloser(gzip.NewWriter(f.writeHandle), func() error { return f.writeHandle.Close() }) } slog.Info("transport.NewFileWriter()", "file", f, "error", err) return } func NewFile(u *url.URL) (f *File, err error) { f = &File { uri: u, path: FilePath(u), } f.extension() f.DetectGzip() exists := FileExists(u) slog.Info("transport.NewFile()", "file", f, "error", err, "exists", exists) if f.path == "" || f.path == "-" { f.readHandle = os.Stdin f.writeHandle = os.Stdout } else { if f.readHandle, err = os.OpenFile(f.Path(), os.O_RDWR|os.O_CREATE, 0644); err != nil { slog.Info("transport.NewFile()", "file", f, "path", f.Path(), "error", err) return } f.writeHandle = f.readHandle } if f.Gzip() { f.gzipWriter = ext.WriteAddCloser(gzip.NewWriter(f.writeHandle), func() error { return f.writeHandle.Close() }) if exists { if f.gzipReader, err = gzip.NewReader(f.readHandle); err != nil { return } } } slog.Info("transport.NewFile()", "file", f, "error", err) return } func (f *File) extension() { elements := strings.Split(f.path, ".") numberOfElements := len(elements) if numberOfElements > 1 { if numberOfElements > 2 { f.exttype = elements[numberOfElements - 2] f.fileext = elements[numberOfElements - 1] } else { f.exttype = elements[numberOfElements - 1] } } } func (f *File) DetectGzip() { f.gzip = (f.uri.Query().Get("gzip") == "true" || f.fileext == "gz" || f.exttype == "tgz" || f.exttype == "gz" || f.fileext == "tgz") } func (f *File) URI() *url.URL { return f.uri } func (f *File) Path() string { return f.path } func (f *File) Signature() (documentSignature string) { if signatureResp, signatureErr := os.Open(fmt.Sprintf("%s.sig", f.uri.String())); signatureErr == nil { defer signatureResp.Close() readSignatureBody, readSignatureErr := io.ReadAll(signatureResp) if readSignatureErr == nil { documentSignature = string(readSignatureBody) } else { panic(readSignatureErr) } } else { panic(signatureErr) } return documentSignature } func (f *File) ContentType() string { var ext strings.Builder if f.uri.Scheme != "file" { return f.uri.Scheme } if f.fileext == "" { return f.exttype } ext.WriteString(f.exttype) ext.WriteRune('.') ext.WriteString(f.fileext) return ext.String() } func (f *File) Stat() (fs.FileInfo, error) { return f.FileInfo() } func (f *File) FileInfo() (info fs.FileInfo, err error) { return os.Lstat(f.Path()) } func (f *File) SetGzip(gzip bool) { f.gzip = gzip } func (f *File) Gzip() bool { return f.gzip } func (f *FileReader) Reader() io.ReadCloser { if f.Gzip() { var err error if f.gzipReader, err = gzip.NewReader(f.readHandle); err != nil { panic(err) } return f.gzipReader } return f.readHandle } func (f *FileWriter) Writer() io.WriteCloser { if f.Gzip() { f.gzipWriter = gzip.NewWriter(f.writeHandle) return f.gzipWriter } return f.writeHandle } func (f *File) ReadWriter() io.ReadWriteCloser { return f.writeHandle }