add extract function, add roadmap and doc link

This commit is contained in:
Shane C. 2024-12-03 10:38:29 -05:00
parent ad7dca46c7
commit 9cc659b973
Signed by: Shane C.
GPG key ID: E46B5FEA35B22FF9
4 changed files with 180 additions and 104 deletions

View file

@ -1,6 +1,12 @@
# Archiver # Archiver
A golang library for extracting and creating archives. A golang library for extracting and creating archives.
[Documentation Link](https://pkg.go.dev/egtyl.xyz/omnibill/archiver)
## Roadmap
- [ ] Automatically detect the archive format
- [ ] Add mutexes to work with concurrency
## Supported Formats ## Supported Formats
- `.tar.gz` - `.tar.gz`
- `.tar.bz2` - `.tar.bz2`

114
archive_fs_test.go Normal file
View file

@ -0,0 +1,114 @@
package archiver
import (
"archive/tar"
"compress/gzip"
"github.com/go-git/go-billy/v5/memfs"
"github.com/stretchr/testify/assert"
"os"
"strconv"
"testing"
)
func TestArchiveFS(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
memoryFS := memfs.New()
archiveF, err := memoryFS.OpenFile("archive.tar.gz", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm)
assert.NoError(t, err)
defer archiveF.Close()
gzipWriter := gzip.NewWriter(archiveF)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
for i := 0; i < 10; i++ {
tarHeader := &tar.Header{
Name: "test" + strconv.Itoa(i) + ".txt",
Size: int64(len([]byte("Hello, World! #" + strconv.Itoa(i)))),
Mode: 0600,
}
err = tarWriter.WriteHeader(tarHeader)
assert.NoError(t, err)
_, err = tarWriter.Write([]byte("Hello, World! #" + strconv.Itoa(i)))
assert.NoError(t, err)
}
err = tarWriter.Close()
assert.NoError(t, err)
err = gzipWriter.Close()
assert.NoError(t, err)
err = archiveF.Close()
assert.NoError(t, err)
t.Log("== OpenArchive ==")
archive, err := OpenFS(WrapBillyFS(memoryFS), TarGzip, "archive.tar.gz")
assert.NoError(t, err)
assert.Equal(t, 10, archive.FileCount())
t.Log("== ExtractArchiveFile ==")
archiveFile, err := archive.GetFile("test0.txt")
assert.NoError(t, err)
err = archiveFile.Extract(ExtractFileOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
})
assert.NoError(t, err)
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
t.Log("== ExtractArchive ==")
err = archive.Extract(ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
})
assert.NoError(t, err)
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
t.Log("== ExtractArchiveFilter ==")
err = archive.Extract(ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
Filter: archiveRegex,
})
assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test1.txt")
assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test5.txt")
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
err = archive.Close()
assert.NoError(t, err)
}
func TestExtractArchiveBillyFS(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
testGenerateTar(t)
archive, err := Open(Tar, testArchiveBaseDir+"/test.tar")
assert.NoError(t, err)
memoryFS := memfs.New()
err = archive.ExtractBillyFS(memoryFS, ExtractOptions{
Folder: memoryFS.Root(),
})
assert.NoError(t, err)
files, err := memoryFS.ReadDir(memoryFS.Root())
assert.NoError(t, err)
assert.Equal(t, 10, len(files))
for _, file := range files {
assert.NotEqual(t, int64(0), file.Size())
}
}

View file

@ -5,7 +5,6 @@ import (
"archive/zip" "archive/zip"
"compress/gzip" "compress/gzip"
"github.com/dsnet/compress/bzip2" "github.com/dsnet/compress/bzip2"
"github.com/go-git/go-billy/v5/memfs"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/ulikunitz/xz" "github.com/ulikunitz/xz"
"os" "os"
@ -20,6 +19,48 @@ const (
var archiveRegex = regexp.MustCompile(`(?m)test[1|5]`) var archiveRegex = regexp.MustCompile(`(?m)test[1|5]`)
func TestArchiveExtract(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
testGenerateZip(t)
testGenerateTar(t)
testGenerateTarXz(t)
testGenerateTarBzip(t)
TestArchiveTarGzip(t)
t.Log("== ExtractZip ==")
err = Extract(Zip, testArchiveBaseDir+"/test.zip", ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/zip",
})
assert.NoError(t, err)
t.Log("== ExtractTar ==")
err = Extract(Tar, testArchiveBaseDir+"/test.tar", ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/tar",
})
assert.NoError(t, err)
t.Log("== ExtractTarXz ==")
err = Extract(TarXz, testArchiveBaseDir+"/test.tar.xz", ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/xz",
})
assert.NoError(t, err)
t.Log("== ExtractTarBz ==")
err = Extract(TarBzip, testArchiveBaseDir+"/test.tar.bz2", ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/bzip",
})
assert.NoError(t, err)
t.Log("== ExtractTarGz ==")
err = Extract(TarGzip, testArchiveBaseDir+"/test.tar.gz", ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/gz",
})
assert.NoError(t, err)
}
func TestArchiveZip(t *testing.T) { func TestArchiveZip(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
@ -252,109 +293,6 @@ func TestArchiveTar(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestArchiveFS(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
memoryFS := memfs.New()
archiveF, err := memoryFS.OpenFile("archive.tar.gz", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm)
assert.NoError(t, err)
defer archiveF.Close()
gzipWriter := gzip.NewWriter(archiveF)
defer gzipWriter.Close()
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
for i := 0; i < 10; i++ {
tarHeader := &tar.Header{
Name: "test" + strconv.Itoa(i) + ".txt",
Size: int64(len([]byte("Hello, World! #" + strconv.Itoa(i)))),
Mode: 0600,
}
err = tarWriter.WriteHeader(tarHeader)
assert.NoError(t, err)
_, err = tarWriter.Write([]byte("Hello, World! #" + strconv.Itoa(i)))
assert.NoError(t, err)
}
err = tarWriter.Close()
assert.NoError(t, err)
err = gzipWriter.Close()
assert.NoError(t, err)
err = archiveF.Close()
assert.NoError(t, err)
t.Log("== OpenArchive ==")
archive, err := OpenFS(WrapBillyFS(memoryFS), TarGzip, "archive.tar.gz")
assert.NoError(t, err)
assert.Equal(t, 10, archive.FileCount())
t.Log("== ExtractArchiveFile ==")
archiveFile, err := archive.GetFile("test0.txt")
assert.NoError(t, err)
err = archiveFile.Extract(ExtractFileOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
})
assert.NoError(t, err)
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
t.Log("== ExtractArchive ==")
err = archive.Extract(ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
})
assert.NoError(t, err)
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
t.Log("== ExtractArchiveFilter ==")
err = archive.Extract(ExtractOptions{
Folder: testArchiveBaseDir + "/extracted/fs",
Filter: archiveRegex,
})
assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test1.txt")
assert.FileExists(t, testArchiveBaseDir+"/extracted/fs/test5.txt")
err = os.RemoveAll(testArchiveBaseDir + "/extracted/fs")
assert.NoError(t, err)
err = archive.Close()
assert.NoError(t, err)
}
func TestExtractArchiveBillyFS(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
testGenerateTar(t)
archive, err := Open(Tar, testArchiveBaseDir+"/test.tar")
assert.NoError(t, err)
memoryFS := memfs.New()
err = archive.ExtractBillyFS(memoryFS, ExtractOptions{
Folder: memoryFS.Root(),
})
assert.NoError(t, err)
files, err := memoryFS.ReadDir(memoryFS.Root())
assert.NoError(t, err)
assert.Equal(t, 10, len(files))
for _, file := range files {
assert.NotEqual(t, int64(0), file.Size())
}
}
func testGenerateTar(t *testing.T) { func testGenerateTar(t *testing.T) {
tarFile, err := os.Create(testArchiveBaseDir + "/test.tar") tarFile, err := os.Create(testArchiveBaseDir + "/test.tar")
defer tarFile.Close() defer tarFile.Close()

18
open.go
View file

@ -30,6 +30,24 @@ func Open(archiveType Type, path string) (*Archive, error) {
return openArchive(WrapPath(archiveFolderPath), archiveType, path) return openArchive(WrapPath(archiveFolderPath), archiveType, path)
} }
// Extract opens an archive file and extracts the contents.
// It takes a Type, a path, and ExtractOptions for its parameters.
// The function returns an error, if any.
func Extract(archiveType Type, path string, options ExtractOptions) error {
archiveFile, err := Open(archiveType, path)
if err != nil {
return err
}
defer archiveFile.Close()
if err := archiveFile.Extract(options); err != nil {
return err
}
if err := archiveFile.Close(); err != nil {
return err
}
return nil
}
func openArchive(filesystem Filesystem, archiveType Type, path string) (*Archive, error) { func openArchive(filesystem Filesystem, archiveType Type, path string) (*Archive, error) {
archive := new(Archive) archive := new(Archive)
archive.files = make(map[string]*File) archive.files = make(map[string]*File)