joint

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2024 License: MIT Imports: 18 Imported by: 3

README

Joint

Provides access to files in ISO-9660 images, FTP-servers, SFTP-servers, WebDAV-servers by standard file system interfaces. Contains cache with reusable connections to endpoints.

Go Reference Go Report Card Hits-of-Code

Goals

You can read the contents of a folder on an FTP-server either sequentially through one connection, but then this will take a lot of time, or by opening multiple connections, in which case too many connections will be opened for multiple requests. This library uses joints, which hold the connection to the FTP-server after reading a file, or some kind of access, after closing the file, they are placed into the cache, and can be reused later. If the connection has not been used for a certain time, it is reset.

Examples

HTTP file server with WebDAV content
package main

import (
    "log"
    "net/http"

    jnt "github.com/schwarzlichtbezirk/hms/joint"
)

// Open http://localhost:8080/ in browser
// to get a list of files in WebDAV-server for given user.
func main() {
    var sp = jnt.NewSubPool(nil, "https://music:x@example.keenetic.link/webdav/")
    defer sp.Close()
    http.Handle("/", http.FileServer(http.FS(sp)))
    log.Fatal(http.ListenAndServe(":8080", nil))
}
HTTP file server with ISO-image content
package main

import (
    "log"
    "net/http"

    jnt "github.com/schwarzlichtbezirk/hms/joint"
)

// Open http://localhost:8080/ in browser
// to get a list of files in ISO-image.
func main() {
    // create map with caches for all currently unused joints
    var jp = jnt.NewJointPool()
    defer jp.Close()
    // file system, that shares content of "testdata" folder
    // and all embedded into ISO-disks files
    var sp, err = jp.Sub("testdata")
    if err != nil {
        log.Fatal(err)
    }
    http.Handle("/", http.FileServer(http.FS(sp)))
    log.Fatal(http.ListenAndServe(":8080", nil))
}
HTTP file server with ISO, WebDAV, FTP and SFTP content
package main

import (
    "log"
    "net/http"

    jnt "github.com/schwarzlichtbezirk/hms/joint"
)

