aviorapi/essential/visible/service/helper/filesystem/filesystem.go
2026-08-28 12:53:42 +08:00

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
}
}