diff --git a/go.mod b/go.mod index 96b01ae..4e96287 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 github.com/pkg/errors v0.9.1 - github.com/snivilised/nefilim v0.1.1 + github.com/snivilised/nefilim v0.1.4 golang.org/x/text v0.19.0 ) diff --git a/go.sum b/go.sum index 19c26e8..f30061d 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/snivilised/nefilim v0.1.1 h1:uMhLSfj5prGr2bahwzSM50rpd22fiiv0ZDLSzxYWsgg= github.com/snivilised/nefilim v0.1.1/go.mod h1:+4/hKxgfvE8eNjLMJC+3ropEZSQuiR/NqfPtIuw7ZMw= +github.com/snivilised/nefilim v0.1.4 h1:bhiENDl/T6ZQO146eF8UnxtXLQenSzEyjwuTeWScImw= +github.com/snivilised/nefilim v0.1.4/go.mod h1:+4/hKxgfvE8eNjLMJC+3ropEZSQuiR/NqfPtIuw7ZMw= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= diff --git a/internal/ifs/file-systems.go b/internal/ifs/file-systems.go deleted file mode 100644 index 8d315d6..0000000 --- a/internal/ifs/file-systems.go +++ /dev/null @@ -1,116 +0,0 @@ -package ifs - -import ( - "io/fs" - "os" - - nef "github.com/snivilised/nefilim" -) - -// NewStatFS creates a new fs.StatFS from a path -func NewStatFS(path string) fs.StatFS { - return StatFSFromFS(os.DirFS(path)) -} - -type readDirFS struct { - fsys fs.FS -} - -// NewReadDirFS creates a ReadDirFS file system, which -// contains the ReadDir method that reads entries of a path -// from the native file system. -func NewReadDirFS(path string) fs.ReadDirFS { - return &readDirFS{ - fsys: os.DirFS(path), - } -} - -// Open opens the named file. -func (n *readDirFS) Open(path string) (fs.File, error) { - return n.fsys.Open(path) -} - -// ReadDir reads the named directory -// and returns a list of directory entries sorted by filename. -func (n *readDirFS) ReadDir(name string) ([]fs.DirEntry, error) { - return fs.ReadDir(n.fsys, name) -} - -type queryStatusFS struct { - fsys fs.FS -} - -// StatFSFromFS creates a file system upon which Stat can be invoked -func StatFSFromFS(fS fs.FS) fs.StatFS { - return &queryStatusFS{ - fsys: fS, - } -} - -// Open opens the named file. -func (q *queryStatusFS) Open(name string) (fs.File, error) { - return q.fsys.Open(name) -} - -// Stat returns a FileInfo describing the named file. -// If there is an error, it will be of type *PathError. -func (q *queryStatusFS) Stat(name string) (fs.FileInfo, error) { - return os.Stat(name) -} - -type nativeDirFS struct { - statFS fs.StatFS -} - -// NewNativeDirFS creates an instance of MkDirAllFS from a path -func NewNativeDirFS(path string) nef.MakeDirFS { - return &nativeDirFS{ - statFS: StatFSFromFS(NewReadDirFS(path)), - } -} - -// DirFSFromFS creates a native instance of MkDirAllFS from a fs.FS -func DirFSFromFS(fS fs.FS) nef.MakeDirFS { - return &nativeDirFS{ - statFS: StatFSFromFS(fS), - } -} - -// FileExists return true if item at path exists as a file -func (f *nativeDirFS) FileExists(path string) bool { - info, err := f.statFS.Stat(path) - if err != nil { - return false - } - - if info.IsDir() { - return false - } - - return true -} - -// DirectoryExists return true if item at path exists as a directory -func (f *nativeDirFS) DirectoryExists(path string) bool { - info, err := f.statFS.Stat(path) - if err != nil { - return false - } - - if !info.IsDir() { - return false - } - - return true -} - -// MkdirAll creates a directory named path, -// along with any necessary parents, and returns nil, -// or else returns an error. -func (f *nativeDirFS) MakeDir(path string, perm os.FileMode) error { - return os.Mkdir(path, perm) -} - -func (f *nativeDirFS) MakeDirAll(path string, perm os.FileMode) error { - return os.MkdirAll(path, perm) -} diff --git a/internal/ifs/ifs-defs.go b/internal/ifs/ifs-defs.go deleted file mode 100644 index a6f963b..0000000 --- a/internal/ifs/ifs-defs.go +++ /dev/null @@ -1,3 +0,0 @@ -package ifs - -// 📚 package ifs: internal file system implementations diff --git a/internal/ifs/matchers_test.go b/internal/ifs/matchers_test.go deleted file mode 100644 index 1b5bbb0..0000000 --- a/internal/ifs/matchers_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package ifs_test - -import ( - "fmt" - - "github.com/onsi/gomega/types" - nef "github.com/snivilised/nefilim" -) - -type PathExistsMatcher struct { - vfs interface{} -} - -type AsDirectory string -type AsFile string - -func ExistInFS(fs interface{}) types.GomegaMatcher { - return &PathExistsMatcher{ - vfs: fs, - } -} - -func (m *PathExistsMatcher) Match(actual interface{}) (bool, error) { - vfs, fileSystemOK := m.vfs.(nef.ExistsInFS) - if !fileSystemOK { - return false, fmt.Errorf("❌ matcher expected a ExistsInFS instance (%T)", vfs) - } - - if actualPath, dirOK := actual.(AsDirectory); dirOK { - return vfs.DirectoryExists(string(actualPath)), nil - } - - if actualPath, fileOK := actual.(AsFile); fileOK { - return vfs.FileExists(string(actualPath)), nil - } - - return false, fmt.Errorf("❌ matcher expected an AsDirectory or AsFile instance (%T)", actual) -} - -func (m *PathExistsMatcher) FailureMessage(actual interface{}) string { - return fmt.Sprintf("🔥 Expected\n\t%v\npath to exist", actual) -} - -func (m *PathExistsMatcher) NegatedFailureMessage(actual interface{}) string { - return fmt.Sprintf("🔥 Expected\n\t%v\npath NOT to exist\n", actual) -} diff --git a/internal/ifs/nfs-suite_test.go b/internal/ifs/nfs-suite_test.go deleted file mode 100644 index d78672b..0000000 --- a/internal/ifs/nfs-suite_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package ifs_test - -import ( - "io/fs" - "os" - "path/filepath" - "regexp" - "strings" - "testing" - "testing/fstest" - - . "github.com/onsi/ginkgo/v2" //nolint:revive // ok - . "github.com/onsi/gomega" //nolint:revive // ok -) - -func TestUtils(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Nfs Suite") -} - -func TrimRoot(root string) string { - // omit leading '/', because test-fs stupidly doesn't like it, - // so we have to jump through hoops - if strings.HasPrefix(root, string(filepath.Separator)) { - return root[1:] - } - - pattern := `^[a-zA-Z]:[\\/]*` - re, _ := regexp.Compile(pattern) - - return re.ReplaceAllString(root, "") -} - -const ( - perm = 0o766 -) - -type ( - ensureTE struct { - given string - should string - relative string - expected string - directory bool - } - mkDirAllMapFS struct { - mapFS fstest.MapFS - } -) - -func (f *mkDirAllMapFS) FileExists(path string) bool { - fi, err := f.mapFS.Stat(path) - if err != nil { - return false - } - - if fi.IsDir() { - return false - } - - return true -} - -func (f *mkDirAllMapFS) DirectoryExists(path string) bool { - if strings.HasPrefix(path, string(filepath.Separator)) { - path = path[1:] - } - - fileInfo, err := f.mapFS.Stat(path) - if err != nil { - return false - } - - if !fileInfo.IsDir() { - return false - } - - return true -} - -func (f *mkDirAllMapFS) MkDirAll(path string, perm os.FileMode) error { - var current string - segments := filepath.SplitList(path) - - for _, part := range segments { - if current == "" { - current = part - } else { - current += string(filepath.Separator) + part - } - - if exists := f.DirectoryExists(current); !exists { - f.mapFS[current] = &fstest.MapFile{ - Mode: fs.ModeDir | perm, - } - } - } - - return nil -} - -// AbsFunc signature of function used to obtain the absolute representation of -// a path. -type AbsFunc func(path string) (string, error) - -// Abs function invoker, allows a function to be used in place where -// an instance of an interface would be expected. -func (f AbsFunc) Abs(path string) (string, error) { - return f(path) -} - -// HomeUserFunc signature of function used to obtain the user's home directory. -type HomeUserFunc func() (string, error) - -// Home function invoker, allows a function to be used in place where -// an instance of an interface would be expected. -func (f HomeUserFunc) Home() (string, error) { - return f() -} - -// ResolveMocks, used to override the internal functions used -// to resolve the home path (os.UserHomeDir) and the abs path -// (filepath.Abs). In normal usage, these do not need to be provided, -// just used for testing purposes. -type ResolveMocks struct { - HomeFunc HomeUserFunc - AbsFunc AbsFunc -} diff --git a/internal/translate/localizer-factory.go b/internal/translate/localizer-factory.go index 1937478..df74184 100644 --- a/internal/translate/localizer-factory.go +++ b/internal/translate/localizer-factory.go @@ -13,7 +13,7 @@ import ( ) func createLocalizer(lang *LanguageInfo, sourceID string, - fS nef.MakeDirFS, + fS nef.ReaderFS, ) (*i18n.Localizer, error) { bundle := i18n.NewBundle(lang.Tag) bundle.RegisterUnmarshalFunc("json", json.Unmarshal) @@ -35,8 +35,9 @@ func createLocalizer(lang *LanguageInfo, sourceID string, return i18n.NewLocalizer(bundle, supported...), nil } +// returns an absolute reference to the bundle file func resolveBundlePath(lang *LanguageInfo, txSource TranslationSource, - fS nef.MakeDirFS, + fS nef.ReaderFS, ) string { path := lo.Ternary(txSource.Path != "" && fS.DirectoryExists(txSource.Path), txSource.Path, diff --git a/internal/translate/multiplexor-container.go b/internal/translate/multiplexor-container.go index 0ed740e..d2642f3 100644 --- a/internal/translate/multiplexor-container.go +++ b/internal/translate/multiplexor-container.go @@ -1,8 +1,6 @@ package translate import ( - "io/fs" - "github.com/nicksnyder/go-i18n/v2/i18n" nef "github.com/snivilised/nefilim" ) @@ -20,8 +18,8 @@ func (mx *multiplexor) invoke(localizer *i18n.Localizer, data Localisable) (stri type multiContainer struct { multiplexor localizers localizerContainer - queryFS fs.StatFS - fS nef.MakeDirFS + queryFS nef.ReaderFS + fS nef.ReaderFS create LocalizerCreatorFn } diff --git a/internal/translate/translate-defs.go b/internal/translate/translate-defs.go index e509493..7f540c7 100644 --- a/internal/translate/translate-defs.go +++ b/internal/translate/translate-defs.go @@ -1,8 +1,6 @@ package translate import ( - "io/fs" - "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/pkg/errors" nef "github.com/snivilised/nefilim" @@ -67,7 +65,7 @@ type ( // LocalizerCreatorFn represents the signature of the function that can // optionally be provided to override how an i18n Localizer is created. LocalizerCreatorFn func(li *LanguageInfo, sourceID string, - fS nef.MakeDirFS, + fS nef.ReaderFS, ) (*i18n.Localizer, error) // UseOptionFn functional options function required by Use. @@ -102,7 +100,7 @@ type ( // FS is a file system from where translations are loaded from. This // does not have to performed explicitly asa it will be created using // the From field if not specified. - FS fs.StatFS + FS nef.ReaderFS } // LanguageInfo information pertaining to setting language. Auto detection diff --git a/internal/translate/translator-factories.go b/internal/translate/translator-factories.go index 4e1ecf2..0134cc4 100644 --- a/internal/translate/translator-factories.go +++ b/internal/translate/translator-factories.go @@ -1,10 +1,6 @@ package translate import ( - "io/fs" - - "github.com/snivilised/li18ngo/internal/ifs" - "github.com/snivilised/li18ngo/internal/third/lo" nef "github.com/snivilised/nefilim" ) @@ -37,28 +33,15 @@ type multiTranslatorFactory struct { func (f *multiTranslatorFactory) New(lang *LanguageInfo) (Translator, error) { f.setup(lang) - queryFS := lo.TernaryF(lang.FS != nil, - func() fs.StatFS { - return lang.FS - }, - func() fs.StatFS { - native := ifs.NewReadDirFS(lang.From.Path) - return ifs.StatFSFromFS(native) - }, - ) + dirFS := lang.FS - dirFS := lo.TernaryF(lang.FS != nil, - func() nef.MakeDirFS { - return ifs.DirFSFromFS(lang.FS) - }, - func() nef.MakeDirFS { - return ifs.DirFSFromFS(queryFS) - }, - ) + if dirFS == nil { + dirFS = nef.NewReaderABS() + } multi := &multiContainer{ localizers: make(localizerContainer), - queryFS: queryFS, + queryFS: dirFS, fS: dirFS, create: f.Create, } diff --git a/li18ngo-api.go b/li18ngo-api.go index 59482e6..3765a83 100644 --- a/li18ngo-api.go +++ b/li18ngo-api.go @@ -15,9 +15,9 @@ var ( // has accidentally not called Use before working with li18ngo. ErrSafePanicWarning = translate.ErrSafePanicWarning - // Li18ngoSourceID the id that represents this module. If client want + // Li18ngoSourceID the id that represents this module. If a client want // to provides translations for languages that li18ngo does not, then - // the localizer the create created for this purpose should use this + // the localizer the 'create' created for this purpose should use this // SourceID. So whenever the Text function is used on templates defined // inside this module, the translation process is directed to use the // correct i18n.Localizer (identified by the SourceID). The Source is diff --git a/nfs/nfs-defs.go b/nfs/nfs-defs.go deleted file mode 100644 index 714b4cc..0000000 --- a/nfs/nfs-defs.go +++ /dev/null @@ -1,3 +0,0 @@ -package nfs - -// 📚 package nfs: file system definitions diff --git a/text_test.go b/text_test.go index bd45e46..a00d5e4 100644 --- a/text_test.go +++ b/text_test.go @@ -8,13 +8,17 @@ import ( . "github.com/onsi/ginkgo/v2" //nolint:revive // ginkgo ok . "github.com/onsi/gomega" //nolint:revive // gomega ok "github.com/snivilised/li18ngo" - "github.com/snivilised/li18ngo/internal/ifs" "github.com/snivilised/li18ngo/internal/lab" "github.com/snivilised/li18ngo/internal/translate" "github.com/snivilised/li18ngo/locale" + nef "github.com/snivilised/nefilim" "golang.org/x/text/language" ) +const ( + relative = "test/data/l10n" +) + var _ = Describe("Text", Ordered, func() { var ( repo string @@ -24,14 +28,19 @@ var _ = Describe("Text", Ordered, func() { BeforeAll(func() { repo = lab.Repo("") - l10nPath = lab.Path(repo, "test/data/l10n") - queryFS := ifs.NewNativeDirFS(l10nPath) - Expect(queryFS.DirectoryExists(l10nPath)).To(BeTrue(), - fmt.Sprintf("l10n '%v' path does not exist", l10nPath), + l10nPath = lab.Path(repo, relative) + queryFS := nef.NewMakeDirFS(nef.Rel{ + Root: repo, + }) + + Expect(queryFS.DirectoryExists(relative)).To(BeTrue(), + fmt.Sprintf("l10n '%v' path does not exist", relative), ) testTranslationFile = li18ngo.TranslationFiles{ - li18ngo.Li18ngoSourceID: li18ngo.TranslationSource{Name: "test"}, + li18ngo.Li18ngoSourceID: li18ngo.TranslationSource{ + Name: "test", + }, } }) @@ -79,6 +88,7 @@ var _ = Describe("Text", Ordered, func() { o.Tag = language.AmericanEnglish o.From.Path = l10nPath o.From.Sources = testTranslationFile + o.DefaultIsAcceptable = false }); err != nil { Fail(err.Error()) } @@ -114,6 +124,7 @@ var _ = Describe("Text", Ordered, func() { locale.TestGrafficoSourceID: li18ngo.TranslationSource{Name: "test.graffico"}, }, } + o.DefaultIsAcceptable = false }); err != nil { Fail(err.Error()) }