Repository: tableauio/tableau
Pull request #161 (2024-10-30 12:11:37)

fs(perm): set permission to 0755 (drwxr-xr-x) when create new directory

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tableauio/tableau/pull/161?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) Report
Attention: Patch coverage is `20.00000%` with `4 lines` in your changes missing coverage. Please review.
> Project coverage is 63.34%. Comparing base [(`e5ff8b9`)](https://app.codecov.io/gh/tableauio/tableau/commit/e5ff8b93538d05177cc020d378d87f219eb8aa50?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) to head [(`a9bdc11`)](https://app.codecov.io/gh/tableauio/tableau/commit/a9bdc1189c288fe395513a8b5d7e98a513b92a1b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).

| [Files with missing lines](https://app.codecov.io/gh/tableauio/tableau/pull/161?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) | Patch % | Lines |
|---|---|---|
| [internal/xlsxgen/xlsxgen.go](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&filepath=internal%2Fxlsxgen%2Fxlsxgen.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-aW50ZXJuYWwveGxzeGdlbi94bHN4Z2VuLmdv) | 0.00% | [2 Missing :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |
| [log/driver/zapdriver/logger.go](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&filepath=log%2Fdriver%2Fzapdriver%2Flogger.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-bG9nL2RyaXZlci96YXBkcml2ZXIvbG9nZ2VyLmdv) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |
| [store/store.go](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&filepath=store%2Fstore.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio#diff-c3RvcmUvc3RvcmUuZ28=) | 0.00% | [0 Missing and 1 partial :warning: ](https://app.codecov.io/gh/tableauio/tableau/pull/161?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio) |

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #161   +/-   ##
=======================================
  Coverage   63.34%   63.34%           
=======================================
  Files          74       74           
  Lines        9959     9959           
=======================================
  Hits         6309     6309           
  Misses       3064     3064           
  Partials      586      586           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tableauio/tableau/pull/161?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=tableauio).


Patch:
diff --git a/internal/confgen/confgen.go b/internal/confgen/confgen.go
index a8d2e9e..fd7ed52 100644
--- a/internal/confgen/confgen.go
+++ b/internal/confgen/confgen.go
@@ -7,8 +7,8 @@ import (
 	"time"
 
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -100,7 +100,7 @@ func (gen *Generator) GenWorkbook(bookSpecifiers ...string) error {
 		if err != nil {
 			return xerrors.Wrapf(err, "parse book specifier failed: %s", specifier)
 		}
-		relCleanSlashPath := fs.CleanSlashPath(bookName)
+		relCleanSlashPath := xfs.CleanSlashPath(bookName)
 		log.Debugf("convert relWorkbookPath to relCleanSlashPath: %s -> %s", bookName, relCleanSlashPath)
 		primaryBookIndexInfo, ok := bookIndexes[relCleanSlashPath]
 		if !ok {
@@ -137,12 +137,12 @@ func (gen *Generator) convert(prFiles *protoregistry.Files, fd protoreflect.File
 	}
 
 	// filter subdir
-	if !fs.HasSubdirPrefix(workbook.Name, gen.InputOpt.Subdirs) {
+	if !xfs.HasSubdirPrefix(workbook.Name, gen.InputOpt.Subdirs) {
 		return nil
 	}
 
 	// rewrite subdir
-	rewrittenWorkbookName := fs.RewriteSubdir(workbook.Name, gen.InputOpt.SubdirRewrites)
+	rewrittenWorkbookName := xfs.RewriteSubdir(workbook.Name, gen.InputOpt.SubdirRewrites)
 	absWbPath := filepath.Join(gen.InputDir, rewrittenWorkbookName)
 	log.Debugf("proto: %s, workbook options: %s", fd.Path(), workbook)
 
diff --git a/internal/confgen/parser.go b/internal/confgen/parser.go
index 04e19c5..0d296e1 100644
--- a/internal/confgen/parser.go
+++ b/internal/confgen/parser.go
@@ -10,11 +10,11 @@ import (
 
 	"github.com/tableauio/tableau/format"
 	"github.com/tableauio/tableau/internal/confgen/prop"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -57,7 +57,7 @@ func (x *sheetExporter) ScatterAndExport(info *SheetInfo,
 			filename = fmt.Sprintf("%s_%s", impInfo.BookName(), sheetName)
 		}
 		if info.Opts.WithParentDir {
-			parentDirName := fs.GetDirectParentDirName(impInfo.Filename())
+			parentDirName := xfs.GetDirectParentDirName(impInfo.Filename())
 			return filepath.Join(parentDirName, filename)
 		}
 		return filename
@@ -113,7 +113,7 @@ func (x *sheetExporter) MergeAndExport(info *SheetInfo,
 		// here filename has no ext suffix
 		filename := string(info.MD.Name())
 		if info.Opts.WithParentDir {
-			parentDirName := fs.GetDirectParentDirName(impInfo.Filename())
+			parentDirName := xfs.GetDirectParentDirName(impInfo.Filename())
 			return filepath.Join(parentDirName, filename)
 		}
 		return filename
diff --git a/internal/confgen/prop/refer.go b/internal/confgen/prop/refer.go
index b9a194d..4f324a2 100644
--- a/internal/confgen/prop/refer.go
+++ b/internal/confgen/prop/refer.go
@@ -7,9 +7,9 @@ import (
 	"sync"
 
 	"github.com/emirpasic/gods/sets/hashset"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/xerrors"
 	"google.golang.org/protobuf/proto"
@@ -152,7 +152,7 @@ func loadValueSpace(refer string, input *Input) (*ValueSpace, error) {
 	sheetName := sheetOpts.Name
 
 	// rewrite subdir
-	rewrittenWorkbookName := fs.RewriteSubdir(bookName, input.SubdirRewrites)
+	rewrittenWorkbookName := xfs.RewriteSubdir(bookName, input.SubdirRewrites)
 	absWbPath := filepath.Join(input.InputDir, rewrittenWorkbookName)
 	primaryImporter, err := importer.New(absWbPath, importer.Sheets([]string{sheetName}))
 	if err != nil {
diff --git a/internal/confgen/util.go b/internal/confgen/util.go
index 1c8ca95..5885156 100644
--- a/internal/confgen/util.go
+++ b/internal/confgen/util.go
@@ -6,10 +6,10 @@ import (
 	"sync"
 
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
@@ -119,7 +119,7 @@ func parseBookSpecifier(bookSpecifier string) (bookName string, sheetName string
 	fmt := format.GetFormat(bookSpecifier)
 	if fmt == format.CSV {
 		// special process for CSV filename pattern: "<BookName>#<SheetName>.csv"
-		bookName, err := fs.ParseCSVBooknamePatternFrom(bookSpecifier)
+		bookName, err := xfs.ParseCSVBooknamePatternFrom(bookSpecifier)
 		if err != nil {
 			return "", "", err
 		}
@@ -129,9 +129,9 @@ func parseBookSpecifier(bookSpecifier string) (bookName string, sheetName string
 	baseBookSpecifier := filepath.Base(bookSpecifier)
 	tokens := strings.SplitN(baseBookSpecifier, "#", 2)
 	if len(tokens) == 2 {
-		return fs.Join(dir, tokens[0]), tokens[1], nil
+		return xfs.Join(dir, tokens[0]), tokens[1], nil
 	}
-	return fs.Join(dir, tokens[0]), "", nil
+	return xfs.Join(dir, tokens[0]), "", nil
 }
 
 type bookIndexInfo struct {
@@ -149,11 +149,11 @@ func buildWorkbookIndex(protoPackage, inputDir string, subdirs []string, subdirR
 				return true
 			}
 			// filter subdir
-			if !fs.HasSubdirPrefix(workbook.Name, subdirs) {
+			if !xfs.HasSubdirPrefix(workbook.Name, subdirs) {
 				return true
 			}
 			// add self: rewrite subdir
-			rewrittenWorkbookName := fs.RewriteSubdir(workbook.Name, subdirRewrites)
+			rewrittenWorkbookName := xfs.RewriteSubdir(workbook.Name, subdirRewrites)
 			if bookIndexes[rewrittenWorkbookName] == nil {
 				bookIndexes[rewrittenWorkbookName] = &bookIndexInfo{
 					books: make(map[string]protoreflect.FileDescriptor),
@@ -213,7 +213,7 @@ func getRealSheetName(info *SheetInfo, impInfo importer.ImporterInfo) string {
 }
 
 func getRelBookName(basepath, filename string) string {
-	if relBookName, err := fs.Rel(basepath, filename); err != nil {
+	if relBookName, err := xfs.Rel(basepath, filename); err != nil {
 		log.Warnf("get relative path failed: %+v", err)
 		return filename
 	} else {
diff --git a/internal/fs/testdata/fake-empty.xlsx b/internal/fs/testdata/fake-empty.xlsx
deleted file mode 100644
index e69de29..0000000
diff --git a/internal/importer/csv.go b/internal/importer/csv.go
index 88241dd..b69fce4 100644
--- a/internal/importer/csv.go
+++ b/internal/importer/csv.go
@@ -7,8 +7,8 @@ import (
 	"path/filepath"
 
 	"github.com/emirpasic/gods/sets/treeset"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/xerrors"
@@ -141,11 +141,11 @@ func readCSVRows(filename string, topN uint) (rows [][]string, err error) {
 }
 
 func parseCSVBookReaderOptions(filename string, sheetNames []string) (*bookReaderOptions, error) {
-	bookName, _, err := fs.ParseCSVFilenamePattern(filename)
+	bookName, _, err := xfs.ParseCSVFilenamePattern(filename)
 	if err != nil {
 		return nil, xerrors.Errorf("cannot parse the book name from filename: %s", filename)
 	}
-	globFilename := fs.GenCSVBooknamePattern(filepath.Dir(filename), bookName)
+	globFilename := xfs.GenCSVBooknamePattern(filepath.Dir(filename), bookName)
 	matches, err := filepath.Glob(globFilename)
 	if err != nil {
 		return nil, xerrors.Wrapf(err, "failed to glob %s", globFilename)
@@ -157,7 +157,7 @@ func parseCSVBookReaderOptions(filename string, sheetNames []string) (*bookReade
 	// NOTE: keep the order of sheets
 	set := treeset.NewWithStringComparator()
 	for _, filename := range matches {
-		set.Add(fs.CleanSlashPath(filename))
+		set.Add(xfs.CleanSlashPath(filename))
 	}
 
 	brOpts := &bookReaderOptions{
@@ -166,7 +166,7 @@ func parseCSVBookReaderOptions(filename string, sheetNames []string) (*bookReade
 	}
 	for _, val := range set.Values() {
 		filename := val.(string)
-		_, sheetName, err := fs.ParseCSVFilenamePattern(filename)
+		_, sheetName, err := xfs.ParseCSVFilenamePattern(filename)
 		if err != nil {
 			return nil, xerrors.Errorf("cannot parse the book name from filename: %s", filename)
 		}
diff --git a/internal/importer/importer.go b/internal/importer/importer.go
index c0b9854..55d7814 100644
--- a/internal/importer/importer.go
+++ b/internal/importer/importer.go
@@ -5,8 +5,8 @@ import (
 	"strings"
 
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb/internalpb"
 	"github.com/tableauio/tableau/xerrors"
@@ -73,7 +73,7 @@ func GetScatterImporters(inputDir, primaryBookName, sheetName string, scatterSpe
 		for relBookPath := range relBookPaths {
 			log.Infof("%18s: %s#%s", "scatter sheet", relBookPath, specifiedSheetName)
 			fpath := filepath.Join(inputDir, relBookPath)
-			rewrittenWorkbookName := fs.RewriteSubdir(primaryBookName, subdirRewrites)
+			rewrittenWorkbookName := xfs.RewriteSubdir(primaryBookName, subdirRewrites)
 			primaryBookPath := filepath.Join(inputDir, rewrittenWorkbookName)
 			importer, err := New(fpath, Sheets([]string{specifiedSheetName}), Cloned(primaryBookPath))
 			if err != nil {
@@ -102,7 +102,7 @@ func GetMergerImporters(inputDir, primaryBookName, sheetName string, sheetSpecif
 		for relBookPath := range relBookPaths {
 			log.Infof("%18s: %s#%s", "merge sheet", relBookPath, specifiedSheetName)
 			fpath := filepath.Join(inputDir, relBookPath)
-			rewrittenWorkbookName := fs.RewriteSubdir(primaryBookName, subdirRewrites)
+			rewrittenWorkbookName := xfs.RewriteSubdir(primaryBookName, subdirRewrites)
 			primaryBookPath := filepath.Join(inputDir, rewrittenWorkbookName)
 			importer, err := New(fpath, Sheets([]string{specifiedSheetName}), Cloned(primaryBookPath))
 			if err != nil {
@@ -124,12 +124,12 @@ func ResolveSheetSpecifier(inputDir, primaryBookName string, sheetSpecifier stri
 	bookNameGlob, specifiedSheetName := ParseSheetSpecifier(sheetSpecifier)
 
 	// rewrite subdir
-	rewrittenWorkbookName := fs.RewriteSubdir(primaryBookName, subdirRewrites)
+	rewrittenWorkbookName := xfs.RewriteSubdir(primaryBookName, subdirRewrites)
 
 	primaryBookPath := filepath.Join(inputDir, rewrittenWorkbookName)
 	log.Debugf("rewrittenAbsWorkbookName: %s", primaryBookPath)
 	fmt := format.GetFormat(primaryBookPath)
-	pattern := fs.Join(filepath.Dir(primaryBookPath), bookNameGlob)
+	pattern := xfs.Join(filepath.Dir(primaryBookPath), bookNameGlob)
 	matches, err := filepath.Glob(pattern)
 	if err != nil {
 		return nil, "", xerrors.Wrapf(err, "failed to glob pattern: %s", pattern)
@@ -142,16 +142,16 @@ func ResolveSheetSpecifier(inputDir, primaryBookName string, sheetSpecifier stri
 		path := match
 		if fmt == format.CSV {
 			// special process for CSV filename pattern: "<BookName>#<SheetName>.csv"
-			path, err = fs.ParseCSVBooknamePatternFrom(match)
+			path, err = xfs.ParseCSVBooknamePatternFrom(match)
 			if err != nil {
 				return nil, "", err
 			}
 		}
-		if specifiedSheetName == "" && fs.IsSamePath(path, primaryBookPath) {
+		if specifiedSheetName == "" && xfs.IsSamePath(path, primaryBookPath) {
 			// sheet name not specified, so exclude self
 			continue
 		}
-		secondaryBookName, err := fs.Rel(inputDir, path)
+		secondaryBookName, err := xfs.Rel(inputDir, path)
 		if err != nil {
 			return nil, "", err
 		}
diff --git a/internal/protogen/exporter.go b/internal/protogen/exporter.go
index 5b5f5de..659a922 100644
--- a/internal/protogen/exporter.go
+++ b/internal/protogen/exporter.go
@@ -6,10 +6,10 @@ import (
 
 	"github.com/emirpasic/gods/sets/treeset"
 	"github.com/rogpeppe/go-internal/lockedfile"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/printer"
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -106,7 +106,7 @@ func (x *bookExporter) export(checkProtoFileConflicts bool) error {
 	// refer: https://go.googlesource.com/proposal/+/master/design/33974-add-public-lockedfile-pkg.md
 
 	if checkProtoFileConflicts {
-		if existed, err := fs.Exists(path); err != nil {
+		if existed, err := xfs.Exists(path); err != nil {
 			return xerrors.WrapKV(err)
 		} else {
 			if existed {
diff --git a/internal/protogen/parser.go b/internal/protogen/parser.go
index ba008e8..799661b 100644
--- a/internal/protogen/parser.go
+++ b/internal/protogen/parser.go
@@ -5,11 +5,11 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/protogen/parseroptions"
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -42,7 +42,7 @@ func newBookParser(bookName, alias, relSlashPath string, gen *Generator) *bookPa
 	filename := strcase.ToSnake(protoBookName)
 	if gen.OutputOpt.FilenameWithSubdirPrefix {
 		bookPath := filepath.Join(filepath.Dir(relSlashPath), protoBookName)
-		snakePath := strcase.ToSnake(fs.CleanSlashPath(bookPath))
+		snakePath := strcase.ToSnake(xfs.CleanSlashPath(bookPath))
 		filename = strings.ReplaceAll(snakePath, "/", "__")
 	}
 	bp := &bookParser{
diff --git a/internal/protogen/protogen.go b/internal/protogen/protogen.go
index 86f6463..f952373 100644
--- a/internal/protogen/protogen.go
+++ b/internal/protogen/protogen.go
@@ -10,12 +10,12 @@ import (
 	"github.com/tableauio/tableau/format"
 	"github.com/tableauio/tableau/internal/confgen"
 	"github.com/tableauio/tableau/internal/excel"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
 	"github.com/tableauio/tableau/internal/importer/book"
 	"github.com/tableauio/tableau/internal/protogen/parseroptions"
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
@@ -212,7 +212,7 @@ func (gen *Generator) generate(dir string) (err error) {
 		}
 
 		if fmt == format.CSV {
-			bookName, _, err := fs.ParseCSVFilenamePattern(entry.Name())
+			bookName, _, err := xfs.ParseCSVFilenamePattern(entry.Name())
 			if err != nil {
 				return err
 			}
@@ -290,7 +290,7 @@ func (gen *Generator) convertDocument(dir, filename string, checkProtoFileConfli
 	}
 	debugBookName := relativePath
 	// rewrite subdir
-	rewrittenBookName := fs.RewriteSubdir(relativePath, gen.InputOpt.SubdirRewrites)
+	rewrittenBookName := xfs.RewriteSubdir(relativePath, gen.InputOpt.SubdirRewrites)
 	if rewrittenBookName != relativePath {
 		debugBookName += " (rewrite: " + rewrittenBookName + ")"
 	}
@@ -373,7 +373,7 @@ func (gen *Generator) convertTable(dir, filename string, checkProtoFileConflicts
 	}
 	debugBookName := relativePath
 	// rewrite subdir
-	rewrittenBookName := fs.RewriteSubdir(relativePath, gen.InputOpt.SubdirRewrites)
+	rewrittenBookName := xfs.RewriteSubdir(relativePath, gen.InputOpt.SubdirRewrites)
 	if rewrittenBookName != relativePath {
 		debugBookName += " (rewrite: " + rewrittenBookName + ")"
 	}
diff --git a/internal/protogen/util.go b/internal/protogen/util.go
index 2389bb5..36b855c 100644
--- a/internal/protogen/util.go
+++ b/internal/protogen/util.go
@@ -8,9 +8,9 @@ import (
 	"strings"
 
 	"github.com/tableauio/tableau/internal/excel"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/xerrors"
@@ -24,7 +24,7 @@ const (
 )
 
 func prepareOutdir(outdir string, importFiles []string, delExisted bool) error {
-	existed, err := fs.Exists(outdir)
+	existed, err := xfs.Exists(outdir)
 	if err != nil {
 		return xerrors.WrapKV(err, xerrors.KeyOutdir, outdir)
 	}
@@ -53,7 +53,7 @@ func prepareOutdir(outdir string, importFiles []string, delExisted bool) error {
 		}
 	} else {
 		// create output dir
-		err = os.MkdirAll(outdir, 0700)
+		err = os.MkdirAll(outdir, xfs.DefaultDirPerm)
 		if err != nil {
 			return xerrors.WrapKV(err, xerrors.KeyOutdir, outdir)
 		}
diff --git a/internal/fs/csv.go b/internal/xfs/csv.go
similarity index 95%
rename from internal/fs/csv.go
rename to internal/xfs/csv.go
index fa2b3d2..d694bcd 100644
--- a/internal/fs/csv.go
+++ b/internal/xfs/csv.go
@@ -1,4 +1,4 @@
-package fs
+package xfs
 
 import (
 	"path/filepath"
diff --git a/internal/fs/path.go b/internal/xfs/path.go
similarity index 95%
rename from internal/fs/path.go
rename to internal/xfs/path.go
index 2dc80f2..c8d7684 100644
--- a/internal/fs/path.go
+++ b/internal/xfs/path.go
@@ -1,4 +1,4 @@
-package fs
+package xfs
 
 import (
 	"path/filepath"
diff --git a/internal/fs/testdata/empty#@TABLEAU.csv b/internal/xfs/testdata/empty#@TABLEAU.csv
similarity index 100%
rename from internal/fs/testdata/empty#@TABLEAU.csv
rename to internal/xfs/testdata/empty#@TABLEAU.csv
diff --git a/internal/fs/testdata/empty.txt b/internal/xfs/testdata/empty.txt
similarity index 100%
rename from internal/fs/testdata/empty.txt
rename to internal/xfs/testdata/empty.txt
diff --git a/internal/fs/testdata/test.txt b/internal/xfs/testdata/test.txt
similarity index 100%
rename from internal/fs/testdata/test.txt
rename to internal/xfs/testdata/test.txt
diff --git a/internal/fs/util.go b/internal/xfs/util.go
similarity index 97%
rename from internal/fs/util.go
rename to internal/xfs/util.go
index ecc20b8..80155b1 100644
--- a/internal/fs/util.go
+++ b/internal/xfs/util.go
@@ -1,8 +1,9 @@
-package fs
+package xfs
 
 import (
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
@@ -11,6 +12,11 @@ import (
 	"github.com/tableauio/tableau/xerrors"
 )
 
+const (
+	DefaultDirPerm  fs.FileMode = 0755 // drwxr-xr-x
+	DefaultFilePerm fs.FileMode = 0644 // -rw-r--r--
+)
+
 // CopyFile copies a file from src to dst. If src and dst files exist, and are
 // the same, then return success. Otherise, attempt to create a hard link
 // between the two files. If that fails, copy the file contents from src to dst.
diff --git a/internal/xlsxgen/xlsxgen.go b/internal/xlsxgen/xlsxgen.go
index aad1de2..9aa5337 100644
--- a/internal/xlsxgen/xlsxgen.go
+++ b/internal/xlsxgen/xlsxgen.go
@@ -13,6 +13,7 @@ import (
 
 	"github.com/tableauio/tableau/internal/strcase"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -185,7 +186,7 @@ func (gen *Generator) Generate() {
 		panic(err)
 	}
 	// create output dir
-	err = os.MkdirAll(gen.OutputDir, 0700)
+	err = os.MkdirAll(gen.OutputDir, xfs.DefaultDirPerm)
 	if err != nil {
 		panic(err)
 	}
@@ -232,7 +233,7 @@ func (gen *Generator) Generate() {
 // ExportSheet export a worksheet.
 func (gen *Generator) ExportSheet(metaSheet *MetaSheet) error {
 	// create output dir
-	if err := os.MkdirAll(gen.OutputDir, 0700); err != nil {
+	if err := os.MkdirAll(gen.OutputDir, xfs.DefaultDirPerm); err != nil {
 		return xerrors.Wrapf(err, "failed to create output dir: %s", gen.OutputDir)
 	}
 	filename := filepath.Join(gen.OutputDir, gen.Workbook)
diff --git a/internal/xproto/build.go b/internal/xproto/build.go
index 9799f2c..3b6d360 100644
--- a/internal/xproto/build.go
+++ b/internal/xproto/build.go
@@ -6,8 +6,8 @@ import (
 
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/desc/protoparse"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/types"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/xerrors"
@@ -47,7 +47,7 @@ func NewFiles(protoPaths []string, protoFiles []string, excludeProtoFiles ...str
 			return nil, xerrors.Wrapf(err, "failed to glob files in %s", filename)
 		}
 		for _, match := range matches {
-			cleanSlashPath := fs.CleanSlashPath(match)
+			cleanSlashPath := xfs.CleanSlashPath(match)
 			parsedExcludedProtoFiles[cleanSlashPath] = true
 		}
 	}
@@ -58,10 +58,10 @@ func NewFiles(protoPaths []string, protoFiles []string, excludeProtoFiles ...str
 			return nil, xerrors.Wrapf(err, "failed to glob files in %s", filename)
 		}
 		for _, match := range matches {
-			cleanSlashPath := fs.CleanSlashPath(match)
+			cleanSlashPath := xfs.CleanSlashPath(match)
 			if !parsedExcludedProtoFiles[cleanSlashPath] {
 				for _, protoPath := range protoPaths {
-					cleanProtoPath := fs.CleanSlashPath(protoPath) + "/"
+					cleanProtoPath := xfs.CleanSlashPath(protoPath) + "/"
 					cleanSlashPath = strings.TrimPrefix(cleanSlashPath, cleanProtoPath)
 				}
 				parsedProtoFiles = append(parsedProtoFiles, cleanSlashPath)
@@ -73,13 +73,13 @@ func NewFiles(protoPaths []string, protoFiles []string, excludeProtoFiles ...str
 		// 	if err != nil {
 		// 		return nil, xerrors.Wrapf(err, "failed to get absolute path for %s", match)
 		// 	}
-		// 	cleanSlashPath := fs.CleanSlashPath(match)
+		// 	cleanSlashPath := xfs.CleanSlashPath(match)
 		// 	for _, importPath := range protoPaths {
 		// 		importPath, err := filepath.Abs(importPath)
 		// 		if err != nil {
 		// 			return nil, xerrors.Wrapf(err, "failed to get absolute path for %s", importPath)
 		// 		}
-		// 		importCleanSlashPath := fs.CleanSlashPath(importPath)
+		// 		importCleanSlashPath := xfs.CleanSlashPath(importPath)
 		// 		if !strings.HasPrefix(cleanSlashPath, importCleanSlashPath) {
 		// 			log.Debugf("add proto file: %s", originMatch)
 		// 			parsedProtoFiles = append(parsedProtoFiles, originMatch)
diff --git a/load/load.go b/load/load.go
index f11762c..4f2fc67 100644
--- a/load/load.go
+++ b/load/load.go
@@ -10,8 +10,8 @@ import (
 
 	"github.com/tableauio/tableau/format"
 	"github.com/tableauio/tableau/internal/confgen"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/internal/xproto"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/proto/tableaupb"
@@ -62,7 +62,7 @@ func loadWithPatch(msg proto.Message, path string, fmt format.Format, patch tabl
 	// check existence of each patch path
 	var existedPatchPaths []string
 	for _, patchPath := range patchPaths {
-		existed, err := fs.Exists(patchPath)
+		existed, err := xfs.Exists(patchPath)
 		if err != nil {
 			return xerrors.Wrapf(err, "failed to check file existence: %s", patchPath)
 		}
@@ -145,7 +145,7 @@ func loadOrigin(msg proto.Message, dir string, options ...Option) error {
 		return xerrors.Errorf("workbook options not found of protofile: %v", protofile)
 	}
 	// rewrite subdir
-	rewrittenWorkbookName := fs.RewriteSubdir(workbook.Name, opts.SubdirRewrites)
+	rewrittenWorkbookName := xfs.RewriteSubdir(workbook.Name, opts.SubdirRewrites)
 	wbPath := filepath.Join(dir, rewrittenWorkbookName)
 	log.Debugf("load origin file: %v", wbPath)
 	// get sheet name
diff --git a/log/driver/zapdriver/logger.go b/log/driver/zapdriver/logger.go
index ec0b870..7988adc 100644
--- a/log/driver/zapdriver/logger.go
+++ b/log/driver/zapdriver/logger.go
@@ -157,7 +157,7 @@ func createFileWriter(filename string) (zapcore.WriteSyncer, error) {
 func createLumberjackLogger(filename string) (*lumberjack.Logger, error) {
 	// create output dir
 	// dir := filepath.Dir(filename)
-	// err := os.MkdirAll(dir, 0700)
+	// err := os.MkdirAll(dir, xfs.DefaultDirPerm)
 	// if err != nil {
 	// 	return nil, err
 	// }
diff --git a/store/store.go b/store/store.go
index dee7281..926b8c1 100644
--- a/store/store.go
+++ b/store/store.go
@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 
 	"github.com/tableauio/tableau/format"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/xerrors"
 	"google.golang.org/protobuf/proto"
@@ -56,12 +57,12 @@ func Store(msg proto.Message, dir string, fmt format.Format, options ...Option)
 
 	fpath := filepath.Join(dir, filename)
 	// prepare dir
-	if err := os.MkdirAll(filepath.Dir(fpath), 0700); err != nil {
+	if err := os.MkdirAll(filepath.Dir(fpath), xfs.DefaultDirPerm); err != nil {
 		return xerrors.WrapKV(err, `failed to create dir "%s"`, filepath.Dir(fpath))
 	}
 
 	// write file
-	err = os.WriteFile(fpath, out, 0644)
+	err = os.WriteFile(fpath, out, xfs.DefaultFilePerm)
 	if err != nil {
 		return xerrors.Errorf(`write file "%s" failed: %s`, fpath, err)
 	}


Test patch:
diff --git a/internal/confgen/confgen_test.go b/internal/confgen/confgen_test.go
index 279dc5e..0f2d592 100644
--- a/internal/confgen/confgen_test.go
+++ b/internal/confgen/confgen_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/tableauio/tableau/format"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
 )
@@ -13,7 +14,7 @@ import (
 func prepareOutput() error {
 	// prepare output common dir
 	outdir := "./testdata/_conf"
-	err := os.MkdirAll(outdir, 0700)
+	err := os.MkdirAll(outdir, xfs.DefaultDirPerm)
 	if err != nil {
 		return fmt.Errorf("failed to create output dir: %v", err)
 	}
diff --git a/internal/importer/importer_test.go b/internal/importer/importer_test.go
index 64d79d2..5cc35ce 100644
--- a/internal/importer/importer_test.go
+++ b/internal/importer/importer_test.go
@@ -7,11 +7,11 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
+	"github.com/tableauio/tableau/internal/xfs"
 )
 
 func init() {
-	err := fs.RangeFilesByFormat("./testdata", format.CSV, func(bookPath string) error {
+	err := xfs.RangeFilesByFormat("./testdata", format.CSV, func(bookPath string) error {
 		// log.Printf("path: %s", bookPath)
 		imp, err := NewCSVImporter(bookPath, nil, nil, 0, false)
 		if err != nil {
@@ -135,7 +135,7 @@ func TestGetMergerImporters(t *testing.T) {
 			}
 			filenames := []string{}
 			for _, imp := range got {
-				filenames = append(filenames, fs.CleanSlashPath(imp.Filename()))
+				filenames = append(filenames, xfs.CleanSlashPath(imp.Filename()))
 			}
 			assert.ElementsMatch(t, tt.want, filenames, "got book filenames not match")
 		})
@@ -175,7 +175,7 @@ func TestGetScatterImporters(t *testing.T) {
 			}
 			filenames := []string{}
 			for _, imp := range got {
-				filenames = append(filenames, fs.CleanSlashPath(imp.Filename()))
+				filenames = append(filenames, xfs.CleanSlashPath(imp.Filename()))
 			}
 			assert.ElementsMatch(t, tt.want, filenames, "got book filenames not match")
 		})
diff --git a/internal/protogen/protogen_test.go b/internal/protogen/protogen_test.go
index d5599d9..599f82a 100644
--- a/internal/protogen/protogen_test.go
+++ b/internal/protogen/protogen_test.go
@@ -8,8 +8,8 @@ import (
 	"testing"
 
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/proto/tableaupb"
 	"github.com/tableauio/tableau/proto/tableaupb/internalpb"
@@ -217,12 +217,12 @@ func TestGenerator_extractTypeInfoFromSpecialSheetMode(t *testing.T) {
 
 func prepareOutput() error {
 	// prepare output common dir
-	err := os.MkdirAll(outdir, 0700)
+	err := os.MkdirAll(outdir, xfs.DefaultDirPerm)
 	if err != nil {
 		return fmt.Errorf("failed to create output dir: %v", err)
 	}
 	outCommDir := filepath.Join(outdir, "common")
-	err = os.MkdirAll(outCommDir, 0700)
+	err = os.MkdirAll(outCommDir, xfs.DefaultDirPerm)
 	if err != nil {
 		return fmt.Errorf("failed to create output common dir: %v", err)
 	}
@@ -236,7 +236,7 @@ func prepareOutput() error {
 		if !entry.IsDir() {
 			src := filepath.Join(srcCommDir, entry.Name())
 			dst := filepath.Join(outCommDir, entry.Name())
-			if err := fs.CopyFile(src, dst); err != nil {
+			if err := xfs.CopyFile(src, dst); err != nil {
 				return fmt.Errorf("copy file failed: %+v", err)
 			}
 		}
diff --git a/internal/fs/csv_test.go b/internal/xfs/csv_test.go
similarity index 99%
rename from internal/fs/csv_test.go
rename to internal/xfs/csv_test.go
index 8731bdb..c8600dd 100644
--- a/internal/fs/csv_test.go
+++ b/internal/xfs/csv_test.go
@@ -1,4 +1,4 @@
-package fs
+package xfs
 
 import "testing"
 
diff --git a/internal/fs/util_test.go b/internal/xfs/util_test.go
similarity index 99%
rename from internal/fs/util_test.go
rename to internal/xfs/util_test.go
index 672d2f5..d5d92de 100644
--- a/internal/fs/util_test.go
+++ b/internal/xfs/util_test.go
@@ -1,4 +1,4 @@
-package fs
+package xfs
 
 import (
 	"testing"
diff --git a/test/bench/bench_test.go b/test/bench/bench_test.go
index 9fb03b2..caecc2b 100644
--- a/test/bench/bench_test.go
+++ b/test/bench/bench_test.go
@@ -7,8 +7,8 @@ import (
 
 	"github.com/tableauio/tableau"
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer/book"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
 	"github.com/tableauio/tableau/xerrors"
@@ -58,11 +58,11 @@ func genConf(logLevel string) error {
 }
 
 func genTestdata(maxID int) {
-	if ok, err := fs.Exists("testdata"); err != nil {
+	if ok, err := xfs.Exists("testdata"); err != nil {
 		panic(err)
 	} else if !ok {
 		// create output dir
-		err = os.MkdirAll("testdata", 0700)
+		err = os.MkdirAll("testdata", xfs.DefaultDirPerm)
 		if err != nil {
 			panic(err)
 		}
diff --git a/test/functest/func_test.go b/test/functest/func_test.go
index 5c7d269..1103320 100644
--- a/test/functest/func_test.go
+++ b/test/functest/func_test.go
@@ -26,8 +26,8 @@ import (
 
 	"github.com/stretchr/testify/require"
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
 	"github.com/tableauio/tableau/internal/importer"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/xerrors"
 )
 
@@ -46,7 +46,7 @@ func Test_CompareGeneratedJSON(t *testing.T) {
 }
 
 func Test_Excel2CSV(t *testing.T) {
-	err := fs.RangeFilesByFormat("./testdata/excel", format.Excel, func(bookPath string) error {
+	err := xfs.RangeFilesByFormat("./testdata/excel", format.Excel, func(bookPath string) error {
 		// log.Printf("path: %s", bookPath)
 		imp, err := importer.NewExcelImporter(bookPath, nil, nil, 0, false)
 		if err != nil {
@@ -58,7 +58,7 @@ func Test_Excel2CSV(t *testing.T) {
 }
 
 func Test_CSV2Excel(t *testing.T) {
-	err := fs.RangeFilesByFormat("./testdata/excel", format.CSV, func(bookPath string) error {
+	err := xfs.RangeFilesByFormat("./testdata/excel", format.CSV, func(bookPath string) error {
 		// log.Printf("path: %s", bookPath)
 		imp, err := importer.NewCSVImporter(bookPath, nil, nil, 0, false)
 		if err != nil {
diff --git a/test/functest/util.go b/test/functest/util.go
index 9f92cf3..2151f6e 100644
--- a/test/functest/util.go
+++ b/test/functest/util.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/tableauio/tableau"
 	"github.com/tableauio/tableau/format"
-	"github.com/tableauio/tableau/internal/fs"
+	"github.com/tableauio/tableau/internal/xfs"
 	"github.com/tableauio/tableau/log"
 	"github.com/tableauio/tableau/options"
 )
@@ -16,12 +16,12 @@ import (
 func genProto(logLevel string) error {
 	// prepare output common dir
 	outdir := "./_proto"
-	err := os.MkdirAll(outdir, 0700)
+	err := os.MkdirAll(outdir, xfs.DefaultDirPerm)
 	if err != nil {
 		return fmt.Errorf("failed to create output dir: %v", err)
 	}
 	outCommDir := filepath.Join(outdir, "common")
-	err = os.MkdirAll(outCommDir, 0700)
+	err = os.MkdirAll(outCommDir, xfs.DefaultDirPerm)
 	if err != nil {
 		return fmt.Errorf("failed to create output common dir: %v", err)
 	}
@@ -35,7 +35,7 @@ func genProto(logLevel string) error {
 		if !entry.IsDir() {
 			src := filepath.Join(srcCommDir, entry.Name())
 			dst := filepath.Join(outCommDir, entry.Name())
-			if err := fs.CopyFile(src, dst); err != nil {
+			if err := xfs.CopyFile(src, dst); err != nil {
 				return fmt.Errorf("copy file failed: %+v", err)
 			}
 		}