forked from Plemya-x/ALR
		
	feat: add files() function
also add files-find-lang and files-find-doc helpers
This commit is contained in:
		@@ -177,7 +177,35 @@ func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
 | 
			
		||||
	return func(ctx context.Context, opts ...interp.RunnerOption) error {
 | 
			
		||||
		sub := d.Runner.Subshell()
 | 
			
		||||
		for _, opt := range opts {
 | 
			
		||||
			opt(sub)
 | 
			
		||||
			err := opt(sub)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return sub.Run(ctx, fn)
 | 
			
		||||
	}, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PrepareFunc func(context.Context, *interp.Runner) error
 | 
			
		||||
 | 
			
		||||
func (d *Decoder) GetFuncP(name string, prepare PrepareFunc) (ScriptFunc, bool) {
 | 
			
		||||
	fn := d.getFunc(name)
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return func(ctx context.Context, opts ...interp.RunnerOption) error {
 | 
			
		||||
		sub := d.Runner.Subshell()
 | 
			
		||||
		for _, opt := range opts {
 | 
			
		||||
			err := opt(sub)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if prepare != nil {
 | 
			
		||||
			if err := prepare(ctx, sub); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return sub.Run(ctx, fn)
 | 
			
		||||
	}, true
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -55,12 +56,17 @@ var Helpers = handlers.ExecFuncs{
 | 
			
		||||
	"install-completion":   installCompletionCmd,
 | 
			
		||||
	"install-library":      installLibraryCmd,
 | 
			
		||||
	"git-version":          gitVersionCmd,
 | 
			
		||||
 | 
			
		||||
	"files-find-lang": filesFindLangCmd,
 | 
			
		||||
	"files-find-doc":  filesFindDocCmd,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restricted contains restricted read-only helper commands
 | 
			
		||||
// that don't modify any state
 | 
			
		||||
var Restricted = handlers.ExecFuncs{
 | 
			
		||||
	"git-version": gitVersionCmd,
 | 
			
		||||
	"git-version":     gitVersionCmd,
 | 
			
		||||
	"files-find-lang": filesFindLangCmd,
 | 
			
		||||
	"files-find-doc":  filesFindDocCmd,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc {
 | 
			
		||||
@@ -256,6 +262,114 @@ func gitVersionCmd(hc interp.HandlerContext, cmd string, args []string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func filesFindLangCmd(hc interp.HandlerContext, cmd string, args []string) error {
 | 
			
		||||
	namePattern := "*.mo"
 | 
			
		||||
	if len(args) > 0 {
 | 
			
		||||
		namePattern = args[0] + ".mo"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localePath := "./usr/share/locale/"
 | 
			
		||||
	realPath := path.Join(hc.Dir, localePath)
 | 
			
		||||
 | 
			
		||||
	info, err := os.Stat(realPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("files-find-lang: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !info.IsDir() {
 | 
			
		||||
		return fmt.Errorf("files-find-lang: %s is not a directory", localePath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var langFiles []string
 | 
			
		||||
	err = filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
 | 
			
		||||
			relPath, relErr := filepath.Rel(hc.Dir, p)
 | 
			
		||||
			if relErr != nil {
 | 
			
		||||
				return relErr
 | 
			
		||||
			}
 | 
			
		||||
			langFiles = append(langFiles, "./"+relPath)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("files-find-lang: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, file := range langFiles {
 | 
			
		||||
		fmt.Fprintln(hc.Stdout, file)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func filesFindDocCmd(hc interp.HandlerContext, cmd string, args []string) error {
 | 
			
		||||
	namePattern := "*"
 | 
			
		||||
	if len(args) > 0 {
 | 
			
		||||
		namePattern = args[0]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	docPath := "./usr/share/doc/"
 | 
			
		||||
	docRealPath := path.Join(hc.Dir, docPath)
 | 
			
		||||
 | 
			
		||||
	info, err := os.Stat(docRealPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("files-find-doc: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !info.IsDir() {
 | 
			
		||||
		return fmt.Errorf("files-find-doc: %s is not a directory", docPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var docFiles []string
 | 
			
		||||
 | 
			
		||||
	entries, err := os.ReadDir(docRealPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, entry := range entries {
 | 
			
		||||
		if matchNamePattern(entry.Name(), namePattern) {
 | 
			
		||||
			targetPath := filepath.Join(docRealPath, entry.Name())
 | 
			
		||||
			targetInfo, err := os.Stat(targetPath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if targetInfo.IsDir() {
 | 
			
		||||
				err := filepath.Walk(targetPath, func(subPath string, subInfo os.FileInfo, subErr error) error {
 | 
			
		||||
					relPath, err := filepath.Rel(hc.Dir, subPath)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					docFiles = append(docFiles, "./"+relPath)
 | 
			
		||||
					return nil
 | 
			
		||||
				})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("files-find-doc: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, file := range docFiles {
 | 
			
		||||
		fmt.Fprintln(hc.Stdout, file)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func matchNamePattern(name, pattern string) bool {
 | 
			
		||||
	matched, err := filepath.Match(pattern, name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return matched
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func helperInstall(from, to string, perms os.FileMode) error {
 | 
			
		||||
	err := os.MkdirAll(filepath.Dir(to), 0o755)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								internal/shutils/helpers/helpers_internal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								internal/shutils/helpers/helpers_internal_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
// ALR - Any Linux Repository
 | 
			
		||||
// Copyright (C) 2025 Евгений Храмов
 | 
			
		||||
//
 | 
			
		||||
// This program is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
//
 | 
			
		||||
// This program is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
//
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
package helpers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"mvdan.cc/sh/v3/interp"
 | 
			
		||||
	"mvdan.cc/sh/v3/syntax"
 | 
			
		||||
 | 
			
		||||
	"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type testCase struct {
 | 
			
		||||
	name           string
 | 
			
		||||
	dirsToCreate   []string
 | 
			
		||||
	filesToCreate  []string
 | 
			
		||||
	expectedOutput []string
 | 
			
		||||
	args           string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFindFilesDoc(t *testing.T) {
 | 
			
		||||
	tests := []testCase{
 | 
			
		||||
		{
 | 
			
		||||
			name: "All dirs",
 | 
			
		||||
			dirsToCreate: []string{
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/subdir",
 | 
			
		||||
				"usr/share/doc/firefox",
 | 
			
		||||
			},
 | 
			
		||||
			filesToCreate: []string{
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/README.md",
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/subdir/nested-file.txt",
 | 
			
		||||
				"usr/share/doc/firefox/README.md",
 | 
			
		||||
			},
 | 
			
		||||
			expectedOutput: []string{
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/README.md",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/subdir",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt",
 | 
			
		||||
				"./usr/share/doc/firefox",
 | 
			
		||||
				"./usr/share/doc/firefox/README.md",
 | 
			
		||||
			},
 | 
			
		||||
			args: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Only selected dir",
 | 
			
		||||
			dirsToCreate: []string{
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/subdir",
 | 
			
		||||
				"usr/share/doc/firefox",
 | 
			
		||||
				"usr/share/doc/foo/yandex-browser-stable",
 | 
			
		||||
			},
 | 
			
		||||
			filesToCreate: []string{
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/README.md",
 | 
			
		||||
				"usr/share/doc/yandex-browser-stable/subdir/nested-file.txt",
 | 
			
		||||
				"usr/share/doc/firefox/README.md",
 | 
			
		||||
				"usr/share/doc/firefox/yandex-browser-stable",
 | 
			
		||||
				"usr/share/doc/foo/yandex-browser-stable/README.md",
 | 
			
		||||
			},
 | 
			
		||||
			expectedOutput: []string{
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/README.md",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/subdir",
 | 
			
		||||
				"./usr/share/doc/yandex-browser-stable/subdir/nested-file.txt",
 | 
			
		||||
			},
 | 
			
		||||
			args: "yandex-browser-stable",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			tempDir, err := os.MkdirTemp("", "test-files-find-doc")
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
			defer os.RemoveAll(tempDir)
 | 
			
		||||
 | 
			
		||||
			for _, dir := range tc.dirsToCreate {
 | 
			
		||||
				dirPath := filepath.Join(tempDir, dir)
 | 
			
		||||
				err := os.MkdirAll(dirPath, 0o755)
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, file := range tc.filesToCreate {
 | 
			
		||||
				filePath := filepath.Join(tempDir, file)
 | 
			
		||||
				err := os.WriteFile(filePath, []byte("test content"), 0o644)
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			helpers := handlers.ExecFuncs{
 | 
			
		||||
				"files-find-doc": filesFindDocCmd,
 | 
			
		||||
			}
 | 
			
		||||
			buf := &bytes.Buffer{}
 | 
			
		||||
			runner, err := interp.New(
 | 
			
		||||
				interp.Dir(tempDir),
 | 
			
		||||
				interp.StdIO(os.Stdin, buf, os.Stderr),
 | 
			
		||||
				interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))),
 | 
			
		||||
			)
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			scriptContent := `
 | 
			
		||||
shopt -s globstar
 | 
			
		||||
files-find-doc ` + tc.args
 | 
			
		||||
 | 
			
		||||
			script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "")
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			err = runner.Run(context.Background(), script)
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			contents := strings.Fields(strings.TrimSpace(buf.String()))
 | 
			
		||||
			assert.ElementsMatch(t, tc.expectedOutput, contents)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFindLang(t *testing.T) {
 | 
			
		||||
	tests := []testCase{
 | 
			
		||||
		{
 | 
			
		||||
			name: "All dirs",
 | 
			
		||||
			dirsToCreate: []string{
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES",
 | 
			
		||||
				"usr/share/locale/tr/LC_MESSAGES",
 | 
			
		||||
			},
 | 
			
		||||
			filesToCreate: []string{
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo",
 | 
			
		||||
				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
			},
 | 
			
		||||
			expectedOutput: []string{
 | 
			
		||||
				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo",
 | 
			
		||||
				"./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
			},
 | 
			
		||||
			args: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "All dirs",
 | 
			
		||||
			dirsToCreate: []string{
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES",
 | 
			
		||||
				"usr/share/locale/tr/LC_MESSAGES",
 | 
			
		||||
			},
 | 
			
		||||
			filesToCreate: []string{
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
				"usr/share/locale/ru/LC_MESSAGES/yandex-disk-indicator.mo",
 | 
			
		||||
				"usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
			},
 | 
			
		||||
			expectedOutput: []string{
 | 
			
		||||
				"./usr/share/locale/ru/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
				"./usr/share/locale/tr/LC_MESSAGES/yandex-disk.mo",
 | 
			
		||||
			},
 | 
			
		||||
			args: "yandex-disk",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range tests {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			tempDir, err := os.MkdirTemp("", "test-files-find-lang")
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
			defer os.RemoveAll(tempDir)
 | 
			
		||||
 | 
			
		||||
			for _, dir := range tc.dirsToCreate {
 | 
			
		||||
				dirPath := filepath.Join(tempDir, dir)
 | 
			
		||||
				err := os.MkdirAll(dirPath, 0o755)
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, file := range tc.filesToCreate {
 | 
			
		||||
				filePath := filepath.Join(tempDir, file)
 | 
			
		||||
				err := os.WriteFile(filePath, []byte("test content"), 0o644)
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			helpers := handlers.ExecFuncs{
 | 
			
		||||
				"files-find-lang": filesFindLangCmd,
 | 
			
		||||
			}
 | 
			
		||||
			buf := &bytes.Buffer{}
 | 
			
		||||
			runner, err := interp.New(
 | 
			
		||||
				interp.Dir(tempDir),
 | 
			
		||||
				interp.StdIO(os.Stdin, buf, os.Stderr),
 | 
			
		||||
				interp.ExecHandler(helpers.ExecHandler(interp.DefaultExecHandler(1000))),
 | 
			
		||||
			)
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			scriptContent := `
 | 
			
		||||
shopt -s globstar
 | 
			
		||||
files-find-lang ` + tc.args
 | 
			
		||||
 | 
			
		||||
			script, err := syntax.NewParser().Parse(strings.NewReader(scriptContent), "")
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			err = runner.Run(context.Background(), script)
 | 
			
		||||
			assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			contents := strings.Fields(strings.TrimSpace(buf.String()))
 | 
			
		||||
			assert.ElementsMatch(t, tc.expectedOutput, contents)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user