mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-19 07:25:27 +02:00
feat(wireguard): Add Wireguard parser
This commit is contained in:
parent
e8fe8d1a96
commit
2db9371fd0
12
handlers/wireguard/errors.go
Normal file
12
handlers/wireguard/errors.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
type malformedLineError struct{}
|
||||||
|
|
||||||
|
func (e *malformedLineError) Error() string {
|
||||||
|
return "Malformed line"
|
||||||
|
}
|
||||||
|
|
||||||
|
type lineError struct {
|
||||||
|
Line uint32
|
||||||
|
Err error
|
||||||
|
}
|
117
handlers/wireguard/parser.go
Normal file
117
handlers/wireguard/parser.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commentPattern = regexp.MustCompile(`^\s*(;|#)`)
|
||||||
|
var emptyLinePattern = regexp.MustCompile(`^\s*$`)
|
||||||
|
var headerPattern = regexp.MustCompile(`^\s*\[`)
|
||||||
|
|
||||||
|
type characterLocation struct {
|
||||||
|
Start uint32
|
||||||
|
End uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type wireguardParser struct {
|
||||||
|
Sections []wireguardSection
|
||||||
|
// Used to identify where not to show diagnostics
|
||||||
|
CommentLines []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type lineType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LineTypeComment lineType = "comment"
|
||||||
|
LineTypeEmpty lineType = "empty"
|
||||||
|
LineTypeHeader lineType = "header"
|
||||||
|
LineTypeProperty lineType = "property"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getLineType(line string) lineType {
|
||||||
|
if commentPattern.MatchString(line) {
|
||||||
|
return LineTypeComment
|
||||||
|
}
|
||||||
|
|
||||||
|
if emptyLinePattern.MatchString(line) {
|
||||||
|
return LineTypeEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if headerPattern.MatchString(line) {
|
||||||
|
return LineTypeHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
return LineTypeProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *wireguardParser) parseFromString(input string) []lineError {
|
||||||
|
errors := []lineError{}
|
||||||
|
lines := strings.Split(
|
||||||
|
input,
|
||||||
|
"\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
slices.Reverse(lines)
|
||||||
|
|
||||||
|
collectedProperties := wireguardProperties{}
|
||||||
|
var lastPropertyLine *uint32
|
||||||
|
|
||||||
|
for index, line := range lines {
|
||||||
|
currentLineNumber := uint32(len(lines) - index - 1)
|
||||||
|
lineType := getLineType(line)
|
||||||
|
|
||||||
|
switch lineType {
|
||||||
|
case LineTypeComment:
|
||||||
|
p.CommentLines = append(p.CommentLines, currentLineNumber)
|
||||||
|
|
||||||
|
case LineTypeEmpty:
|
||||||
|
continue
|
||||||
|
|
||||||
|
case LineTypeProperty:
|
||||||
|
err := collectedProperties.AddLine(currentLineNumber, line)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, lineError{
|
||||||
|
Line: currentLineNumber,
|
||||||
|
Err: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastPropertyLine == nil {
|
||||||
|
lastPropertyLine = ¤tLineNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
case LineTypeHeader:
|
||||||
|
var lastLine uint32
|
||||||
|
|
||||||
|
if lastPropertyLine == nil {
|
||||||
|
// Current line
|
||||||
|
lastLine = currentLineNumber
|
||||||
|
} else {
|
||||||
|
lastLine = *lastPropertyLine
|
||||||
|
}
|
||||||
|
|
||||||
|
section := createWireguardSection(
|
||||||
|
currentLineNumber,
|
||||||
|
lastLine,
|
||||||
|
line,
|
||||||
|
collectedProperties,
|
||||||
|
)
|
||||||
|
p.Sections = append(p.Sections, section)
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
collectedProperties = wireguardProperties{}
|
||||||
|
lastPropertyLine = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we parse the content from bottom to top,
|
||||||
|
// we need to reverse the order
|
||||||
|
slices.Reverse(p.CommentLines)
|
||||||
|
slices.Reverse(p.Sections)
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
255
handlers/wireguard/parser_test.go
Normal file
255
handlers/wireguard/parser_test.go
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dedent(s string) string {
|
||||||
|
return strings.TrimLeft(s, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidWildTestWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = 1234567890
|
||||||
|
Address = 192.168.1.0/24
|
||||||
|
|
||||||
|
# I'm a comment
|
||||||
|
[Peer]
|
||||||
|
PublicKey = 1234567890
|
||||||
|
Endpoint = 1.2.3.4 ; I'm just a comment
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = 5555
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.CommentLines) == 1 && parser.CommentLines[0] == 4) {
|
||||||
|
t.Fatalf("parseFromString failed to collect comment lines %v", parser.CommentLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections) == 3) && (*parser.Sections[0].Name == "Interface") && (*parser.Sections[1].Name == "Peer") && (*parser.Sections[2].Name == "Peer")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parser.Sections[0].StartLine == 0 && parser.Sections[0].EndLine == 2 && parser.Sections[1].StartLine == 5 && parser.Sections[1].EndLine == 7 && parser.Sections[2].StartLine == 9 && parser.Sections[2].EndLine == 10) {
|
||||||
|
t.Fatalf("parseFromString: Invalid start and end lines %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections[0].Properties) == 2) && (len(parser.Sections[1].Properties) == 2) && (len(parser.Sections[2].Properties) == 1)) {
|
||||||
|
t.Fatalf("parseFromString: Invalid amount of properties %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((parser.Sections[0].Properties[1].Key.Name == "PrivateKey") && (parser.Sections[0].Properties[2].Key.Name == "Address")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties of section 0 %v", parser.Sections[0].Properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((parser.Sections[1].Properties[6].Key.Name == "PublicKey") && (parser.Sections[1].Properties[7].Key.Name == "Endpoint")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties of section 1 %v", parser.Sections[1].Properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parser.Sections[2].Properties[10].Key.Name == "PublicKey") {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties of section 2 %v", parser.Sections[2].Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptySectionAtStartWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Interface]
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = 1234567890
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Interface") && (*parser.Sections[1].Name == "Peer")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections[0].Properties) == 0 && len(parser.Sections[1].Properties) == 1) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties %v", parser.Sections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptySectionAtEndWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Inteface]
|
||||||
|
PrivateKey = 1234567890
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
# Just sneaking in here, hehe
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inteface") && (*parser.Sections[1].Name == "Peer")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections[0].Properties) == 1 && len(parser.Sections[1].Properties) == 0) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.CommentLines) == 1 && parser.CommentLines[0] == 4) {
|
||||||
|
t.Fatalf("parseFromString failed to collect comment lines %v", parser.CommentLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyFileWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections) == 0) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections %v", parser.Sections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartialSectionWithNoPropertiesWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Inte
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = 1234567890
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inte") && (*parser.Sections[1].Name == "Peer")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections[0].Properties) == 0 && len(parser.Sections[1].Properties) == 1) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.CommentLines) == 0) {
|
||||||
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parser.Sections[1].Properties[3].Key.Name == "PublicKey") {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties of section 1: %v", parser.Sections[1].Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartialSectionWithPropertiesWorksFine(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Inte
|
||||||
|
PrivateKey = 1234567890
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
`)
|
||||||
|
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !((len(parser.Sections) == 2) && (*parser.Sections[0].Name == "Inte") && (*parser.Sections[1].Name == "Peer")) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections[0].Properties) == 1 && len(parser.Sections[1].Properties) == 0) {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parser.Sections[0].Properties[1].Key.Name == "PrivateKey") {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties of section 0: %v", parser.Sections[0].Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileWithOnlyComments(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
# This is a comment
|
||||||
|
# Another comment
|
||||||
|
`)
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections) == 0) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.CommentLines) == 2) {
|
||||||
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(parser.CommentLines[0] == 0 && parser.CommentLines[1] == 1) {
|
||||||
|
t.Fatalf("parseFromString failed to collect comment lines: %v", parser.CommentLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleSectionsNoProperties(
|
||||||
|
t *testing.T,
|
||||||
|
) {
|
||||||
|
sample := dedent(`
|
||||||
|
[Interface]
|
||||||
|
[Peer]
|
||||||
|
[Peer]
|
||||||
|
`)
|
||||||
|
parser := wireguardParser{}
|
||||||
|
errors := parser.parseFromString(sample)
|
||||||
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
t.Fatalf("parseFromString failed with error: %v", errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(len(parser.Sections) == 3) {
|
||||||
|
t.Fatalf("parseFromString failed to collect sections: %v", parser.Sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, section := range parser.Sections {
|
||||||
|
if len(section.Properties) != 0 {
|
||||||
|
t.Fatalf("parseFromString failed to collect properties: %v", section.Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
handlers/wireguard/wg-property.go
Normal file
119
handlers/wireguard/wg-property.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/utils"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var linePattern = regexp.MustCompile(`^\s*(?P<key>.+?)\s*(?P<separator>=)\s*(?P<value>\S.*?)?\s*(?:(?:;|#).*)?\s*$`)
|
||||||
|
|
||||||
|
type wireguardPropertyKey struct {
|
||||||
|
Location characterLocation
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type wireguardPropertyValue struct {
|
||||||
|
Location characterLocation
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type wireguardPropertySeparator struct {
|
||||||
|
Location characterLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
type wireguardProperty struct {
|
||||||
|
Key wireguardPropertyKey
|
||||||
|
Separator *wireguardPropertySeparator
|
||||||
|
Value *wireguardPropertyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p wireguardProperty) String() string {
|
||||||
|
if p.Value == nil {
|
||||||
|
return p.Key.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.Key.Name + "=" + p.Value.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWireguardProperty(line string) (*wireguardProperty, error) {
|
||||||
|
if !strings.Contains(line, "=") {
|
||||||
|
indexes := utils.GetTrimIndex(line)
|
||||||
|
|
||||||
|
if indexes == nil {
|
||||||
|
// weird, should not happen
|
||||||
|
return nil, &malformedLineError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wireguardProperty{
|
||||||
|
Key: wireguardPropertyKey{
|
||||||
|
Location: characterLocation{
|
||||||
|
Start: uint32(indexes[0]),
|
||||||
|
End: uint32(indexes[1]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
indexes := linePattern.FindStringSubmatchIndex(line)
|
||||||
|
|
||||||
|
if indexes == nil || len(indexes) == 0 {
|
||||||
|
return nil, &malformedLineError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyStart := uint32(indexes[2])
|
||||||
|
keyEnd := uint32(indexes[3])
|
||||||
|
key := wireguardPropertyKey{
|
||||||
|
Location: characterLocation{
|
||||||
|
Start: keyStart,
|
||||||
|
End: keyEnd,
|
||||||
|
},
|
||||||
|
Name: line[keyStart:keyEnd],
|
||||||
|
}
|
||||||
|
|
||||||
|
separatorStart := uint32(indexes[4])
|
||||||
|
separatorEnd := uint32(indexes[5])
|
||||||
|
separator := wireguardPropertySeparator{
|
||||||
|
Location: characterLocation{
|
||||||
|
Start: separatorStart,
|
||||||
|
End: separatorEnd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var value *wireguardPropertyValue
|
||||||
|
|
||||||
|
if len(indexes) > 6 {
|
||||||
|
// value exists
|
||||||
|
valueStart := uint32(indexes[6])
|
||||||
|
valueEnd := uint32(indexes[7])
|
||||||
|
|
||||||
|
value = &wireguardPropertyValue{
|
||||||
|
Location: characterLocation{
|
||||||
|
Start: valueStart,
|
||||||
|
End: valueEnd,
|
||||||
|
},
|
||||||
|
Value: line[valueStart:valueEnd],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wireguardProperty{
|
||||||
|
Key: key,
|
||||||
|
Separator: &separator,
|
||||||
|
Value: value,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// [<line number>]: <property>
|
||||||
|
type wireguardProperties map[uint32]wireguardProperty
|
||||||
|
|
||||||
|
func (p *wireguardProperties) AddLine(lineNumber uint32, line string) error {
|
||||||
|
property, err := createWireguardProperty(line)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*p)[lineNumber] = *property
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
48
handlers/wireguard/wg-section.go
Normal file
48
handlers/wireguard/wg-section.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wireguardSection struct {
|
||||||
|
StartLine uint32
|
||||||
|
EndLine uint32
|
||||||
|
// nil = do not belong to a section
|
||||||
|
Name *string
|
||||||
|
Properties wireguardProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s wireguardSection) String() string {
|
||||||
|
var name string
|
||||||
|
|
||||||
|
if s.Name == nil {
|
||||||
|
name = "//<nil>//"
|
||||||
|
} else {
|
||||||
|
name = *s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("[%s]; %d-%d: %v", name, s.StartLine, s.EndLine, s.Properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
var validHeaderPattern = regexp.MustCompile(`^\s*\[(?P<header>.+?)\]\s*$`)
|
||||||
|
|
||||||
|
func createWireguardSection(startLine uint32, endLine uint32, headerLine string, props wireguardProperties) wireguardSection {
|
||||||
|
match := validHeaderPattern.FindStringSubmatch(headerLine)
|
||||||
|
|
||||||
|
var header string
|
||||||
|
|
||||||
|
if match == nil {
|
||||||
|
// Still typing it
|
||||||
|
header = headerLine[1:]
|
||||||
|
} else {
|
||||||
|
header = match[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return wireguardSection{
|
||||||
|
StartLine: startLine,
|
||||||
|
EndLine: endLine,
|
||||||
|
Name: &header,
|
||||||
|
Properties: props,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user