diff --git a/internal/cli/update_test.go b/internal/cli/update_test.go index 823b7cf..b7e04c8 100644 --- a/internal/cli/update_test.go +++ b/internal/cli/update_test.go @@ -24,6 +24,10 @@ func TestUpdate(t *testing.T) { inputFile: "../../testdata/markdown/import-with-exporter-before.md", wantFile: "../../testdata/markdown/import-with-exporter-updated.md", }, + "markdown with skip update": { + inputFile: "../../testdata/markdown/skip-update.md", + wantFile: "../../testdata/markdown/skip-update.md", + }, } for name, tc := range cases { diff --git a/internal/file/file.go b/internal/file/file.go index cf80014..f2b052c 100644 --- a/internal/file/file.go +++ b/internal/file/file.go @@ -23,4 +23,8 @@ type File struct { // Markers is an array holding onto each annotation block. Markers map[int]*marker.Marker + + // SkipUpdate is used to skip updating the file in place. This is set by a + // special marker syntax. + SkipUpdate bool } diff --git a/internal/file/replace.go b/internal/file/replace.go index 4b595a9..96949e4 100644 --- a/internal/file/replace.go +++ b/internal/file/replace.go @@ -33,6 +33,13 @@ func (f *File) ReplaceWithAfter(options ...ReplaceOption) error { opt(mode) } + // If SkipUpdate flag is on, do not update the file. + if f.SkipUpdate { + // Perhaps it's more friendly to log that the file update is being + // skipped. If that's truly the case, handle it here. + mode.isDryRun = true + } + return replace(f.FileName, f.ContentAfter, mode) } diff --git a/internal/file/replace_test.go b/internal/file/replace_test.go index 6816685..99d60f6 100644 --- a/internal/file/replace_test.go +++ b/internal/file/replace_test.go @@ -44,6 +44,18 @@ func TestReplaceWithAfter(t *testing.T) { }, want: "", // not written }, + "skip-update found": { + input: &File{ + FileName: "tmpfile.txt", + ContentBefore: []string{ + "Some data", + "and more", + }, + ContentAfter: []byte(`Completely different data`), + SkipUpdate: true, + }, + want: "", // not written + }, } for name, tc := range cases { diff --git a/internal/parse/parse.go b/internal/parse/parse.go index 89aca49..8bbc7ea 100644 --- a/internal/parse/parse.go +++ b/internal/parse/parse.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "path/filepath" + "strings" "github.com/upsidr/importer/internal/errorsplus" "github.com/upsidr/importer/internal/file" @@ -33,28 +34,37 @@ var ( // // If any of the above steps failed, it would return an error. This function // does not populate the ContentAfter. +// +// TODO: Consider merging with the private parse method, as this public +// function is not adding any value at the moment. func Parse(fileName string, input io.Reader) (*file.File, error) { if input == nil { return nil, ErrNoInput } - fileType := filepath.Ext(fileName) + return parse(fileName, input) +} + +// parse reads file input using scanner. This reads the input line by line, and +// store the data into File data. Parsing the data stores 3 sets of data: file +// content as is, marker details, and file content with all data between +// marker pairs purged. +func parse(fileName string, input io.Reader) (*file.File, error) { + var importerMarkerRegex string + var importerSkipMarker string + fileType := filepath.Ext(fileName) switch fileType { case ".md": - return parse(marker.ImporterMarkerMarkdown, fileName, input) + importerMarkerRegex = marker.ImporterMarkerMarkdown + importerSkipMarker = marker.ImporterSkipProcessingMarkdown case ".yaml", ".yml": - return parse(marker.ImporterMarkerYAML, fileName, input) + importerMarkerRegex = marker.ImporterMarkerYAML + importerSkipMarker = marker.ImporterSkipProcessingYAML default: return nil, fmt.Errorf("%w, '%s' provided", ErrUnsupportedFileType, fileType) } -} -// parse reads file input using scanner. This reads the input line by line, and -// store the data into File data. Parsing the data stores 3 sets of data: file -// content as is, marker details, and file content with all data between -// marker pairs purged. -func parse(markerRegex string, fileName string, input io.Reader) (*file.File, error) { f := &file.File{ FileName: fileName, @@ -80,8 +90,14 @@ func parse(markerRegex string, fileName string, input io.Reader) (*file.File, er currentStr := scanner.Text() f.ContentBefore = append(f.ContentBefore, currentStr) + // If skip marker is found, turn on the flag. This flag should disable + // in-place file update, but should not suppress generate or preview. + if strings.Contains(currentStr, importerSkipMarker) { + f.SkipUpdate = true + } + // Look for marker match - matches, err := regexpplus.MapWithNamedSubgroups(currentStr, markerRegex) + matches, err := regexpplus.MapWithNamedSubgroups(currentStr, importerMarkerRegex) if err != nil { if errors.Is(err, regexpplus.ErrNoMatch) { // If the line appears within some other marker set, remove the line. diff --git a/testdata/markdown/skip-update.md b/testdata/markdown/skip-update.md new file mode 100644 index 0000000..ce1ed0f --- /dev/null +++ b/testdata/markdown/skip-update.md @@ -0,0 +1,7 @@ + +# Markdown Demo + + +This part will not be deleted, because of the "importer-skip-update" marker at the top of the file. +This is useful for some files that use Importer Markers as "template", and use "importer generate" to generate a separate file. + diff --git a/testdata/yaml/skip-update.yaml b/testdata/yaml/skip-update.yaml new file mode 100644 index 0000000..528c2f4 --- /dev/null +++ b/testdata/yaml/skip-update.yaml @@ -0,0 +1,6 @@ +# == importer-skip-update == + +title: Demo of YAML Importer +# == import: description / begin from: ./snippet-description.yaml#[for-demo] == +dummy: This data will NOT be removed +# == import: description / end ==