💾 Archived View for capsule.wetterberg.nu › 2021 › 2021-01-29-io-fs.gmi captured on 2022-03-01 at 14:59:29. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-11-30)

-=-=-=-=-=-=-

Embedding & the new io/fs Go package

Once I had embedded all assets and templates in my http proxy "gemini-ignorant" I realised it was a pain in the neck to iterate on the CSS. I'm *very* rusty when it comes to frontend work and require several attempts to even get the basics right. I had an early idea to either add a flag to disable the embedded fs in favour of the current working directory, or create an overlay filesystem of sorts.

gemini-ignorant

I settled on creating an override filesystem that allows you to replace individual files. Mostly because it was a way to kick the wheels of the new io/fs package, disabling the embedded files probably makes more sense, but would require me to write a command for copying out the embedded files to local fs to make it a neat workflow. ...and let's face it, if somebody is interested in using this proxy they'll build from source anyhow, which renders the whole override thing moot except for allowing easy CSS refresh :)

Using the OverrideFS together with embeds.FS and os.DirFS() is fairly straightforward:

assetFS := OverrideFS{
	Base:     assetsEmbed,
	Override: os.DirFS("."),
}

Here's the code for OverrideFS:

package main

import (
	"errors"
	"fmt"
	"io/fs"
)

// OverrideFS is a file system that allows files from the base
// filesystem to be overridden by the override filesystem. Directory
// listings are still read from the base filesystem.
//
// Files that do not exist in the base filesystem will return a
// not exists error even if they exist in the override filesystem.
type OverrideFS struct {
	Base     fs.FS
	Override fs.FS
}

func (of *OverrideFS) Open(name string) (fs.File, error) {
	info, err := fs.Stat(of.Base, name)
	if errors.Is(err, fs.ErrNotExist) {
		return nil, &fs.PathError{
			Op:   "open",
			Path: name,
			Err:  fs.ErrNotExist,
		}
	}
	if err != nil {
		return nil, &fs.PathError{
			Op:   "open",
			Path: name,
			Err: fmt.Errorf(
				"failed to stat base file: %w",
				err,
			),
		}
	}

	if info.IsDir() {
		return of.Base.Open(name)
	}

	f, err := of.Override.Open(name)
	if errors.Is(err, fs.ErrNotExist) {
		return of.Base.Open(name)
	}
	if err != nil {
		return nil, err
	}

	return f, nil
}