1213 lines
34 KiB
Go
1213 lines
34 KiB
Go
/*
|
|
|--------------------------------------------------------------------------
|
|
| Description
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Name:
|
|
| - Filesystem
|
|
|
|
|
| Purpose:
|
|
| - Provide a reusable, OS-agnostic utility to deterministically perform
|
|
| filesystem operations through a unified execution interface supporting
|
|
| listing, validation, creation, deletion, and permission management with
|
|
| consistent behavior and structured results.
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Instruction
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Guideline:
|
|
| - Use ObrimFilesystem() as the single public entry point.
|
|
| - Execute one filesystem operation per invocation.
|
|
| - Validate the operation type and type-specific configuration before
|
|
| processing.
|
|
| - Resolve filesystem paths using the supported user or custom strategy.
|
|
| - Use list for filesystem traversal, filtering, sorting, and discovery.
|
|
| - Use check for filesystem existence, entity type, and accessibility
|
|
| validation.
|
|
| - Use create for creating exactly one file or directory.
|
|
| - Use delete for deleting exactly one file or directory, with optional
|
|
| recursive directory deletion.
|
|
| - Use permission for retrieving or updating filesystem permissions.
|
|
| - Return standardized structured results for both successful and failed
|
|
| executions.
|
|
|
|
|
| Example:
|
|
| - ObrimFilesystem("list", map[string]any{
|
|
| "entity": "file",
|
|
| "strategy": "user",
|
|
| "base": "",
|
|
| "name": "Documents",
|
|
| "recursive": true,
|
|
| "include_hidden": false,
|
|
| "sort_by": "name",
|
|
| "sort_order": "asc",
|
|
| "filter_pattern": "*.txt",
|
|
| "follow_symlink": false,
|
|
| "absolute_path": true,
|
|
| })
|
|
|
|
|
| - ObrimFilesystem("check", map[string]any{
|
|
| "entity": "file",
|
|
| "strategy": "custom",
|
|
| "base": "/tmp",
|
|
| "name": "example.txt",
|
|
| "readable": true,
|
|
| "writable": true,
|
|
| })
|
|
|
|
|
| - ObrimFilesystem("create", map[string]any{
|
|
| "entity": "file",
|
|
| "strategy": "custom",
|
|
| "base": "/tmp",
|
|
| "name": "example.txt",
|
|
| "hidden": false,
|
|
| })
|
|
|
|
|
| - ObrimFilesystem("delete", map[string]any{
|
|
| "entity": "directory",
|
|
| "strategy": "custom",
|
|
| "base": "/tmp",
|
|
| "name": "workspace",
|
|
| "recursive": true,
|
|
| })
|
|
|
|
|
| - ObrimFilesystem("permission", map[string]any{
|
|
| "strategy": "custom",
|
|
| "base": "/tmp",
|
|
| "name": "example.txt",
|
|
| "mode": "write",
|
|
| "permission": "0644",
|
|
| })
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Credit
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Contributor:
|
|
| - Rajon Ahmed
|
|
| - Blockonite
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
package filesystem
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Defines filesystem operation types.
|
|
const (
|
|
obrimFilesystemTypeList = "list"
|
|
obrimFilesystemTypeCheck = "check"
|
|
obrimFilesystemTypeCreate = "create"
|
|
obrimFilesystemTypeDelete = "delete"
|
|
obrimFilesystemTypePermission = "permission"
|
|
)
|
|
|
|
// Defines supported filesystem entity types.
|
|
const (
|
|
obrimFilesystemEntityFile = "file"
|
|
obrimFilesystemEntityDirectory = "directory"
|
|
)
|
|
|
|
// Defines supported filesystem path resolution strategies.
|
|
const (
|
|
obrimFilesystemStrategyUser = "user"
|
|
obrimFilesystemStrategyCustom = "custom"
|
|
)
|
|
|
|
// Defines supported permission operation modes.
|
|
const (
|
|
obrimFilesystemModeRead = "read"
|
|
obrimFilesystemModeWrite = "write"
|
|
)
|
|
|
|
// Defines supported listing sort criteria.
|
|
const (
|
|
obrimFilesystemSortName = "name"
|
|
obrimFilesystemSortSize = "size"
|
|
obrimFilesystemSortModified = "modified"
|
|
obrimFilesystemSortCreated = "created"
|
|
)
|
|
|
|
// Defines supported listing sort orders.
|
|
const (
|
|
obrimFilesystemSortOrderAsc = "asc"
|
|
obrimFilesystemSortOrderDesc = "desc"
|
|
)
|
|
|
|
// Defines filesystem execution result codes.
|
|
const (
|
|
obrimFilesystemSuccessList = "SUCCESS_LIST"
|
|
obrimFilesystemSuccessCheck = "SUCCESS_CHECK"
|
|
obrimFilesystemSuccessCreate = "SUCCESS_CREATE"
|
|
obrimFilesystemSuccessDelete = "SUCCESS_DELETE"
|
|
obrimFilesystemSuccessPermission = "SUCCESS_PERMISSION"
|
|
|
|
obrimFilesystemFailureInvalidType = "FAILURE_INVALID_TYPE"
|
|
obrimFilesystemFailureInvalidConfig = "FAILURE_INVALID_CONFIG"
|
|
obrimFilesystemFailureInvalidEntity = "FAILURE_INVALID_ENTITY"
|
|
obrimFilesystemFailureInvalidStrategy = "FAILURE_INVALID_STRATEGY"
|
|
obrimFilesystemFailureInvalidSortBy = "FAILURE_INVALID_SORT_BY"
|
|
obrimFilesystemFailureInvalidSortOrder = "FAILURE_INVALID_SORT_ORDER"
|
|
obrimFilesystemFailureInvalidMode = "FAILURE_INVALID_MODE"
|
|
obrimFilesystemFailureInvalidPermission = "FAILURE_INVALID_PERMISSION"
|
|
obrimFilesystemFailureResolvePath = "FAILURE_RESOLVE_PATH"
|
|
obrimFilesystemFailureList = "FAILURE_LIST"
|
|
obrimFilesystemFailureCheck = "FAILURE_CHECK"
|
|
obrimFilesystemFailureCreate = "FAILURE_CREATE"
|
|
obrimFilesystemFailureDelete = "FAILURE_DELETE"
|
|
obrimFilesystemFailurePermission = "FAILURE_PERMISSION"
|
|
obrimFilesystemFailureEntityNotFound = "FAILURE_ENTITY_NOT_FOUND"
|
|
obrimFilesystemFailureEntityTypeMismatch = "FAILURE_ENTITY_TYPE_MISMATCH"
|
|
obrimFilesystemFailureReadAccess = "FAILURE_READ_ACCESS"
|
|
obrimFilesystemFailureWriteAccess = "FAILURE_WRITE_ACCESS"
|
|
)
|
|
|
|
// Defines a filesystem listing entry.
|
|
type obrimFilesystemEntry struct {
|
|
Path string `json:"path"`
|
|
Name string `json:"name"`
|
|
Entity string `json:"entity"`
|
|
Size int64 `json:"size"`
|
|
Modified string `json:"modified"`
|
|
Created string `json:"created"`
|
|
Hidden bool `json:"hidden"`
|
|
Read bool `json:"readable"`
|
|
Write bool `json:"writable"`
|
|
}
|
|
|
|
// Defines the filesystem utility result.
|
|
type obrimFilesystemResult struct {
|
|
Status bool `json:"status"`
|
|
Code string `json:"code"`
|
|
Payload map[string]any `json:"payload"`
|
|
}
|
|
|
|
// Defines listing configuration.
|
|
type obrimFilesystemListConfig struct {
|
|
Entity string
|
|
Strategy string
|
|
Base string
|
|
Name string
|
|
Recursive bool
|
|
IncludeHidden bool
|
|
SortBy string
|
|
SortOrder string
|
|
FilterPattern string
|
|
FollowSymlink bool
|
|
AbsolutePath bool
|
|
}
|
|
|
|
// Defines check configuration.
|
|
type obrimFilesystemCheckConfig struct {
|
|
Entity string
|
|
Strategy string
|
|
Base string
|
|
Name string
|
|
Readable bool
|
|
Writable bool
|
|
}
|
|
|
|
// Defines create configuration.
|
|
type obrimFilesystemCreateConfig struct {
|
|
Entity string
|
|
Strategy string
|
|
Base string
|
|
Name string
|
|
Hidden bool
|
|
}
|
|
|
|
// Defines delete configuration.
|
|
type obrimFilesystemDeleteConfig struct {
|
|
Entity string
|
|
Strategy string
|
|
Base string
|
|
Name string
|
|
Recursive bool
|
|
}
|
|
|
|
// Defines permission configuration.
|
|
type obrimFilesystemPermissionConfig struct {
|
|
Strategy string
|
|
Base string
|
|
Name string
|
|
Mode string
|
|
Permission string
|
|
}
|
|
|
|
// Executes a filesystem operation and returns a structured result.
|
|
func ObrimFilesystem(operationType string, config map[string]any) map[string]any {
|
|
if err := obrimFilesystemValidateInput(operationType, config); err != nil {
|
|
return obrimFilesystemBuildOutput(false, err.code, nil)
|
|
}
|
|
|
|
payload, code, err := obrimFilesystemRouteRequest(operationType, config)
|
|
if err != nil {
|
|
return obrimFilesystemBuildOutput(false, code, nil)
|
|
}
|
|
|
|
return obrimFilesystemBuildOutput(true, code, payload)
|
|
}
|
|
|
|
// Defines a filesystem validation error.
|
|
type obrimFilesystemValidationError struct {
|
|
code string
|
|
}
|
|
|
|
// Validates the requested operation and its configuration.
|
|
func obrimFilesystemValidateInput(operationType string, config map[string]any) *obrimFilesystemValidationError {
|
|
if config == nil {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
switch operationType {
|
|
case obrimFilesystemTypeList:
|
|
return obrimFilesystemValidateListConfig(config)
|
|
case obrimFilesystemTypeCheck:
|
|
return obrimFilesystemValidateCheckConfig(config)
|
|
case obrimFilesystemTypeCreate:
|
|
return obrimFilesystemValidateCreateConfig(config)
|
|
case obrimFilesystemTypeDelete:
|
|
return obrimFilesystemValidateDeleteConfig(config)
|
|
case obrimFilesystemTypePermission:
|
|
return obrimFilesystemValidatePermissionConfig(config)
|
|
default:
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidType}
|
|
}
|
|
}
|
|
|
|
// Routes a validated filesystem request to its operation-specific function.
|
|
func obrimFilesystemRouteRequest(operationType string, config map[string]any) (map[string]any, string, error) {
|
|
switch operationType {
|
|
case obrimFilesystemTypeList:
|
|
payload, err := obrimFilesystemList(config)
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailureList, err
|
|
}
|
|
return payload, obrimFilesystemSuccessList, nil
|
|
|
|
case obrimFilesystemTypeCheck:
|
|
payload, code, err := obrimFilesystemCheck(config)
|
|
if err != nil {
|
|
return nil, code, err
|
|
}
|
|
return payload, obrimFilesystemSuccessCheck, nil
|
|
|
|
case obrimFilesystemTypeCreate:
|
|
payload, err := obrimFilesystemCreate(config)
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailureCreate, err
|
|
}
|
|
return payload, obrimFilesystemSuccessCreate, nil
|
|
|
|
case obrimFilesystemTypeDelete:
|
|
payload, err := obrimFilesystemDelete(config)
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailureDelete, err
|
|
}
|
|
return payload, obrimFilesystemSuccessDelete, nil
|
|
|
|
case obrimFilesystemTypePermission:
|
|
payload, err := obrimFilesystemPermission(config)
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailurePermission, err
|
|
}
|
|
return payload, obrimFilesystemSuccessPermission, nil
|
|
|
|
default:
|
|
return nil, obrimFilesystemFailureInvalidType, fmt.Errorf("unsupported filesystem operation")
|
|
}
|
|
}
|
|
|
|
// Builds the standardized filesystem output.
|
|
func obrimFilesystemBuildOutput(status bool, code string, payload map[string]any) map[string]any {
|
|
return map[string]any{
|
|
"status": status,
|
|
"code": code,
|
|
"payload": payload,
|
|
}
|
|
}
|
|
|
|
// Validates list configuration.
|
|
func obrimFilesystemValidateListConfig(config map[string]any) *obrimFilesystemValidationError {
|
|
entity := obrimFilesystemString(config, "entity")
|
|
strategy := obrimFilesystemString(config, "strategy")
|
|
sortBy := obrimFilesystemString(config, "sort_by")
|
|
sortOrder := obrimFilesystemString(config, "sort_order")
|
|
|
|
if entity != "" && entity != obrimFilesystemEntityFile && entity != obrimFilesystemEntityDirectory {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidEntity}
|
|
}
|
|
|
|
if strategy != "" && strategy != obrimFilesystemStrategyUser && strategy != obrimFilesystemStrategyCustom {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidStrategy}
|
|
}
|
|
|
|
if sortBy != "" {
|
|
switch sortBy {
|
|
case obrimFilesystemSortName, obrimFilesystemSortSize, obrimFilesystemSortModified, obrimFilesystemSortCreated:
|
|
default:
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidSortBy}
|
|
}
|
|
}
|
|
|
|
if sortOrder != "" && sortOrder != "asc" && sortOrder != "desc" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidSortOrder}
|
|
}
|
|
|
|
if strategy == obrimFilesystemStrategyCustom && obrimFilesystemString(config, "base") == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validates check configuration.
|
|
func obrimFilesystemValidateCheckConfig(config map[string]any) *obrimFilesystemValidationError {
|
|
entity := obrimFilesystemString(config, "entity")
|
|
strategy := obrimFilesystemString(config, "strategy")
|
|
name := obrimFilesystemString(config, "name")
|
|
|
|
if entity != obrimFilesystemEntityFile && entity != obrimFilesystemEntityDirectory {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidEntity}
|
|
}
|
|
|
|
if strategy != "" && strategy != obrimFilesystemStrategyUser && strategy != obrimFilesystemStrategyCustom {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidStrategy}
|
|
}
|
|
|
|
if name == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
if strategy == obrimFilesystemStrategyCustom && obrimFilesystemString(config, "base") == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validates create configuration.
|
|
func obrimFilesystemValidateCreateConfig(config map[string]any) *obrimFilesystemValidationError {
|
|
entity := obrimFilesystemString(config, "entity")
|
|
strategy := obrimFilesystemString(config, "strategy")
|
|
name := obrimFilesystemString(config, "name")
|
|
|
|
if entity != obrimFilesystemEntityFile && entity != obrimFilesystemEntityDirectory {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidEntity}
|
|
}
|
|
|
|
if strategy != "" && strategy != obrimFilesystemStrategyUser && strategy != obrimFilesystemStrategyCustom {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidStrategy}
|
|
}
|
|
|
|
if name == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
if strategy == obrimFilesystemStrategyCustom && obrimFilesystemString(config, "base") == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validates delete configuration.
|
|
func obrimFilesystemValidateDeleteConfig(config map[string]any) *obrimFilesystemValidationError {
|
|
entity := obrimFilesystemString(config, "entity")
|
|
strategy := obrimFilesystemString(config, "strategy")
|
|
name := obrimFilesystemString(config, "name")
|
|
|
|
if entity != obrimFilesystemEntityFile && entity != obrimFilesystemEntityDirectory {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidEntity}
|
|
}
|
|
|
|
if strategy != "" && strategy != obrimFilesystemStrategyUser && strategy != obrimFilesystemStrategyCustom {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidStrategy}
|
|
}
|
|
|
|
if name == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
if strategy == obrimFilesystemStrategyCustom && obrimFilesystemString(config, "base") == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validates permission configuration.
|
|
func obrimFilesystemValidatePermissionConfig(config map[string]any) *obrimFilesystemValidationError {
|
|
strategy := obrimFilesystemString(config, "strategy")
|
|
name := obrimFilesystemString(config, "name")
|
|
mode := obrimFilesystemString(config, "mode")
|
|
|
|
if strategy != "" && strategy != obrimFilesystemStrategyUser && strategy != obrimFilesystemStrategyCustom {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidStrategy}
|
|
}
|
|
|
|
if name == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
if mode != obrimFilesystemModeRead && mode != obrimFilesystemModeWrite {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidMode}
|
|
}
|
|
|
|
if mode == obrimFilesystemModeWrite {
|
|
permission := obrimFilesystemString(config, "permission")
|
|
if _, err := strconv.ParseUint(permission, 8, 32); err != nil || len(permission) < 3 || len(permission) > 4 {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidPermission}
|
|
}
|
|
}
|
|
|
|
if strategy == obrimFilesystemStrategyCustom && obrimFilesystemString(config, "base") == "" {
|
|
return &obrimFilesystemValidationError{code: obrimFilesystemFailureInvalidConfig}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Executes a filesystem listing operation.
|
|
func obrimFilesystemList(config map[string]any) (map[string]any, error) {
|
|
options := obrimFilesystemListConfig{
|
|
Entity: obrimFilesystemString(config, "entity"),
|
|
Strategy: obrimFilesystemStringDefault(config, "strategy", obrimFilesystemStrategyUser),
|
|
Base: obrimFilesystemString(config, "base"),
|
|
Name: obrimFilesystemString(config, "name"),
|
|
Recursive: obrimFilesystemBool(config, "recursive"),
|
|
IncludeHidden: obrimFilesystemBool(config, "include_hidden"),
|
|
SortBy: obrimFilesystemStringDefault(config, "sort_by", obrimFilesystemSortName),
|
|
SortOrder: obrimFilesystemStringDefault(config, "sort_order", "asc"),
|
|
FilterPattern: obrimFilesystemString(config, "filter_pattern"),
|
|
FollowSymlink: obrimFilesystemBool(config, "follow_symlink"),
|
|
AbsolutePath: obrimFilesystemBool(config, "absolute_path"),
|
|
}
|
|
|
|
basePath, err := obrimFilesystemResolvePath(options.Strategy, options.Base, options.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entries, err := obrimFilesystemListTraverse(basePath, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entries = obrimFilesystemListFilter(entries, options)
|
|
obrimFilesystemListSort(entries, options.SortBy, options.SortOrder)
|
|
|
|
return map[string]any{
|
|
"entries": entries,
|
|
"total": len(entries),
|
|
"recursive": options.Recursive,
|
|
"base_path": basePath,
|
|
}, nil
|
|
}
|
|
|
|
// Traverses the filesystem according to listing options.
|
|
func obrimFilesystemListTraverse(basePath string, options obrimFilesystemListConfig) ([]map[string]any, error) {
|
|
info, err := os.Lstat(basePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
return nil, fmt.Errorf("listing root is not a directory")
|
|
}
|
|
|
|
entries := make([]map[string]any, 0)
|
|
|
|
if !options.Recursive {
|
|
items, err := os.ReadDir(basePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, item := range items {
|
|
entry, include, err := obrimFilesystemListEntry(item, basePath, options)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if include {
|
|
entries = append(entries, entry)
|
|
}
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
err = filepath.Walk(basePath, func(path string, info os.FileInfo, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
|
|
if path == basePath {
|
|
return nil
|
|
}
|
|
|
|
if info.Mode()&os.ModeSymlink != 0 && options.FollowSymlink {
|
|
targetInfo, targetErr := os.Stat(path)
|
|
if targetErr != nil {
|
|
return targetErr
|
|
}
|
|
info = targetInfo
|
|
}
|
|
|
|
entry := obrimFilesystemEntryFromInfo(path, info, options.AbsolutePath)
|
|
|
|
if entry["hidden"] == true && !options.IncludeHidden {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if options.Entity != "" && entry["entity"] != options.Entity {
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if options.FilterPattern != "" {
|
|
match, matchErr := filepath.Match(options.FilterPattern, info.Name())
|
|
if matchErr != nil {
|
|
return matchErr
|
|
}
|
|
if !match {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
entries = append(entries, entry)
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
// Filters listing results according to entity, hidden, and pattern rules.
|
|
func obrimFilesystemListFilter(entries []map[string]any, options obrimFilesystemListConfig) []map[string]any {
|
|
filtered := make([]map[string]any, 0, len(entries))
|
|
|
|
for _, entry := range entries {
|
|
if !options.IncludeHidden && obrimFilesystemMapBool(entry, "hidden") {
|
|
continue
|
|
}
|
|
|
|
if options.Entity != "" && obrimFilesystemMapString(entry, "entity") != options.Entity {
|
|
continue
|
|
}
|
|
|
|
if options.FilterPattern != "" {
|
|
matched, err := filepath.Match(options.FilterPattern, obrimFilesystemMapString(entry, "name"))
|
|
if err != nil || !matched {
|
|
continue
|
|
}
|
|
}
|
|
|
|
filtered = append(filtered, entry)
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
// Sorts listing results using the requested criteria and direction.
|
|
func obrimFilesystemListSort(entries []map[string]any, sortBy string, sortOrder string) {
|
|
sort.SliceStable(entries, func(i, j int) bool {
|
|
var less bool
|
|
|
|
switch sortBy {
|
|
case obrimFilesystemSortSize:
|
|
less = obrimFilesystemMapInt64(entries[i], "size") < obrimFilesystemMapInt64(entries[j], "size")
|
|
case obrimFilesystemSortModified:
|
|
less = obrimFilesystemMapString(entries[i], "modified") < obrimFilesystemMapString(entries[j], "modified")
|
|
case obrimFilesystemSortCreated:
|
|
less = obrimFilesystemMapString(entries[i], "created") < obrimFilesystemMapString(entries[j], "created")
|
|
default:
|
|
less = strings.ToLower(obrimFilesystemMapString(entries[i], "name")) <
|
|
strings.ToLower(obrimFilesystemMapString(entries[j], "name"))
|
|
}
|
|
|
|
if sortOrder == "desc" {
|
|
return !less && !obrimFilesystemListSortEqual(entries[i], entries[j], sortBy)
|
|
}
|
|
|
|
return less
|
|
})
|
|
}
|
|
|
|
// Determines whether two listing entries have equal sort values.
|
|
func obrimFilesystemListSortEqual(left map[string]any, right map[string]any, sortBy string) bool {
|
|
switch sortBy {
|
|
case obrimFilesystemSortSize:
|
|
return obrimFilesystemMapInt64(left, "size") == obrimFilesystemMapInt64(right, "size")
|
|
case obrimFilesystemSortModified:
|
|
return obrimFilesystemMapString(left, "modified") == obrimFilesystemMapString(right, "modified")
|
|
case obrimFilesystemSortCreated:
|
|
return obrimFilesystemMapString(left, "created") == obrimFilesystemMapString(right, "created")
|
|
default:
|
|
return strings.EqualFold(
|
|
obrimFilesystemMapString(left, "name"),
|
|
obrimFilesystemMapString(right, "name"),
|
|
)
|
|
}
|
|
}
|
|
|
|
// Builds a listing entry from a directory entry.
|
|
func obrimFilesystemListEntry(item os.DirEntry, basePath string, options obrimFilesystemListConfig) (map[string]any, bool, error) {
|
|
info, err := item.Info()
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
if item.Type()&os.ModeSymlink != 0 && options.FollowSymlink {
|
|
info, err = os.Stat(filepath.Join(basePath, item.Name()))
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
}
|
|
|
|
entry := obrimFilesystemEntryFromInfo(filepath.Join(basePath, item.Name()), info, options.AbsolutePath)
|
|
|
|
if !options.IncludeHidden && obrimFilesystemMapBool(entry, "hidden") {
|
|
return entry, false, nil
|
|
}
|
|
|
|
if options.Entity != "" && obrimFilesystemMapString(entry, "entity") != options.Entity {
|
|
return entry, false, nil
|
|
}
|
|
|
|
if options.FilterPattern != "" {
|
|
matched, err := filepath.Match(options.FilterPattern, item.Name())
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if !matched {
|
|
return entry, false, nil
|
|
}
|
|
}
|
|
|
|
return entry, true, nil
|
|
}
|
|
|
|
// Converts filesystem metadata into a standardized listing entry.
|
|
func obrimFilesystemEntryFromInfo(path string, info os.FileInfo, absolute bool) map[string]any {
|
|
resolvedPath := path
|
|
|
|
if absolute {
|
|
if absolutePath, err := filepath.Abs(path); err == nil {
|
|
resolvedPath = absolutePath
|
|
}
|
|
} else {
|
|
if cleanPath, err := filepath.Rel(".", path); err == nil {
|
|
resolvedPath = cleanPath
|
|
}
|
|
}
|
|
|
|
entity := obrimFilesystemEntityFile
|
|
if info.IsDir() {
|
|
entity = obrimFilesystemEntityDirectory
|
|
}
|
|
|
|
readable := obrimFilesystemCanRead(path)
|
|
writable := obrimFilesystemCanWrite(path)
|
|
|
|
return map[string]any{
|
|
"path": resolvedPath,
|
|
"name": info.Name(),
|
|
"entity": entity,
|
|
"size": info.Size(),
|
|
"modified": info.ModTime().UTC().Format("2006-01-02T15:04:05Z07:00"),
|
|
"created": info.ModTime().UTC().Format("2006-01-02T15:04:05Z07:00"),
|
|
"hidden": obrimFilesystemIsHidden(info.Name()),
|
|
"readable": readable,
|
|
"writable": writable,
|
|
}
|
|
}
|
|
|
|
// Executes a filesystem existence and accessibility check.
|
|
func obrimFilesystemCheck(config map[string]any) (map[string]any, string, error) {
|
|
options := obrimFilesystemCheckConfig{
|
|
Entity: obrimFilesystemString(config, "entity"),
|
|
Strategy: obrimFilesystemStringDefault(config, "strategy", obrimFilesystemStrategyUser),
|
|
Base: obrimFilesystemString(config, "base"),
|
|
Name: obrimFilesystemString(config, "name"),
|
|
Readable: obrimFilesystemBool(config, "readable"),
|
|
Writable: obrimFilesystemBool(config, "writable"),
|
|
}
|
|
|
|
path, err := obrimFilesystemResolvePath(options.Strategy, options.Base, options.Name)
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailureResolvePath, err
|
|
}
|
|
|
|
info, err := os.Lstat(path)
|
|
if os.IsNotExist(err) {
|
|
return nil, obrimFilesystemFailureEntityNotFound, err
|
|
}
|
|
if err != nil {
|
|
return nil, obrimFilesystemFailureCheck, err
|
|
}
|
|
|
|
entity := obrimFilesystemEntityFile
|
|
if info.IsDir() {
|
|
entity = obrimFilesystemEntityDirectory
|
|
}
|
|
|
|
if entity != options.Entity {
|
|
return nil, obrimFilesystemFailureEntityTypeMismatch, fmt.Errorf("filesystem entity type mismatch")
|
|
}
|
|
|
|
readable := obrimFilesystemCheckRead(path)
|
|
writable := obrimFilesystemCheckWrite(path)
|
|
|
|
if options.Readable && !readable {
|
|
return nil, obrimFilesystemFailureReadAccess, fmt.Errorf("filesystem entity is not readable")
|
|
}
|
|
|
|
if options.Writable && !writable {
|
|
return nil, obrimFilesystemFailureWriteAccess, fmt.Errorf("filesystem entity is not writable")
|
|
}
|
|
|
|
return map[string]any{
|
|
"path": path,
|
|
"exists": true,
|
|
"entity": entity,
|
|
"readable": readable,
|
|
"writable": writable,
|
|
}, obrimFilesystemSuccessCheck, nil
|
|
}
|
|
|
|
// Checks read permission without modifying the filesystem.
|
|
func obrimFilesystemCheckRead(path string) bool {
|
|
return obrimFilesystemCheckReadPath(path)
|
|
}
|
|
|
|
// Checks write permission without modifying the filesystem.
|
|
func obrimFilesystemCheckWrite(path string) bool {
|
|
return obrimFilesystemCheckWritePath(path)
|
|
}
|
|
|
|
// Checks whether a filesystem path can be read.
|
|
func obrimFilesystemCheckReadPath(path string) bool {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
err = file.Close()
|
|
return err == nil
|
|
}
|
|
|
|
// Checks whether a filesystem path can be written.
|
|
func obrimFilesystemCheckWritePath(path string) bool {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if info.Mode().Perm()&0200 != 0 {
|
|
return true
|
|
}
|
|
|
|
parent := filepath.Dir(path)
|
|
parentInfo, err := os.Stat(parent)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return parentInfo.Mode().Perm()&0200 != 0
|
|
}
|
|
|
|
// Executes a filesystem creation operation.
|
|
func obrimFilesystemCreate(config map[string]any) (map[string]any, error) {
|
|
options := obrimFilesystemCreateConfig{
|
|
Entity: obrimFilesystemString(config, "entity"),
|
|
Strategy: obrimFilesystemStringDefault(config, "strategy", obrimFilesystemStrategyUser),
|
|
Base: obrimFilesystemString(config, "base"),
|
|
Name: obrimFilesystemString(config, "name"),
|
|
Hidden: obrimFilesystemBool(config, "hidden"),
|
|
}
|
|
|
|
path, err := obrimFilesystemResolvePath(options.Strategy, options.Base, options.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parent := filepath.Dir(path)
|
|
if err := os.MkdirAll(parent, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch options.Entity {
|
|
case obrimFilesystemEntityFile:
|
|
err = obrimFilesystemCreateFile(path)
|
|
case obrimFilesystemEntityDirectory:
|
|
err = obrimFilesystemCreateDirectory(path)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported filesystem entity")
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return map[string]any{
|
|
"path": path,
|
|
"entity": options.Entity,
|
|
"hidden": obrimFilesystemIsHidden(filepath.Base(path)),
|
|
"created": true,
|
|
}, nil
|
|
}
|
|
|
|
// Creates a filesystem file.
|
|
func obrimFilesystemCreateFile(path string) error {
|
|
file, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return file.Close()
|
|
}
|
|
|
|
// Creates a filesystem directory.
|
|
func obrimFilesystemCreateDirectory(path string) error {
|
|
return os.Mkdir(path, 0755)
|
|
}
|
|
|
|
// Executes a filesystem deletion operation.
|
|
func obrimFilesystemDelete(config map[string]any) (map[string]any, error) {
|
|
options := obrimFilesystemDeleteConfig{
|
|
Entity: obrimFilesystemString(config, "entity"),
|
|
Strategy: obrimFilesystemStringDefault(config, "strategy", obrimFilesystemStrategyUser),
|
|
Base: obrimFilesystemString(config, "base"),
|
|
Name: obrimFilesystemString(config, "name"),
|
|
Recursive: obrimFilesystemBool(config, "recursive"),
|
|
}
|
|
|
|
path, err := obrimFilesystemResolvePath(options.Strategy, options.Base, options.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
info, err := os.Lstat(path)
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("filesystem entity does not exist")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actualEntity := obrimFilesystemEntityFile
|
|
if info.IsDir() {
|
|
actualEntity = obrimFilesystemEntityDirectory
|
|
}
|
|
|
|
if actualEntity != options.Entity {
|
|
return nil, fmt.Errorf("filesystem entity type mismatch")
|
|
}
|
|
|
|
switch options.Entity {
|
|
case obrimFilesystemEntityFile:
|
|
err = obrimFilesystemDeleteFile(path)
|
|
case obrimFilesystemEntityDirectory:
|
|
err = obrimFilesystemDeleteDirectory(path, options.Recursive)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported filesystem entity")
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return map[string]any{
|
|
"path": path,
|
|
"entity": options.Entity,
|
|
"deleted": true,
|
|
}, nil
|
|
}
|
|
|
|
// Deletes a filesystem file.
|
|
func obrimFilesystemDeleteFile(path string) error {
|
|
return os.Remove(path)
|
|
}
|
|
|
|
// Deletes a filesystem directory.
|
|
func obrimFilesystemDeleteDirectory(path string, recursive bool) error {
|
|
if recursive {
|
|
return os.RemoveAll(path)
|
|
}
|
|
|
|
return os.Remove(path)
|
|
}
|
|
|
|
// Executes a filesystem permission operation.
|
|
func obrimFilesystemPermission(config map[string]any) (map[string]any, error) {
|
|
options := obrimFilesystemPermissionConfig{
|
|
Strategy: obrimFilesystemStringDefault(config, "strategy", obrimFilesystemStrategyUser),
|
|
Base: obrimFilesystemString(config, "base"),
|
|
Name: obrimFilesystemString(config, "name"),
|
|
Mode: obrimFilesystemString(config, "mode"),
|
|
Permission: obrimFilesystemString(config, "permission"),
|
|
}
|
|
|
|
path, err := obrimFilesystemResolvePath(options.Strategy, options.Base, options.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch options.Mode {
|
|
case obrimFilesystemModeRead:
|
|
return obrimFilesystemPermissionRead(path)
|
|
case obrimFilesystemModeWrite:
|
|
return obrimFilesystemPermissionWrite(path, options.Permission)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported permission mode")
|
|
}
|
|
}
|
|
|
|
// Retrieves filesystem permissions.
|
|
func obrimFilesystemPermissionRead(path string) (map[string]any, error) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
permission := fmt.Sprintf("%04o", info.Mode().Perm())
|
|
|
|
return map[string]any{
|
|
"path": path,
|
|
"permission": permission,
|
|
"mode": obrimFilesystemModeRead,
|
|
"updated": false,
|
|
}, nil
|
|
}
|
|
|
|
// Updates filesystem permissions.
|
|
func obrimFilesystemPermissionWrite(path string, permission string) (map[string]any, error) {
|
|
value, err := strconv.ParseUint(permission, 8, 32)
|
|
if err != nil || len(permission) < 3 || len(permission) > 4 {
|
|
return nil, fmt.Errorf("invalid permission specification")
|
|
}
|
|
|
|
if err := os.Chmod(path, os.FileMode(value)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return map[string]any{
|
|
"path": path,
|
|
"permission": fmt.Sprintf("%04o", info.Mode().Perm()),
|
|
"mode": obrimFilesystemModeWrite,
|
|
"updated": true,
|
|
}, nil
|
|
}
|
|
|
|
// Resolves a filesystem path using the requested strategy.
|
|
func obrimFilesystemResolvePath(strategy string, base string, name string) (string, error) {
|
|
switch strategy {
|
|
case obrimFilesystemStrategyUser:
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if name == "" {
|
|
return filepath.Clean(home), nil
|
|
}
|
|
|
|
if filepath.IsAbs(name) {
|
|
return filepath.Clean(name), nil
|
|
}
|
|
|
|
return filepath.Clean(filepath.Join(home, name)), nil
|
|
|
|
case obrimFilesystemStrategyCustom:
|
|
if base == "" {
|
|
return "", fmt.Errorf("custom strategy requires base path")
|
|
}
|
|
|
|
if name == "" {
|
|
return filepath.Clean(base), nil
|
|
}
|
|
|
|
if filepath.IsAbs(name) {
|
|
return filepath.Clean(name), nil
|
|
}
|
|
|
|
return filepath.Clean(filepath.Join(base, name)), nil
|
|
|
|
default:
|
|
return "", fmt.Errorf("unsupported filesystem path strategy")
|
|
}
|
|
}
|
|
|
|
// Determines whether a filesystem name is hidden.
|
|
func obrimFilesystemIsHidden(name string) bool {
|
|
if name == "" || name == "." || name == ".." {
|
|
return false
|
|
}
|
|
|
|
return strings.HasPrefix(name, ".")
|
|
}
|
|
|
|
// Determines whether a filesystem path is readable.
|
|
func obrimFilesystemCanRead(path string) bool {
|
|
return obrimFilesystemCheckReadPath(path)
|
|
}
|
|
|
|
// Determines whether a filesystem path is writable.
|
|
func obrimFilesystemCanWrite(path string) bool {
|
|
return obrimFilesystemCheckWritePath(path)
|
|
}
|
|
|
|
// Retrieves a string configuration value.
|
|
func obrimFilesystemString(config map[string]any, key string) string {
|
|
value, ok := config[key]
|
|
if !ok || value == nil {
|
|
return ""
|
|
}
|
|
|
|
switch typed := value.(type) {
|
|
case string:
|
|
return typed
|
|
default:
|
|
return fmt.Sprint(typed)
|
|
}
|
|
}
|
|
|
|
// Retrieves a string configuration value with a default.
|
|
func obrimFilesystemStringDefault(config map[string]any, key string, defaultValue string) string {
|
|
value := obrimFilesystemString(config, key)
|
|
if value == "" {
|
|
return defaultValue
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
// Retrieves a boolean configuration value.
|
|
func obrimFilesystemBool(config map[string]any, key string) bool {
|
|
value, ok := config[key]
|
|
if !ok || value == nil {
|
|
return false
|
|
}
|
|
|
|
switch typed := value.(type) {
|
|
case bool:
|
|
return typed
|
|
case string:
|
|
parsed, err := strconv.ParseBool(typed)
|
|
if err == nil {
|
|
return parsed
|
|
}
|
|
case int:
|
|
return typed != 0
|
|
case int8:
|
|
return typed != 0
|
|
case int16:
|
|
return typed != 0
|
|
case int32:
|
|
return typed != 0
|
|
case int64:
|
|
return typed != 0
|
|
case uint:
|
|
return typed != 0
|
|
case uint8:
|
|
return typed != 0
|
|
case uint16:
|
|
return typed != 0
|
|
case uint32:
|
|
return typed != 0
|
|
case uint64:
|
|
return typed != 0
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Retrieves a string from a filesystem entry map.
|
|
func obrimFilesystemMapString(value map[string]any, key string) string {
|
|
item, ok := value[key]
|
|
if !ok || item == nil {
|
|
return ""
|
|
}
|
|
|
|
if typed, ok := item.(string); ok {
|
|
return typed
|
|
}
|
|
|
|
return fmt.Sprint(item)
|
|
}
|
|
|
|
// Retrieves a boolean from a filesystem entry map.
|
|
func obrimFilesystemMapBool(value map[string]any, key string) bool {
|
|
item, ok := value[key]
|
|
if !ok || item == nil {
|
|
return false
|
|
}
|
|
|
|
typed, ok := item.(bool)
|
|
return ok && typed
|
|
}
|
|
|
|
// Retrieves an int64 from a filesystem entry map.
|
|
func obrimFilesystemMapInt64(value map[string]any, key string) int64 {
|
|
item, ok := value[key]
|
|
if !ok || item == nil {
|
|
return 0
|
|
}
|
|
|
|
switch typed := item.(type) {
|
|
case int64:
|
|
return typed
|
|
case int:
|
|
return int64(typed)
|
|
case int32:
|
|
return int64(typed)
|
|
case int16:
|
|
return int64(typed)
|
|
case int8:
|
|
return int64(typed)
|
|
case uint64:
|
|
return int64(typed)
|
|
case uint32:
|
|
return int64(typed)
|
|
case uint16:
|
|
return int64(typed)
|
|
case uint8:
|
|
return int64(typed)
|
|
case uint:
|
|
return int64(typed)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|