cfs

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2025 License: MIT Imports: 5 Imported by: 1

README

CompFS: Composite Filesystem for Go

CompFS provides a composite filesystem implementation for Go's io/fs interface. It allows you to combine multiple filesystems into a single one, with cascading priority.

Features

  • Combine multiple filesystems into a single fs.FS implementation
  • Prioritize files from different sources (first match wins)
  • Merge directory listings from all sources
  • Thread-safe for concurrent read operations
  • Compatible with standard library's io/fs interfaces
  • Helper functions for common operations (ReadDir, Sub)
  • Works with both embedded and OS filesystems

Use Cases

  • Template engines with theme/override support
  • http.FileSystem from multiple embedded resources
  • Development overrides for embedded resources
  • Application resources from multiple sources
  • Layered configurations
  • Plugin systems

Installation

go get github.com/goliatone/go-composite-fs

Basic Usage

package main

import (
	"embed"
	"fmt"
	"io"
	"io/fs"
	"os"

	"github.com/goliatone/go-composite-fs"
)

//go:embed base/*
var baseFS embed.FS

//go:embed theme/*
var themeFS embed.FS

func main() {
	// Create a composite filesystem with multiple sources
	// Files will be searched in order (first match wins)
	composite := cfs.NewCompositeFS(
		os.DirFS("./dev"),  // First check local dev directory
		themeFS,            // Then check theme files
		baseFS,             // Finally check base files
	)

	// Open a file (will check each filesystem in order)
	file, err := composite.Open("config.json")
	if err != nil {
		fmt.Println("File not found:", err)
		return
	}
	defer file.Close()

	// Read file content
	content, err := io.ReadAll(file)
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	fmt.Println(string(content))

	// List directory contents (merged from all filesystems)
	entries, err := cfs.ReadDir(composite, "templates")
	if err != nil {
		fmt.Println("Error reading directory:", err)
		return
	}

	fmt.Println("Templates:")
	for _, entry := range entries {
		fmt.Printf("- %s\n", entry.Name())
	}
}

Template Engine Integration

CompFS is perfect for template engines that need to support themes and overrides:

package main

import (
	"embed"
	"io/fs"
	"net/http"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/template/django"
	"github.com/goliatone/go-composite-fs"
)

//go:embed templates/*
var baseTemplates embed.FS

//go:embed theme/templates/*
var themeTemplates embed.FS

func main() {
	// Create a sub-filesystem for each source to normalize paths
	baseFS, _ := fs.Sub(baseTemplates, "templates")
	themeFS, _ := fs.Sub(themeTemplates, "theme/templates")

	// Create composite filesystem with theme overriding base templates
	templateFS := cfs.NewCompositeFS(
		os.DirFS("./dev/templates"), // Development overrides (highest priority)
		themeFS,                     // Theme templates (higher priority)
		baseFS,                      // Base templates (lower priority)
	)

	// Initialize the template engine with the composite filesystem
	viewEngine := django.NewPathForwardingFileSystem(
		http.FS(templateFS),
		"",   // Root directory (already handled by fs.Sub)
		".html",
	)

	// Configure Fiber
	app := fiber.New(fiber.Config{
		Views: viewEngine,
	})

	// Define routes
	app.Get("/", func(c *fiber.Ctx) error {
		return c.Render("index", fiber.Map{
			"Title": "Welcome",
		})
	})

	app.Listen(":3000")
}

API Reference

Types
CompositeFS
type CompositeFS struct {
	// unexported fields
}

CompositeFS implements fs.FS by checking multiple underlying filesystems in order. When a file is requested, it tries each filesystem in the order they were provided until the file is found or all filesystems have been checked.

Functions
NewCompositeFS
func NewCompositeFS(filesystems ...fs.FS) *CompositeFS

NewCompositeFS creates a new CompositeFS with the given filesystems. Filesystems will be checked in the order they are provided.

ReadDir
func ReadDir(fsys fs.FS, name string) ([]fs.DirEntry, error)

ReadDir is a helper function that reads a directory's contents from an fs.FS. It supports both fs.ReadDirFS implementations and regular fs.FS.

Sub
func Sub(fsys fs.FS, dir string) (fs.FS, error)

Sub is a helper function to get a sub-filesystem from an fs.FS.

Methods
Open
func (cfs *CompositeFS) Open(name string) (fs.File, error)

Open implements fs.FS.Open by trying each underlying filesystem in order.

ReadDir
func (cfs *CompositeFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir returns the contents of the named directory, merging entries from all filesystems.

Stat
func (cfs *CompositeFS) Stat(name string) (fs.FileInfo, error)

Stat returns file info for the named file from the first filesystem that successfully opens it.

Sub
func (cfs *CompositeFS) Sub(dir string) (fs.FS, error)

Sub returns a new CompositeFS rooted at dir in each of the underlying filesystems.

ReadFile
func (cfs *CompositeFS) ReadFile(name string) ([]byte, error)

ReadFile reads the named file from the first filesystem that successfully opens it.

Thread Safety

CompFS is thread-safe for concurrent read operations. The implementation contains no mutable state that would be affected by concurrent access.

Error Handling

When a file cannot be found in any of the sources, CompFS returns a detailed error message that includes errors from each filesystem. This helps with diagnosing why the file couldn't be located.

Performance Considerations

  • CompFS short-circuits on the first successful file open, minimizing filesystem checks
  • Directory operations merge results from all filesystems
  • For best performance, put frequently accessed files in the first filesystem

License

MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadDir

func ReadDir(fsys fs.FS, name string) ([]fs.DirEntry, error)

ReadDir is a helper function to read a directory's contents from an fs.FS It supports both fs.ReadDirFS implementations and regular fs.FS

func Sub

func Sub(fsys fs.FS, dir string) (fs.FS, error)

Sub is a helper function to get a sub-filesystem

Types

type CompositeFS

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

CompositeFS implements fs.FS by checking multiple underlying filesystems in order. When a file is requested, it tries each filesystem in the order they were provided until the file is found or all filesystems have been checked.

func NewCompositeFS

func NewCompositeFS(filesystems ...fs.FS) *CompositeFS

NewCompositeFS creates a new CompositeFS with the given filesystems. Filesystems will be checked in the order they are provided.

func (*CompositeFS) Open

func (cfs *CompositeFS) Open(name string) (fs.File, error)

Open implements fs.FS.Open by trying each underlying filesystem in order.

func (*CompositeFS) ReadDir

func (cfs *CompositeFS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir returns the contents of the named directory from the first filesystem that successfully opens it. This implements a custom directory listing capability.

func (*CompositeFS) ReadFile

func (cfs *CompositeFS) ReadFile(name string) ([]byte, error)

ReadFile reads the named file from the first filesystem that successfully opens it

func (*CompositeFS) Stat

func (cfs *CompositeFS) Stat(name string) (fs.FileInfo, error)

Stat returns file info for the named file from the first filesystem that successfully opens it

func (*CompositeFS) Sub

func (cfs *CompositeFS) Sub(dir string) (fs.FS, error)

Sub returns a new CompositeFS rooted at dir in each of the underlying filesystems

Jump to

Keyboard shortcuts

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