mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 15:05:28 +02:00
refactor(server): Improve Wireguard config; Improve the parser
Signed-off-by: Myzel394 <github.7a2op@simplelogin.co>
This commit is contained in:
parent
bf05d07fc9
commit
ba056d6ae9
212
server/handlers/wireguard/ast/parser.go
Normal file
212
server/handlers/wireguard/ast/parser.go
Normal file
@ -0,0 +1,212 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
"config-lsp/utils"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewWGConfig() *WGConfig {
|
||||
config := &WGConfig{}
|
||||
config.Clear()
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *WGConfig) Clear() {
|
||||
c.Sections = make([]*WGSection, 0, 2)
|
||||
c.CommentLines = make(map[uint32]struct{})
|
||||
}
|
||||
|
||||
var commentPattern = regexp.MustCompile(`^\s*([;#])`)
|
||||
var emptyPattern = regexp.MustCompile(`^\s*$`)
|
||||
var headerPattern = regexp.MustCompile(`^\s*\[(\w+)]?`)
|
||||
var linePattern = regexp.MustCompile(`^\s*(?P<key>.+?)\s*(?P<separator>=)\s*(?P<value>\S.*?)?\s*(?:[;#].*)?\s*$`)
|
||||
|
||||
func (c *WGConfig) Parse(input string) []common.LSPError {
|
||||
errors := make([]common.LSPError, 0)
|
||||
lines := utils.SplitIntoLines(input)
|
||||
|
||||
var currentSection *WGSection
|
||||
|
||||
for rawLineNumber, line := range lines {
|
||||
lineNumber := uint32(rawLineNumber)
|
||||
|
||||
if emptyPattern.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
|
||||
if commentPattern.MatchString(line) {
|
||||
c.CommentLines[lineNumber] = struct{}{}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if headerPattern.MatchString(line) {
|
||||
name := headerPattern.FindStringSubmatch(line)[1]
|
||||
|
||||
currentSection = &WGSection{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: 0,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(len(line)) + 1,
|
||||
},
|
||||
},
|
||||
Header: WGHeader{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: 0,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(len(line)) + 1,
|
||||
},
|
||||
},
|
||||
Name: name,
|
||||
},
|
||||
Properties: make(map[uint32]*WGProperty),
|
||||
}
|
||||
|
||||
c.Sections = append(c.Sections, currentSection)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Else property
|
||||
if currentSection == nil {
|
||||
// Root properties are not allowed
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: 0,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(len(line)),
|
||||
},
|
||||
},
|
||||
Err: fmt.Errorf("A header is missing before a property. This property has no header above it."),
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.Contains(line, "=") {
|
||||
// Incomplete property
|
||||
indexes := utils.GetTrimIndex(line)
|
||||
|
||||
currentSection.Properties[lineNumber] = &WGProperty{
|
||||
Key: WGPropertyKey{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(indexes[0]),
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(indexes[1]),
|
||||
},
|
||||
},
|
||||
Name: line[indexes[0]:indexes[1]],
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Fully written out property
|
||||
|
||||
indexes := linePattern.FindStringSubmatchIndex(line)
|
||||
|
||||
if indexes == nil || len(indexes) == 0 {
|
||||
// Error
|
||||
errors = append(errors, common.LSPError{
|
||||
Range: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: 0,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: uint32(len(line)),
|
||||
},
|
||||
},
|
||||
Err: fmt.Errorf("This property seems to be malformed"),
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Construct key
|
||||
keyStart := uint32(indexes[2])
|
||||
keyEnd := uint32(indexes[3])
|
||||
key := WGPropertyKey{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: keyStart,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: keyEnd,
|
||||
},
|
||||
},
|
||||
Name: line[keyStart:keyEnd],
|
||||
}
|
||||
|
||||
// Construct separator
|
||||
separatorStart := uint32(indexes[4])
|
||||
separatorEnd := uint32(indexes[5])
|
||||
separator := WGPropertySeparator{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: separatorStart,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: separatorEnd,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Construct value
|
||||
var value *WGPropertyValue
|
||||
|
||||
if indexes[6] != -1 && indexes[7] != -1 {
|
||||
// value exists
|
||||
valueStart := uint32(indexes[6])
|
||||
valueEnd := uint32(indexes[7])
|
||||
|
||||
value = &WGPropertyValue{
|
||||
LocationRange: common.LocationRange{
|
||||
Start: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: valueStart,
|
||||
},
|
||||
End: common.Location{
|
||||
Line: lineNumber,
|
||||
Character: valueEnd,
|
||||
},
|
||||
},
|
||||
Value: line[valueStart:valueEnd],
|
||||
}
|
||||
}
|
||||
|
||||
// And lastly, add the property
|
||||
currentSection.Properties[lineNumber] = &WGProperty{
|
||||
Key: key,
|
||||
Separator: &separator,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
54
server/handlers/wireguard/ast/parser_test.go
Normal file
54
server/handlers/wireguard/ast/parser_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExample1Works(
|
||||
t *testing.T,
|
||||
) {
|
||||
sample := utils.Dedent(`
|
||||
# A comment at the very top
|
||||
|
||||
|
||||
[Interface]
|
||||
PrivateKey = 1234567890 # Some comment
|
||||
Address = 10.0.0.1
|
||||
|
||||
|
||||
|
||||
[Peer]
|
||||
PublicKey = 1234567890
|
||||
|
||||
; I'm a comment
|
||||
`)
|
||||
|
||||
config := NewWGConfig()
|
||||
|
||||
errors := config.Parse(sample)
|
||||
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("Parse: Expected no errors, but got %v", errors)
|
||||
}
|
||||
|
||||
if !(utils.KeyExists(config.CommentLines, 0) && utils.KeyExists(config.CommentLines, 12)) {
|
||||
t.Errorf("Parse: Expected comments to be present on lines 0 and 12")
|
||||
}
|
||||
|
||||
if !(config.Sections[0].Header.Name == "Interface" && config.Sections[1].Header.Name == "Peer") {
|
||||
t.Errorf("Parse: Expected sections to be present on lines 0, 1, and 2")
|
||||
}
|
||||
|
||||
if !(config.Sections[0].Properties[4].Key.Name == "PrivateKey" && config.Sections[0].Properties[4].Value.Value == "1234567890") {
|
||||
t.Errorf("Parse: Expected property line 4 to be correct")
|
||||
}
|
||||
|
||||
if !(config.Sections[0].Properties[5].Key.Name == "Address" && config.Sections[0].Properties[5].Value.Value == "10.0.0.1") {
|
||||
t.Errorf("Parse: Expected property line 5 to be correct")
|
||||
}
|
||||
|
||||
if !(config.Sections[1].Properties[10].Key.Name == "PublicKey" && config.Sections[1].Properties[10].Value.Value == "1234567890") {
|
||||
t.Errorf("Parse: Expected property line 10 to be correct")
|
||||
}
|
||||
}
|
43
server/handlers/wireguard/ast/wireguard.go
Normal file
43
server/handlers/wireguard/ast/wireguard.go
Normal file
@ -0,0 +1,43 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"config-lsp/common"
|
||||
)
|
||||
|
||||
type WGPropertyKey struct {
|
||||
common.LocationRange
|
||||
Name string
|
||||
}
|
||||
|
||||
type WGPropertyValue struct {
|
||||
common.LocationRange
|
||||
Value string
|
||||
}
|
||||
|
||||
type WGPropertySeparator struct {
|
||||
common.LocationRange
|
||||
}
|
||||
|
||||
type WGProperty struct {
|
||||
Key WGPropertyKey
|
||||
Separator *WGPropertySeparator
|
||||
Value *WGPropertyValue
|
||||
}
|
||||
|
||||
type WGHeader struct {
|
||||
common.LocationRange
|
||||
Name string
|
||||
}
|
||||
|
||||
type WGSection struct {
|
||||
common.LocationRange
|
||||
Header WGHeader
|
||||
// map of: line number -> WGProperty
|
||||
Properties map[uint32]*WGProperty
|
||||
}
|
||||
|
||||
type WGConfig struct {
|
||||
Sections []*WGSection
|
||||
// Used to identify where not to show diagnostics
|
||||
CommentLines map[uint32]struct{}
|
||||
}
|
39
server/handlers/wireguard/ast/wireguard_fields.go
Normal file
39
server/handlers/wireguard/ast/wireguard_fields.go
Normal file
@ -0,0 +1,39 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
func (c *WGConfig) FindSectionByLine(line uint32) *WGSection {
|
||||
index, found := slices.BinarySearchFunc(
|
||||
c.Sections,
|
||||
line,
|
||||
func(current *WGSection, target uint32) int {
|
||||
if target < current.Start.Line {
|
||||
return 1
|
||||
}
|
||||
|
||||
if target > current.Start.Line {
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
},
|
||||
)
|
||||
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Sections[index]
|
||||
}
|
||||
|
||||
func (c *WGConfig) FindPropertyByLine(line uint32) *WGProperty {
|
||||
section := c.FindSectionByLine(line)
|
||||
|
||||
if section == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return section.Properties[line]
|
||||
}
|
1
server/handlers/wireguard/indexes/indexes.go
Normal file
1
server/handlers/wireguard/indexes/indexes.go
Normal file
@ -0,0 +1 @@
|
||||
package indexes
|
1
server/handlers/wireguard/shared.go
Normal file
1
server/handlers/wireguard/shared.go
Normal file
@ -0,0 +1 @@
|
||||
package wireguard
|
Loading…
x
Reference in New Issue
Block a user