From 1f4717debaad8d93a5d2281e83408d700abe1eac Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:11:02 +0200 Subject: [PATCH] refactor(fstab): Adapt fstab to new style; Adapting the parser --- server/handlers/fstab/ast/fstab.go | 45 +++++++ server/handlers/fstab/ast/parser.go | 159 +++++++++++++++++++++++ server/handlers/fstab/ast/parser_test.go | 75 +++++++++++ 3 files changed, 279 insertions(+) create mode 100644 server/handlers/fstab/ast/fstab.go create mode 100644 server/handlers/fstab/ast/parser.go create mode 100644 server/handlers/fstab/ast/parser_test.go diff --git a/server/handlers/fstab/ast/fstab.go b/server/handlers/fstab/ast/fstab.go new file mode 100644 index 0000000..125e624 --- /dev/null +++ b/server/handlers/fstab/ast/fstab.go @@ -0,0 +1,45 @@ +package ast + +import ( + "config-lsp/common" + commonparser "config-lsp/common/parser" + "github.com/emirpasic/gods/maps/treemap" +) + +type FstabFieldName string + +const ( + FstabFieldSpec FstabFieldName = "spec" + FstabFieldMountPoint FstabFieldName = "mountpoint" + FstabFieldFileSystemType FstabFieldName = "filesystemtype" + FstabFieldOptions FstabFieldName = "options" + FstabFieldFreq FstabFieldName = "freq" + FstabFieldPass FstabFieldName = "pass" +) + +type FstabField struct { + common.LocationRange + Value commonparser.ParsedString +} + +type FstabFields struct { + common.LocationRange + Spec *FstabField + MountPoint *FstabField + FilesystemType *FstabField + Options *FstabField + Freq *FstabField + Pass *FstabField +} + +type FstabEntry struct { + Fields FstabFields +} + +type FstabConfig struct { + // [uint32]FstabEntry - line number to line mapping + Entries *treemap.Map + + // [uint32]{} - line number to empty struct for comments + CommentLines map[uint32]struct{} +} diff --git a/server/handlers/fstab/ast/parser.go b/server/handlers/fstab/ast/parser.go new file mode 100644 index 0000000..78acef8 --- /dev/null +++ b/server/handlers/fstab/ast/parser.go @@ -0,0 +1,159 @@ +package ast + +import ( + "config-lsp/common" + commonparser "config-lsp/common/parser" + "config-lsp/utils" + "regexp" + + "github.com/emirpasic/gods/maps/treemap" + + gods "github.com/emirpasic/gods/utils" +) + +func NewFstabConfig() *FstabConfig { + config := &FstabConfig{} + config.Clear() + + return config +} + +func (c *FstabConfig) Clear() { + c.Entries = treemap.NewWith(gods.UInt32Comparator) + c.CommentLines = map[uint32]struct{}{} +} + +var commentPattern = regexp.MustCompile(`^\s*#`) +var emptyPattern = regexp.MustCompile(`^\s*$`) +var whitespacePattern = regexp.MustCompile(`\S+`) + +func (c *FstabConfig) Parse(input string) []common.LSPError { + errors := make([]common.LSPError, 0) + lines := utils.SplitIntoLines(input) + + for rawLineNumber, line := range lines { + lineNumber := uint32(rawLineNumber) + + if emptyPattern.MatchString(line) { + continue + } + + if commentPattern.MatchString(line) { + c.CommentLines[lineNumber] = struct{}{} + continue + } + + errors = append( + errors, + c.parseStatement(lineNumber, line)..., + ) + } + + return errors +} + +func (c *FstabConfig) parseStatement( + line uint32, + input string, +) []common.LSPError { + fields := whitespacePattern.FindAllStringIndex(input, -1) + + if len(fields) == 0 { + return []common.LSPError{ + { + Range: common.LocationRange{ + Start: common.Location{ + Line: line, + Character: 0, + }, + End: common.Location{ + Line: line, + Character: 0, + }, + }, + }, + } + } + + var spec *FstabField + var mountPoint *FstabField + var filesystemType *FstabField + var options *FstabField + var freq *FstabField + var pass *FstabField + + switch len(fields) { + case 6: + pass = parseField(line, input, fields[5]) + fallthrough + case 5: + freq = parseField(line, input, fields[4]) + fallthrough + case 4: + options = parseField(line, input, fields[3]) + fallthrough + case 3: + filesystemType = parseField(line, input, fields[2]) + fallthrough + case 2: + mountPoint = parseField(line, input, fields[1]) + fallthrough + case 1: + spec = parseField(line, input, fields[0]) + } + + fstabLine := FstabEntry{ + Fields: FstabFields{ + LocationRange: common.LocationRange{ + Start: common.Location{ + Line: line, + Character: 0, + }, + End: common.Location{ + Line: line, + Character: uint32(len(input)), + }, + }, + Spec: spec, + MountPoint: mountPoint, + FilesystemType: filesystemType, + Options: options, + Freq: freq, + Pass: pass, + }, + } + + c.Entries.Put(line, fstabLine) + + return nil +} + +func parseField( + line uint32, + input string, + field []int, +) *FstabField { + start := uint32(field[0]) + end := uint32(field[1]) + value := input[start:end] + + return &FstabField{ + LocationRange: common.LocationRange{ + Start: common.Location{ + Line: line, + Character: start, + }, + End: common.Location{ + Line: line, + Character: end, + }, + }, + Value: commonparser.ParseRawString(value, commonparser.ParseFeatures{ + ParseEscapedCharacters: true, + ParseDoubleQuotes: true, + Replacements: &map[string]string{ + `\\040`: " ", + }, + }), + } +} diff --git a/server/handlers/fstab/ast/parser_test.go b/server/handlers/fstab/ast/parser_test.go new file mode 100644 index 0000000..199eb02 --- /dev/null +++ b/server/handlers/fstab/ast/parser_test.go @@ -0,0 +1,75 @@ +package ast + +import ( + "config-lsp/utils" + "testing" +) + +func TestExample1( + t *testing.T, +) { + input := utils.Dedent(` +LABEL=test /mnt/test ext4 defaults 0 0 +`) + c := NewFstabConfig() + + errors := c.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if c.Entries.Size() != 1 { + t.Fatalf("Expected 1 entry, got %d", c.Entries.Size()) + } + + rawFirstEntry, _ := c.Entries.Get(uint32(0)) + firstEntry := rawFirstEntry.(FstabEntry) + if !(firstEntry.Fields.Spec.Value.Value == "LABEL=test" && firstEntry.Fields.MountPoint.Value.Value == "/mnt/test" && firstEntry.Fields.FilesystemType.Value.Value == "ext4" && firstEntry.Fields.Options.Value.Value == "defaults" && firstEntry.Fields.Freq.Value.Value == "0" && firstEntry.Fields.Pass.Value.Value == "0") { + t.Fatalf("Expected entry to be LABEL=test /mnt/test ext4 defaults 0 0, got %v", firstEntry) + } + + if !(firstEntry.Fields.Spec.LocationRange.Start.Line == 0 && firstEntry.Fields.Spec.LocationRange.Start.Character == 0) { + t.Errorf("Expected spec start to be 0:0, got %v", firstEntry.Fields.Spec.LocationRange.Start) + } + + if !(firstEntry.Fields.Spec.LocationRange.End.Line == 0 && firstEntry.Fields.Spec.LocationRange.End.Character == 10) { + t.Errorf("Expected spec end to be 0:10, got %v", firstEntry.Fields.Spec.LocationRange.End) + } + + if !(firstEntry.Fields.MountPoint.LocationRange.Start.Line == 0 && firstEntry.Fields.MountPoint.LocationRange.Start.Character == 11) { + t.Errorf("Expected mountpoint start to be 0:11, got %v", firstEntry.Fields.MountPoint.LocationRange.Start) + } + + if !(firstEntry.Fields.MountPoint.LocationRange.End.Line == 0 && firstEntry.Fields.MountPoint.LocationRange.End.Character == 20) { + t.Errorf("Expected mountpoint end to be 0:20, got %v", firstEntry.Fields.MountPoint.LocationRange.End) + } + + if !(firstEntry.Fields.FilesystemType.LocationRange.Start.Line == 0 && firstEntry.Fields.FilesystemType.LocationRange.Start.Character == 21) { + t.Errorf("Expected filesystemtype start to be 0:21, got %v", firstEntry.Fields.FilesystemType.LocationRange.Start) + } + + if !(firstEntry.Fields.FilesystemType.LocationRange.End.Line == 0 && firstEntry.Fields.FilesystemType.LocationRange.End.Character == 25) { + t.Errorf("Expected filesystemtype end to be 0:25, got %v", firstEntry.Fields.FilesystemType.LocationRange.End) + } + + if !(firstEntry.Fields.Options.LocationRange.Start.Line == 0 && firstEntry.Fields.Options.LocationRange.Start.Character == 26) { + t.Errorf("Expected options start to be 0:26, got %v", firstEntry.Fields.Options.LocationRange.Start) + } + + if !(firstEntry.Fields.Options.LocationRange.End.Line == 0 && firstEntry.Fields.Options.LocationRange.End.Character == 34) { + t.Errorf("Expected options end to be 0:34, got %v", firstEntry.Fields.Options.LocationRange.End) + } + + if !(firstEntry.Fields.Freq.LocationRange.Start.Line == 0 && firstEntry.Fields.Freq.LocationRange.Start.Character == 35) { + t.Errorf("Expected freq start to be 0:35, got %v", firstEntry.Fields.Freq.LocationRange.Start) + } + + if !(firstEntry.Fields.Freq.LocationRange.End.Line == 0 && firstEntry.Fields.Freq.LocationRange.End.Character == 36) { + t.Errorf("Expected freq end to be 0:36, got %v", firstEntry.Fields.Freq.LocationRange.End) + } + + if !(firstEntry.Fields.Pass.LocationRange.Start.Line == 0 && firstEntry.Fields.Pass.LocationRange.Start.Character == 37) { + t.Errorf("Expected pass start to be 0:37, got %v", firstEntry.Fields.Pass.LocationRange.Start) + } +}