Skip to content

Commit

Permalink
filemapping: add support for io/fs (including e.g. go:embed filesyste…
Browse files Browse the repository at this point in the history
…ms) (#16)

* filemapping: add support for io/fs (including e.g. go:embed filesystems)

* fix: *os.File build error on Windows

* tests: document & unit test io/fs

* tests: use go:embed for testing & push minimum Go version to 1.18

* fix: missing imports in windows filemapping

* fix: another missing import
  • Loading branch information
moqmar committed Jul 21, 2023
1 parent 38740d1 commit 9082cdc
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 12 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ requiring a context string respectively.
```go
import "github.com/snapcore/go-gettext"

//go:embed translations
var assets embed.FS

domain := &gettext.TextDomain{
Name: "messages",
LocaleDir: "path/to/translations",
LocaleDir: "./translations",
LocaleFS: assets, // leave away to use the filesystem directly
}
// or use domain.Locale(lang...) to open a different locale's catalog
locale := domain.UserLocale()
Expand Down
4 changes: 2 additions & 2 deletions filemapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package gettext

import (
"fmt"
"io/fs"
"io/ioutil"
"os"
"runtime"
)

Expand All @@ -21,7 +21,7 @@ func (m *fileMapping) Close() error {
return m.closeMapping()
}

func openMapping(f *os.File) (*fileMapping, error) {
func openMapping(f fs.File) (*fileMapping, error) {
fi, err := f.Stat()
if err != nil {
return nil, err
Expand Down
11 changes: 9 additions & 2 deletions filemapping_unix.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
//go:build !windows
// +build !windows

package gettext

import (
"errors"
"io/fs"
"os"
"syscall"
)

func (m *fileMapping) tryMap(f *os.File, size int64) error {
func (m *fileMapping) tryMap(f fs.File, size int64) error {
var err error
m.data, err = syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_PRIVATE)
of, ok := f.(*os.File)
if !ok {
return errors.New("virtual filesystem doesn't support mmap")
}
m.data, err = syscall.Mmap(int(of.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_PRIVATE)
if err != nil {
return err
}
Expand Down
10 changes: 8 additions & 2 deletions filemapping_windows.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package gettext

import (
"errors"
"io/fs"
"os"
"syscall"
"unsafe"
)

// Adapted from https://github.com/golang/exp/blob/master/mmap/mmap_windows.go

func (m *fileMapping) tryMap(f *os.File, size int64) error {
func (m *fileMapping) tryMap(f fs.File, size int64) error {
of, ok := f.(*os.File)
if !ok {
return errors.New("virtual filesystem doesn't support mmap")
}
low, high := uint32(size), uint32(size>>32)
fmap, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, high, low, nil)
fmap, err := syscall.CreateFileMapping(syscall.Handle(of.Fd()), nil, syscall.PAGE_READONLY, high, low, nil)
if err != nil {
return err
}
Expand Down
16 changes: 13 additions & 3 deletions gettext.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package gettext

import (
"fmt"
"io/fs"
"os"
"path"
"sync"
Expand All @@ -17,8 +18,11 @@ type TextDomain struct {
// Name is the name of the text domain
Name string
// LocaleDir is the base directory holding translations of the
// domain. If it is empty, DefaultLocaleDir will be used.
// domain. If it is empty, DefaultLocaleDir will be used if LocaleFS is nil; otherwise the root of LocaleFS will be used.
LocaleDir string
// LocaleFS is the filesystem used to access LocaleDir.
// When nil, the normal OS filesystem will be used.
LocaleFS fs.FS
// PathResolver is called to determine the path of a
// particular locale's translations. If it is nil then
// DefaultResolver will be used, which implements the standard
Expand Down Expand Up @@ -62,7 +66,7 @@ func (t *TextDomain) load(locale string) *mocatalog {
}

localeDir := t.LocaleDir
if localeDir == "" {
if localeDir == "" && t.LocaleFS == nil {
localeDir = DefaultLocaleDir
}
resolver := t.PathResolver
Expand All @@ -71,7 +75,13 @@ func (t *TextDomain) load(locale string) *mocatalog {
}
t.cache[locale] = nil
path := resolver(localeDir, locale, t.Name)
f, err := os.Open(path)
var f fs.File
var err error
if t.LocaleFS != nil {
f, err = t.LocaleFS.Open(path)
} else {
f, err = os.Open(path)
}
if err != nil {
return nil
}
Expand Down
10 changes: 10 additions & 0 deletions gettext_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gettext

import (
"embed"
"fmt"
"io/ioutil"
"os"
Expand Down Expand Up @@ -271,3 +272,12 @@ func TestNotMoFile(t *testing.T) {
)

}

//go:embed testdata
var assets embed.FS

func TestFS(t *testing.T) {
translations := &TextDomain{Name: "messages", LocaleDir: "testdata", LocaleFS: assets, PathResolver: my_resolver}
en := translations.Locale("en")
assert_equal(t, en.Gettext("greeting"), "Hello")
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/snapcore/go-gettext

go 1.13
go 1.18
3 changes: 2 additions & 1 deletion mofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"io/fs"
"os"
"sort"
"strings"
Expand Down Expand Up @@ -223,7 +224,7 @@ func ParseMO(file *os.File) (Catalog, error) {
return Catalog{[]*mocatalog{mo}}, nil
}

func parseMO(file *os.File) (*mocatalog, error) {
func parseMO(file fs.File) (*mocatalog, error) {
m, err := openMapping(file)
if err != nil {
return nil, err
Expand Down

0 comments on commit 9082cdc

Please sign in to comment.