// http://localhost:8080/iso/ - content of ISO-image
// http://localhost:8080/dav/ - content of WebDAV-server
// http://localhost:8080/ftp/ - content of FTP-server
// http://localhost:8080/sftp/ - content of SFTP-server
func main() {
    // create map with caches for all currently unused joints
    var jp = jnt.NewJointPool()
    defer jp.Close()
    // handle list of resources as binded file systems
    http.Handle("/iso/", http.StripPrefix("/iso/", http.FileServer(
        http.FS(jnt.NewSubPool(jp, "testdata/external.iso")))))
    http.Handle("/dav/", http.StripPrefix("/dav/", http.FileServer(
        http.FS(jnt.NewSubPool(jp, "https://music:x@example.keenetic.link/webdav/")))))
    http.Handle("/ftp/", http.StripPrefix("/ftp/", http.FileServer(
        http.FS(jnt.NewSubPool(jp, "ftp://music:x@192.168.1.1:21")))))
    http.Handle("/sftp/", http.StripPrefix("/sftp/", http.FileServer(
        http.FS(jnt.NewSubPool(jp, "sftp://music:x@192.168.1.1:22")))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}
Files reading by joints
package main

import (
    "fmt"
    "io"
    "io/fs"
    "log"

    jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
    var err error

    // Create joint to ISO-9660 image.
    var j jnt.Joint = &jnt.IsoJoint{}
    if err = j.Make(nil, "testdata/external.iso"); err != nil {
        log.Fatal(err)
    }
    // Cleanup drops joint's link at the end. Any not cached joints
    // should be destroyed by Cleanup call.
    defer j.Cleanup()

    // Working with file object returned by Open-function.
    // Open-function returns joint casted to fs.File.
    var f fs.File
    if f, err = j.Open("fox.txt"); err != nil {
        log.Fatal(err)
    }
    var b []byte
    if b, err = io.ReadAll(f); err != nil { // read from file
        log.Fatal(err)
    }
    fmt.Println(string(b))
    f.Close()

    // Working with joint explicitly. If joint is received from cache,
    // Close-call will return joint back to cache.
    if _, err = j.Open("data/lorem1.txt"); err != nil {
        log.Fatal(err)
    }
    if _, err = io.Copy(os.Stdout, j); err != nil { // read from joint
        log.Fatal(err)
    }
    j.Close() // f.Close() and j.Close() do the same work
}
Open nested ISO-image by joints
package main

import (
    "io"
    "io/fs"
    "log"
    "os"

    jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
    var err error

    // Create joint to external ISO-image.
    // This joint will be removed by top-level joint.
    var j1 jnt.Joint = &jnt.IsoJoint{}
    if err = j1.Make(nil, "testdata/external.iso"); err != nil {
        log.Fatal(err)
    }
    defer j1.Cleanup() // Cleanup or Close can be called twice

    // Create top-level joint to internal ISO-image placed inside of first.
    var j2 jnt.Joint = &jnt.IsoJoint{}
    if err = j2.Make(j1, "disk/internal.iso"); err != nil {
        log.Fatal(err)
    }
    // Top-level joint calls inherited Cleanup, so this call can be one.
    defer j2.Cleanup()

    // Open file at internal ISO-image.
    var f fs.File
    if f, err = j2.Open("fox.txt"); err != nil {
        log.Fatal(err)
    }
    defer f.Close() // Close file and joint can be reused

    io.Copy(os.Stdout, f)
}
Simple file reading
package main

import (
    "io"
    "log"
    "os"

    jnt "github.com/schwarzlichtbezirk/joint"
)

var jp = jnt.NewJointPool()

func main() {
    // Open file and get the joint by one call.
    var j, err = jp.Open("testdata/external.iso/disk/internal.iso/docs/doc1.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer j.Cleanup()

    io.Copy(os.Stdout, j)
}

Tests remarks

Unit tests for services runs on real FTP, SFTP and WebDAV services, not on any emulations. Before run go test command it should be set 3 environment variables:

  1. JOINT_FTP with address of FTP-service and credentials. As in pattern set JOINT_FTP=ftp://user:password@192.168.1.1:21
  2. JOINT_SFTP with address of SFTP-service and credentials. As in pattern set JOINT_SFTP=sftp://user:password@192.168.1.1:22
  3. JOINT_DAV with URL of WebDAV-service and credentials. As in pattern set JOINT_DAV=https://user:password@example.keenetic.link/webdav/

If some environment variables not set or empty, those tests will be skipped.

Then copy testdata folder with ISO-file to the services root folder as is.


(c) schwarzlichtbezirk, 2023-2024.

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrFtpWhence = errors.New("invalid whence at FTP seeker")
	ErrFtpNegPos = errors.New("negative position at FTP seeker")
)
View Source
var Cfg = Config{
	DialTimeout:     5 * time.Second,
	DiskCacheExpire: 2 * time.Minute,
}

Cfg is singleton with timeouts settings for all joints.

Functions

func FindDavRoot added in v0.4.0

func FindDavRoot(addr, fpath string) (root string, ok bool)

FindDavRoot returns cached root for given address, or tries to find it from given path.

func FtpEscapeBrackets

func FtpEscapeBrackets(s string) string

FtpEscapeBrackets escapes square brackets at FTP-path. FTP-server does not recognize path with square brackets as is to get a list of files, so such path should be escaped.

Example
package main

import (
	"fmt"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	fmt.Println(jnt.FtpEscapeBrackets("Music/Denney [2018]"))
}
Output:

Music/Denney [[]2018[]]

func GetDavRoot added in v0.4.0

func GetDavRoot(addr string) (root string, ok bool)

GetDavRoot returns cached WebDAV root for given address.

func HasFoldPrefix added in v0.4.0

func HasFoldPrefix(s, prefix string) bool

HasFoldPrefix tests whether the string s begins with prefix without case sensitivity.

func IsTypeIso added in v0.2.0

func IsTypeIso(fpath string) bool

IsTypeIso checks that endpoint-file in given path has ISO-extension.

func JoinPath added in v0.4.0

func JoinPath(dir, base string) string

JoinPath performs fast join of two path chunks.

Example

JoinPath can be used for high performance path concatenation.

package main

import (
	"fmt"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	fmt.Println(jnt.JoinPath("any/path", "fox.txt"))
	fmt.Println(jnt.JoinPath("any/path/", "fox.txt"))
	fmt.Println(jnt.JoinPath("any", "/path/fox.txt"))
	fmt.Println(jnt.JoinPath("any/path/", "/fox.txt"))
	fmt.Println(jnt.JoinPath("/any/path", "fox.txt"))
}
Output:

any/path/fox.txt
any/path/fox.txt
any/path/fox.txt
any/path/fox.txt
/any/path/fox.txt

func SetDavRoot added in v0.4.0

func SetDavRoot(addr, root string)

SetDavRoot associates given WebDAV root with address without check up.

func SftpPwd

func SftpPwd(ftpaddr string, client *sftp.Client) (pwd string, err error)

SftpPwd return SFTP current directory. It's used cache to avoid extra calls to SFTP-server to get current directory for every call.

func SplitKey added in v0.2.0

func SplitKey(fullpath string) (string, string, bool)

SplitKey splits full path to joint key to establish link, and remained local path. Also returns boolean value that given path is not at primary file system.

Example
package main

import (
	"fmt"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var list = []string{
		"some/path/fox.txt",
		"testdata/external.iso",
		"testdata/external.iso/fox.txt",
		"testdata/external.iso/disk/internal.iso/fox.txt",
		"ftp://music:x@192.168.1.1:21/Music",
		"ftp://music:x@192.168.1.1:21/testdata/external.iso/disk/internal.iso/docs/doc1.txt",
		"https://music:x@example.keenetic.link/webdav/Global%20Underground/Nubreed/",
	}
	jnt.SetDavRoot("https://music:x@example.keenetic.link", "/webdav/")
	for _, s := range list {
		var key, fpath, _ = jnt.SplitKey(s)
		fmt.Printf("key: '%s', path: '%s'\n", key, fpath)
	}
}
Output:

key: '', path: 'some/path/fox.txt'
key: 'testdata/external.iso', path: ''
key: 'testdata/external.iso', path: 'fox.txt'
key: 'testdata/external.iso/disk/internal.iso', path: 'fox.txt'
key: 'ftp://music:x@192.168.1.1:21', path: 'Music'
key: 'ftp://music:x@192.168.1.1:21/testdata/external.iso/disk/internal.iso', path: 'docs/doc1.txt'
key: 'https://music:x@example.keenetic.link/webdav/', path: 'Global%20Underground/Nubreed/'

func SplitUrl

func SplitUrl(urlpath string) (string, string, bool)

SplitUrl splits URL to address string and to path as is. For file path it splits to volume name and path at this volume.

Example
package main

import (
	"fmt"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var list = []string{
		"ftp://music:x@192.168.1.1:21/Music/DJ.m3u",
		"sftp://music:x@192.168.1.1:22/Video",
		"https://github.com/schwarzlichtbezirk/joint",
		"https://pkg.go.dev/github.com/schwarzlichtbezirk/joint",
		"C:\\Windows\\System",
	}
	for _, s := range list {
		var addr, fpath, url = jnt.SplitUrl(s)
		fmt.Printf("addr: %s, path: %s, url: %t\n", addr, fpath, url)
	}
}
Output:

addr: ftp://music:x@192.168.1.1:21, path: Music/DJ.m3u, url: true
addr: sftp://music:x@192.168.1.1:22, path: Video, url: true
addr: https://github.com, path: schwarzlichtbezirk/joint, url: true
addr: https://pkg.go.dev, path: github.com/schwarzlichtbezirk/joint, url: true
addr: C:, path: Windows\System, url: false

func ToDirEntry added in v0.3.0

func ToDirEntry(fi fs.FileInfo) fs.DirEntry

ToDirEntry returns FileInfo that compatible with fs.DirEntry interface, and have derived IsDir.

Types

type Config

type Config struct {
	// Timeout to establish connection to FTP-server.
	DialTimeout time.Duration `json:"dial-timeout" yaml:"dial-timeout" xml:"dial-timeout"`
	// Expiration duration to keep opened iso-disk structures in cache from last access to it.
	DiskCacheExpire time.Duration `json:"disk-cache-expire" yaml:"disk-cache-expire" xml:"disk-cache-expire"`
}

type DavFileInfo

type DavFileInfo = gowebdav.File

type DavJoint

type DavJoint struct {
	io.ReadCloser
	// contains filtered or unexported fields
}

DavJoint keeps gowebdav.Client object. Key is URL to service, address + service route, i.e. https://user:pass@example.com/webdav/.

func (*DavJoint) Busy

func (j *DavJoint) Busy() bool

func (*DavJoint) Cleanup

func (j *DavJoint) Cleanup() error

func (*DavJoint) Close

func (j *DavJoint) Close() (err error)

func (*DavJoint) Info

func (j *DavJoint) Info(fpath string) (fs.FileInfo, error)

func (*DavJoint) Make

func (j *DavJoint) Make(base Joint, urladdr string) (err error)

func (*DavJoint) Open

func (j *DavJoint) Open(fpath string) (file fs.File, err error)

func (*DavJoint) Read

func (j *DavJoint) Read(b []byte) (n int, err error)

func (*DavJoint) ReadAt

func (j *DavJoint) ReadAt(b []byte, off int64) (n int, err error)

func (*DavJoint) ReadDir

func (j *DavJoint) ReadDir(n int) (list []fs.DirEntry, err error)

func (*DavJoint) Seek

func (j *DavJoint) Seek(offset int64, whence int) (abs int64, err error)

func (*DavJoint) Size added in v0.3.0

func (j *DavJoint) Size() (int64, error)

func (*DavJoint) Stat

func (j *DavJoint) Stat() (fs.FileInfo, error)

type FileInfo added in v0.3.0

type FileInfo interface {
	fs.FileInfo
	IsRealDir() bool // returns real file state representation
}

FileInfo inherits fs.FileInfo and provide IsDir that returns true for ISO-disk files. Has IsRealDir that provide value from inherited fs.FileInfo.

func ToFileInfo added in v0.3.0

func ToFileInfo(fi fs.FileInfo) FileInfo

ToFileInfo converts base fs.FileInfo to FileInfo that compatible both with fs.FileInfo and with fs.DirEntry interface and have derived IsDir.

type FtpFileInfo

type FtpFileInfo struct {
	*ftp.Entry
}

FtpFileInfo encapsulates ftp.Entry structure and provides fs.FileInfo implementation.

func (FtpFileInfo) Info added in v0.3.0

func (fi FtpFileInfo) Info() (fs.FileInfo, error)

Info provided for fs.DirEntry compatibility and returns object itself.

func (FtpFileInfo) IsDir

func (fi FtpFileInfo) IsDir() bool

fs.FileInfo implementation.

func (FtpFileInfo) IsRealDir added in v0.3.0

func (fi FtpFileInfo) IsRealDir() bool

func (FtpFileInfo) ModTime

func (fi FtpFileInfo) ModTime() time.Time

fs.FileInfo implementation.

func (FtpFileInfo) Mode

func (fi FtpFileInfo) Mode() fs.FileMode

fs.FileInfo implementation.

func (FtpFileInfo) Name

func (fi FtpFileInfo) Name() string

fs.FileInfo implementation.

func (FtpFileInfo) Size

func (fi FtpFileInfo) Size() int64

fs.FileInfo implementation.

func (FtpFileInfo) String added in v0.3.0

func (fi FtpFileInfo) String() string

func (FtpFileInfo) Sys

func (fi FtpFileInfo) Sys() interface{}

fs.FileInfo implementation. Returns structure pointer itself.

func (FtpFileInfo) Type added in v0.3.0

func (fi FtpFileInfo) Type() fs.FileMode

type FtpJoint

type FtpJoint struct {
	// contains filtered or unexported fields
}

FtpJoint create connection to FTP-server, login with provided by given URL credentials, and gets a once current directory. Key is address of FTP-service, i.e. ftp://user:pass@example.com.

func (*FtpJoint) Busy

func (j *FtpJoint) Busy() bool

func (*FtpJoint) ChangeDir added in v0.3.0

func (j *FtpJoint) ChangeDir(wd string) (err error)

func (*FtpJoint) ChangeDirToParent added in v0.3.0

func (j *FtpJoint) ChangeDirToParent() (err error)

func (*FtpJoint) Cleanup

func (j *FtpJoint) Cleanup() error

func (*FtpJoint) Close

func (j *FtpJoint) Close() (err error)

func (*FtpJoint) CurrentDir added in v0.3.0

func (j *FtpJoint) CurrentDir() (wd string, err error)

func (*FtpJoint) Info

func (j *FtpJoint) Info(fpath string) (fs.FileInfo, error)

func (*FtpJoint) Make

func (j *FtpJoint) Make(base Joint, urladdr string) (err error)

func (*FtpJoint) ModTime added in v0.5.0

func (j *FtpJoint) ModTime() (time.Time, error)

func (*FtpJoint) Open

func (j *FtpJoint) Open(fpath string) (file fs.File, err error)

func (*FtpJoint) Read

func (j *FtpJoint) Read(b []byte) (n int, err error)

func (*FtpJoint) ReadAt

func (j *FtpJoint) ReadAt(b []byte, off int64) (n int, err error)

func (*FtpJoint) ReadDir

func (j *FtpJoint) ReadDir(n int) (list []fs.DirEntry, err error)

func (*FtpJoint) Seek

func (j *FtpJoint) Seek(offset int64, whence int) (abs int64, err error)

func (*FtpJoint) Size

func (j *FtpJoint) Size() (int64, error)

func (*FtpJoint) Stat

func (j *FtpJoint) Stat() (fs.FileInfo, error)

func (*FtpJoint) Write

func (j *FtpJoint) Write(p []byte) (n int, err error)

type IsoFileInfo

type IsoFileInfo struct {
	*iso.File
}

func (IsoFileInfo) Info added in v0.3.0

func (fi IsoFileInfo) Info() (fs.FileInfo, error)

Info provided for fs.DirEntry compatibility and returns object itself.

func (IsoFileInfo) IsDir added in v0.3.0

func (fi IsoFileInfo) IsDir() bool

func (IsoFileInfo) IsRealDir added in v0.3.0

func (fi IsoFileInfo) IsRealDir() bool

func (IsoFileInfo) Mode added in v0.3.0

func (fi IsoFileInfo) Mode() fs.FileMode

func (IsoFileInfo) Name

func (fi IsoFileInfo) Name() string

Name returns filename translated from cyrillic to unicode.

func (IsoFileInfo) String added in v0.3.0

func (fi IsoFileInfo) String() string

func (IsoFileInfo) Sys

func (fi IsoFileInfo) Sys() interface{}

Sys returns IsoFileInfo itself.

func (IsoFileInfo) Type added in v0.3.0

func (fi IsoFileInfo) Type() fs.FileMode

type IsoJoint

type IsoJoint struct {
	Base Joint

	*iso.File
	*io.SectionReader
	// contains filtered or unexported fields
}

IsoJoint opens file with ISO9660 disk and prepares disk-structure to access to nested files. Key is external path, to ISO9660-file disk image at local filesystem.

func (*IsoJoint) Busy

func (j *IsoJoint) Busy() bool

func (*IsoJoint) Cleanup

func (j *IsoJoint) Cleanup() (err error)

func (*IsoJoint) Close

func (j *IsoJoint) Close() error

func (*IsoJoint) Info

func (j *IsoJoint) Info(fpath string) (fs.FileInfo, error)

func (*IsoJoint) Make

func (j *IsoJoint) Make(base Joint, isopath string) (err error)

func (*IsoJoint) Open

func (j *IsoJoint) Open(fpath string) (file fs.File, err error)

func (*IsoJoint) OpenFile

func (j *IsoJoint) OpenFile(fpath string) (*iso.File, error)

func (*IsoJoint) ReadDir

func (j *IsoJoint) ReadDir(n int) (list []fs.DirEntry, err error)

func (*IsoJoint) Size added in v0.3.0

func (j *IsoJoint) Size() (int64, error)

func (*IsoJoint) Stat

func (j *IsoJoint) Stat() (fs.FileInfo, error)

type Joint

type Joint interface {
	Make(Joint, string) error // establish connection to file system provider
	Cleanup() error           // close connection to file system provider
	Busy() bool               // file is opened
	fs.FS                     // open file with local file path
	io.Closer                 // close local file
	Size() (int64, error)     // helps to make io.SectionReader
	fs.ReadDirFile            // read directory pointed by local file path
	RFile
}

Joint describes interface with joint to some file system provider.

func MakeJoint added in v0.2.0

func MakeJoint(fullpath string) (j Joint, err error)

MakeJoint creates joint with all subsequent chain of joints. Please note that folders with .iso extension and non ISO-images with .iso extension will cause an error.

type JointCache

type JointCache struct {
	// contains filtered or unexported fields
}

JointCache implements cache with opened joints to some file system resource.

func NewJointCache

func NewJointCache(key string) *JointCache

func (*JointCache) Close

func (jc *JointCache) Close() (err error)

Close performs close-call to all cached disk joints.

func (*JointCache) Count

func (jc *JointCache) Count() int

Count is number of free joints in cache for one key path.

func (*JointCache) Eject added in v0.3.0

func (jc *JointCache) Eject(j Joint) bool

Eject joint from the cache.

func (*JointCache) Get

func (jc *JointCache) Get() (jw JointWrap, err error)

Get retrieves cached disk joint, or makes new one.

func (*JointCache) Has

func (jc *JointCache) Has(j Joint) bool

Checks whether it is contained joint in cache.

func (*JointCache) Key added in v0.2.0

func (jc *JointCache) Key() string

Key is the base address or path for cache file system.

func (*JointCache) Open

func (jc *JointCache) Open(fpath string) (f fs.File, err error)

Open implements fs.FS interface, and returns file that can be casted to joint wrapper. Note that internal ISO-files are considered as directories and it should be provided another JointCache to work with their file system. Use JointPool on this case.

Example
package main

import (
	"io"
	"log"
	"os"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var jc = jnt.NewJointCache("testdata/external.iso")
	defer jc.Close()

	var f, err = jc.Open("fox.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	io.Copy(os.Stdout, f)
}
Output:

The quick brown fox jumps over the lazy dog.

func (*JointCache) Pop

func (jc *JointCache) Pop() (jw JointWrap, ok bool)

Pop retrieves cached disk joint, and returns ok if it has.

func (*JointCache) Put

func (jc *JointCache) Put(j Joint)

Put disk joint to cache.

func (*JointCache) ReadDir

func (jc *JointCache) ReadDir(fpath string) (list []fs.DirEntry, err error)

ReadDir implements fs.ReadDirFS interface.

func (*JointCache) Stat

func (jc *JointCache) Stat(fpath string) (fi fs.FileInfo, err error)

Stat implements fs.StatFS interface.

type JointFileInfo added in v0.5.0

type JointFileInfo interface {
	fs.FileInfo
	IsRealDir() bool
}

JointFileInfo have additional IsRealDir, which points real file representation.

type JointPool

type JointPool struct {
	// contains filtered or unexported fields
}

JointPool is map with joint caches. Each key in map is address or path to file system resource, value - cached for this resource list of joints.

func NewJointPool added in v0.2.0

func NewJointPool() *JointPool

func (*JointPool) Clear added in v0.2.0

func (jp *JointPool) Clear() error

Clear is same as Close, and removes all entries in the map.

func (*JointPool) Close added in v0.2.0

func (jp *JointPool) Close() error

Close resets all caches.

func (*JointPool) GetCache added in v0.2.0

func (jp *JointPool) GetCache(key string) (jc *JointCache)

GetCache returns cache from pool for given key path, or creates new one.

func (*JointPool) GetJoint added in v0.2.0

func (jp *JointPool) GetJoint(key string) (j Joint, err error)

GetJoint returns joint for given key.

func (*JointPool) Keys added in v0.2.0

func (jp *JointPool) Keys() []string

Keys returns list of all joints key paths.

func (*JointPool) Open added in v0.2.0

func (jp *JointPool) Open(fullpath string) (f fs.File, err error)

Open opens file with given full path to this file, that can be located inside of nested ISO-images and/or on FTP, SFTP, WebDAV servers. Open implements fs.FS interface, and returns file that can be casted to joint wrapper.

Example

Opens file on joints pool. Path to file can starts with WebDAV/SFTP/FTP service address or at local filesystem, and include ISO9660 disks as chunks of path.

package main

import (
	"io"
	"log"
	"os"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var jp = jnt.NewJointPool() // can be global declaration
	defer jp.Close()

	var f, err = jp.Open("testdata/external.iso/fox.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	io.Copy(os.Stdout, f)
}
Output:

The quick brown fox jumps over the lazy dog.

func (*JointPool) ReadDir added in v0.2.0

func (jp *JointPool) ReadDir(fullpath string) (list []fs.DirEntry, err error)

ReadDir returns directory files fs.DirEntry list pointed by given full path. ReadDir implements ReadDirFS interface.

Example
package main

import (
	"fmt"
	"log"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var jp = jnt.NewJointPool() // can be global declaration
	defer jp.Close()

	var files, err = jp.ReadDir("testdata/external.iso/data")
	if err != nil {
		log.Fatal(err)
	}
	for _, de := range files {
		if de.IsDir() {
			fmt.Printf("dir:  %s\n", de.Name())
		} else {
			var fi, _ = de.Info()
			fmt.Printf("file: %s, %d bytes\n", de.Name(), fi.Size())
		}
	}
}
Output:

dir:  docs
dir:  empty
file: lorem1.txt, 2747 bytes
file: lorem2.txt, 2629 bytes
file: lorem3.txt, 2714 bytes
dir:  доки
file: рыба.txt, 1789 bytes

func (*JointPool) Stat added in v0.2.0

func (jp *JointPool) Stat(fullpath string) (fi fs.FileInfo, err error)

Stat returns fs.FileInfo of file pointed by given full path. Stat implements fs.StatFS interface.

Example
package main

import (
	"fmt"
	"log"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	var jp = jnt.NewJointPool() // can be global declaration
	defer jp.Close()

	var fi, err = jp.Stat("testdata/external.iso/fox.txt")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("name: %s, size: %d\n", fi.Name(), fi.Size())
}
Output:

name: fox.txt, size: 44

func (*JointPool) Sub added in v0.3.0

func (jp *JointPool) Sub(dir string) (fs.FS, error)

Sub returns new file subsystem with given absolute root directory. It's assumed that this call can be used to get access to some WebDAV/SFTP/FTP service. Sub implements fs.SubFS interface, and returns object that can be casted to *SubPool.

Example

Open http://localhost:8080/ in browser to get a list of files in ISO-image.

package main

import (
	"log"
	"net/http"

	jnt "github.com/schwarzlichtbezirk/joint"
)

func main() {
	// create map with caches for all currently unused joints
	var jp = jnt.NewJointPool()
	// file system, that shares content of "testdata" folder
	// and all embedded into ISO-disks files
	var sp, err = jp.Sub("testdata")
	if err != nil {
		log.Fatal(err)
	}
	http.Handle("/", http.FileServer(http.FS(sp)))
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Output:

type JointWrap

type JointWrap struct {
	Joint
	// contains filtered or unexported fields
}

JointWrap helps to return joint into cache after Close-call. It has pointer to JointCache that it binded to.

func (JointWrap) Close

func (jw JointWrap) Close() error

Close calls inherited Close-function and puts joint into binded cache.

func (JointWrap) GetCache added in v0.2.0

func (jw JointWrap) GetCache() *JointCache

GetCache returns binded cache.

type RFile

type RFile interface {
	io.Reader
	io.ReaderAt
	io.Seeker
	fs.File
}

RFile combines fs.File interface and io.Seeker interface.

type SftpFileStat

type SftpFileStat = sftp.FileStat

type SftpJoint

type SftpJoint struct {
	*sftp.File
	// contains filtered or unexported fields
}

SftpJoint create SSH-connection to SFTP-server, login with provided by given URL credentials, and gets a once current directory. Key is address of SFTP-service, i.e. sftp://user:pass@example.com.

func (*SftpJoint) Busy

func (j *SftpJoint) Busy() bool

func (*SftpJoint) Cleanup

func (j *SftpJoint) Cleanup() error

func (*SftpJoint) Close

func (j *SftpJoint) Close() (err error)

func (*SftpJoint) Info

func (j *SftpJoint) Info(fpath string) (fs.FileInfo, error)

func (*SftpJoint) Make

func (j *SftpJoint) Make(base Joint, urladdr string) (err error)

func (*SftpJoint) Open

func (j *SftpJoint) Open(fpath string) (file fs.File, err error)

Opens new connection for any some one file with given full SFTP URL.

func (*SftpJoint) ReadDir

func (j *SftpJoint) ReadDir(n int) (list []fs.DirEntry, err error)

func (*SftpJoint) Size added in v0.3.0

func (j *SftpJoint) Size() (int64, error)

func (*SftpJoint) Stat added in v0.3.0

func (j *SftpJoint) Stat() (fs.FileInfo, error)

type SubPool added in v0.3.0

type SubPool struct {
	*JointPool
	// contains filtered or unexported fields
}

SubPool releases io/fs interfaces in the way that can be used for http-handlers. It has pointer to pool to share same pool for several derived file subsystems. SubPool is developed to pass fstest.TestFS tests, and all its functions receives only valid FS-paths.

func NewSubPool added in v0.3.0

func NewSubPool(jp *JointPool, dir string) *SubPool

NewSubPool creates new SubPool objects with given pool of caches and given absolute root directory.

func (*SubPool) Dir added in v0.3.0

func (sp *SubPool) Dir() string

Dir returns root directory of this file subsystem.

func (*SubPool) Open added in v0.3.0

func (sp *SubPool) Open(fpath string) (f fs.File, err error)

Open implements fs.FS interface, and returns file that can be casted to joint wrapper.

func (*SubPool) ReadDir added in v0.3.0

func (sp *SubPool) ReadDir(fpath string) (ret []fs.DirEntry, err error)

ReadDir implements ReadDirFS interface.

func (*SubPool) Stat added in v0.3.0

func (sp *SubPool) Stat(fpath string) (fi fs.FileInfo, err error)

Stat implements fs.StatFS interface.

func (*SubPool) Sub added in v0.3.0

func (sp *SubPool) Sub(dir string) (fs.FS, error)

Sub returns new file subsystem with given relative root directory. Performs given directory check up. Sub implements fs.SubFS interface, and returns object that can be casted to *SubPool.

type SysJoint

type SysJoint struct {
	*os.File
	// contains filtered or unexported fields
}

func (*SysJoint) Busy

func (j *SysJoint) Busy() bool

func (*SysJoint) Cleanup

func (j *SysJoint) Cleanup() error

func (*SysJoint) Close

func (j *SysJoint) Close() (err error)

func (*SysJoint) Info

func (j *SysJoint) Info(fpath string) (fs.FileInfo, error)

func (*SysJoint) Make

func (j *SysJoint) Make(base Joint, dir string) (err error)

func (*SysJoint) Open

func (j *SysJoint) Open(fpath string) (file fs.File, err error)

Opens file at local file system.

func (*SysJoint) ReadDir added in v0.3.0

func (j *SysJoint) ReadDir(n int) ([]fs.DirEntry, error)

func (*SysJoint) Size added in v0.3.0

func (j *SysJoint) Size() (int64, error)

func (*SysJoint) Stat added in v0.3.0

func (j *SysJoint) Stat() (fs.FileInfo, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL