diff --git a/internal/transport/buffer.go b/internal/transport/buffer.go new file mode 100644 index 0000000..63ee8bc --- /dev/null +++ b/internal/transport/buffer.go @@ -0,0 +1,116 @@ +// Copyright 2024 Matthew Rich . All rights reserved. + +package transport + +import ( +_ "errors" + "path/filepath" + "io" + "os" + "net/url" + "strings" + "fmt" + "compress/gzip" +) + +type Buffer struct { + uri *url.URL + path string + exttype string + fileext string + readHandle *os.File + writeHandle *os.File + gzip bool + gzipWriter io.WriteCloser + gzipReader io.ReadCloser +} + +func NewBuffer(u *url.URL) (b *Buffer, err error) { + b = &Buffer{ + uri: u, + path: filepath.Join(u.Hostname(), u.RequestURI()), + } + b.extension() + b.DetectGzip() + + if b.path == "" || b.path == "-" { + b.readHandle = os.Stdin + b.writeHandle = os.Stdout + } else { + if b.readHandle, err = os.OpenFile(b.Path(), os.O_RDWR|os.O_CREATE, 0644); err != nil { + return + } + b.writeHandle = b.readHandle + } + + if b.Gzip() { + b.gzipWriter = gzip.NewWriter(b.writeHandle) + if b.gzipReader, err = gzip.NewReader(b.readHandle); err != nil { + return + } + } + return +} + +func (b *Buffer) extension() { + elements := strings.Split(b.path, ".") + numberOfElements := len(elements) + if numberOfElements > 2 { + b.exttype = elements[numberOfElements - 2] + b.fileext = elements[numberOfElements - 1] + } + b.exttype = elements[numberOfElements - 1] +} + +func (b *Buffer) DetectGzip() { + b.gzip = (b.uri.Query().Get("gzip") == "true" || b.fileext == "gz") +} + +func (b *Buffer) URI() *url.URL { + return b.uri +} + +func (b *Buffer) Path() string { + return b.path +} + +func (b *Buffer) Signature() (documentSignature string) { + if signatureResp, signatureErr := os.Open(fmt.Sprintf("%s.sig", b.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 (b *Buffer) ContentType() string { + return b.exttype +} + +func (b *Buffer) SetGzip(gzip bool) { + b.gzip = gzip +} + +func (b *Buffer) Gzip() bool { + return b.gzip +} + +func (b *Buffer) Reader() io.ReadCloser { + if b.Gzip() { + return b.gzipReader + } + return b.readHandle +} + +func (b *Buffer) Writer() io.WriteCloser { + if b.Gzip() { + return b.gzipWriter + } + return b.writeHandle +} diff --git a/internal/transport/buffer_test.go b/internal/transport/buffer_test.go new file mode 100644 index 0000000..bb1462f --- /dev/null +++ b/internal/transport/buffer_test.go @@ -0,0 +1,39 @@ +// Copyright 2024 Matthew Rich . All rights reserved. + +package transport + +import ( + "github.com/stretchr/testify/assert" + "testing" + "fmt" + "os" + "net/url" + "path/filepath" +) + +var TransportBufferTestFile = fmt.Sprintf("%s/foo", TempDir) + +func TestNewTransportBufferReader(t *testing.T) { + path := fmt.Sprintf("%s/foo", TempDir) + u, e := url.Parse(fmt.Sprintf("file://%s", path)) + assert.Nil(t, e) + + writeErr := os.WriteFile(path, []byte("test"), 0644) + assert.Nil(t, writeErr) + + file, err := NewBuffer(u) + assert.Nil(t, err) + assert.Equal(t, file.Path(), path) +} + +func TestNewTransportBufferReaderExtension(t *testing.T) { + u, e := url.Parse(fmt.Sprintf("file://%s.yaml", TransportBufferTestFile)) + assert.Nil(t, e) + + b := &Buffer{ + uri: u, + path: filepath.Join(u.Hostname(), u.RequestURI()), + } + b.extension() + assert.Equal(t, b.exttype, "yaml") +} diff --git a/internal/transport/file.go b/internal/transport/file.go index 1a9c8b4..0dea74a 100644 --- a/internal/transport/file.go +++ b/internal/transport/file.go @@ -25,10 +25,19 @@ type File struct { gzipReader io.ReadCloser } +func FilePath(u *url.URL) string { + return filepath.Join(u.Hostname(), u.RequestURI()) +} + +func FileExists(u *url.URL) bool { + _, err := os.Stat(FilePath(u)) + return err == nil +} + func NewFile(u *url.URL) (f *File, err error) { f = &File { uri: u, - path: filepath.Join(u.Hostname(), u.RequestURI()), + path: FilePath(u), } f.extension() f.DetectGzip() @@ -55,11 +64,13 @@ func NewFile(u *url.URL) (f *File, err error) { func (f *File) extension() { elements := strings.Split(f.path, ".") numberOfElements := len(elements) - if numberOfElements > 2 { - f.exttype = elements[numberOfElements - 2] - f.fileext = elements[numberOfElements - 1] + if numberOfElements > 1 { + if numberOfElements > 2 { + f.exttype = elements[numberOfElements - 2] + f.fileext = elements[numberOfElements - 1] + } + f.exttype = elements[numberOfElements - 1] } - f.exttype = elements[numberOfElements - 1] } func (f *File) DetectGzip() { @@ -90,6 +101,9 @@ func (f *File) Signature() (documentSignature string) { } func (f *File) ContentType() string { + if f.uri.Scheme != "file" { + return f.uri.Scheme + } return f.exttype } diff --git a/internal/transport/http.go b/internal/transport/http.go index ac502e1..8ef26dc 100644 --- a/internal/transport/http.go +++ b/internal/transport/http.go @@ -33,6 +33,10 @@ type HTTP struct { Client *http.Client } +func HTTPExists(u *url.URL) bool { + return false +} + func (b BufferCloser) Close() error { if b.stream != nil { return b.stream.Close() diff --git a/internal/transport/transport.go b/internal/transport/transport.go index 57284f8..e3e3bd3 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -27,6 +27,7 @@ type Reader struct { uri *url.URL handle Handler stream io.ReadCloser + exists func() bool } func NewReader(u *url.URL) (reader *Reader, e error) { @@ -39,8 +40,9 @@ func NewReader(u *url.URL) (reader *Reader, e error) { fallthrough default: reader.handle, e = NewFile(u) + reader.exists = func() bool { return FileExists(u) } } - reader.stream = reader.handle.Reader() + reader.SetStream(reader.handle.Reader()) return } @@ -56,6 +58,7 @@ type Writer struct { uri *url.URL handle Handler stream io.WriteCloser + exists func() bool } func NewWriter(u *url.URL) (writer *Writer, e error) { @@ -68,8 +71,9 @@ func NewWriter(u *url.URL) (writer *Writer, e error) { fallthrough default: writer.handle, e = NewFile(u) + writer.exists = func() bool { return FileExists(u) } } - writer.stream = writer.handle.Writer() + writer.SetStream(writer.handle.Writer()) return writer, e } @@ -81,6 +85,25 @@ func NewWriterURI(uri string) (writer *Writer, e error) { return } +func ExistsURI(uri string) bool { + var u *url.URL + u, _ = url.Parse(uri) + return Exists(u) +} + +func Exists(u *url.URL) bool { + switch u.Scheme { + case "http", "https": + return HTTPExists(u) + case "file": + fallthrough + default: + return FileExists(u) + } +} + +func (r *Reader) Exists() bool { return r.exists() } + func (r *Reader) Read(b []byte) (int, error) { return r.stream.Read(b) } @@ -101,8 +124,13 @@ func (r *Reader) Signature() string { return r.handle.Signature() } +func (r *Reader) SetStream(s io.ReadCloser) { + r.stream = s +} +func (w *Writer) Exists() bool { return w.exists() } + func (w *Writer) Write(b []byte) (int, error) { return w.stream.Write(b) } @@ -122,3 +150,7 @@ func (w *Writer) Gzip() bool { func (w *Writer) Signature() string { return w.handle.Signature() } + +func (w *Writer) SetStream(s io.WriteCloser) { + w.stream = s +} diff --git a/internal/transport/transport_test.go b/internal/transport/transport_test.go index 7bbb3a8..d54b8c7 100644 --- a/internal/transport/transport_test.go +++ b/internal/transport/transport_test.go @@ -52,13 +52,32 @@ func TestTransportReaderContentType(t *testing.T) { u, e := url.Parse(fmt.Sprintf("file://%s", path)) assert.Nil(t, e) + assert.False(t, Exists(u)) writeErr := os.WriteFile(path, []byte(testFileResourceDoc), 0644) assert.Nil(t, writeErr) reader, err := NewReader(u) assert.Nil(t, err) + assert.True(t, reader.Exists()) assert.NotNil(t, reader) assert.Equal(t, reader.ContentType(), "yaml") } + +func TestTransportWriter(t *testing.T) { + path := fmt.Sprintf("%s/writefoo", TempDir) + u, e := url.Parse(fmt.Sprintf("file://%s", path)) + assert.Nil(t, e) + + assert.False(t, Exists(u)) + writer, err := NewWriter(u) + assert.Nil(t, err) + defer writer.Close() + assert.NotNil(t, writer) + assert.True(t, writer.Exists()) + _, writeErr := writer.Write([]byte("testdata")) + assert.Nil(t, writeErr) + assert.FileExists(t, path) + +}