-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Open
Labels
area/apiAPIAPIkind/enhancementEnhancements are not bugs or new features but can improve usability or performance.Enhancements are not bugs or new features but can improve usability or performance.
Milestone
Description
Description
Just a quick idea while I was working on the API types; I let ChatGPT write up some possible implementations.
We currently use a string field for MAC-addresses in the API; consider using net.HardwareAddr
with a custom marshal/unmarshal (it doesn't (yet?) implement this itself);
https://go.dev/play/p/0s3R4D4rSlh
package main
import (
"encoding/json"
"fmt"
"net"
)
type HW net.HardwareAddr
func (h HW) MarshalText() ([]byte, error) {
return []byte(net.HardwareAddr(h).String()), nil
}
func (h *HW) UnmarshalText(b []byte) error {
m, err := net.ParseMAC(string(b))
if err != nil {
return err
}
*h = HW(m)
return nil
}
type Ex struct {
MAC HW `json:"mac"`
}
func main() {
var inputs = []string{
`{"mac":"aa:bb:cc:dd:ee:ff"}`, // canonical
`{"mac":"AA:BB:CC:DD:EE:FF"}`, // uppercase
`{"mac":"AA-BB-CC-DD-EE-FF"}`, // hyphens
`{"mac":"aabb.ccdd.eeff"}`, // dotted (Cisco-style)
}
for _, js := range inputs {
var ex Ex
if err := json.Unmarshal([]byte(js), &ex); err != nil {
fmt.Printf("Unmarshal error for %q: %v\n", js, err)
continue
}
mac := net.HardwareAddr(ex.MAC)
out, _ := json.Marshal(ex)
fmt.Printf("Input: %-28s → Parsed: %-17s → Marshaled: %s\n",
js, mac, out)
}
}
// Input: {"mac":"aa:bb:cc:dd:ee:ff"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aa:bb:cc:dd:ee:ff"}
// Input: {"mac":"AA:BB:CC:DD:EE:FF"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aa:bb:cc:dd:ee:ff"}
// Input: {"mac":"AA-BB-CC-DD-EE-FF"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aa:bb:cc:dd:ee:ff"}
// Input: {"mac":"aabb.ccdd.eeff"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aa:bb:cc:dd:ee:ff"}
If we do want to preserve the user's input format, we can use something like;
https://go.dev/play/p/S3XgdZSGLpY
package main
import (
"encoding/json"
"fmt"
"net"
)
// MAC preserves the original user input formatting.
type MAC struct {
hw net.HardwareAddr
input string
}
func (m MAC) MarshalText() ([]byte, error) {
if m.input != "" {
return []byte(m.input), nil // emit exactly what user provided
}
return []byte(m.hw.String()), nil
}
func (m *MAC) UnmarshalText(b []byte) error {
hw, err := net.ParseMAC(string(b))
if err != nil {
return err
}
m.hw = hw
m.input = string(b)
return nil
}
type Ex struct {
MAC MAC `json:"mac"`
}
func main() {
var inputs = []string{
`{"mac":"aa:bb:cc:dd:ee:ff"}`, // canonical
`{"mac":"AA:BB:CC:DD:EE:FF"}`, // uppercase
`{"mac":"AA-BB-CC-DD-EE-FF"}`, // hyphens
`{"mac":"aabb.ccdd.eeff"}`, // dotted (Cisco-style)
}
for _, js := range inputs {
var ex Ex
if err := json.Unmarshal([]byte(js), &ex); err != nil {
fmt.Printf("Unmarshal error for %q: %v\n", js, err)
continue
}
mac := ex.MAC.hw // normalized internal form
out, _ := json.Marshal(ex)
fmt.Printf("Input: %-28s → Parsed: %-17s → Marshaled: %s\n",
js, mac, out)
}
}
// Input: {"mac":"aa:bb:cc:dd:ee:ff"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aa:bb:cc:dd:ee:ff"}
// Input: {"mac":"AA:BB:CC:DD:EE:FF"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"AA:BB:CC:DD:EE:FF"}
// Input: {"mac":"AA-BB-CC-DD-EE-FF"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"AA-BB-CC-DD-EE-FF"}
// Input: {"mac":"aabb.ccdd.eeff"} → Parsed: aa:bb:cc:dd:ee:ff → Marshaled: {"mac":"aabb.ccdd.eeff"}
Metadata
Metadata
Assignees
Labels
area/apiAPIAPIkind/enhancementEnhancements are not bugs or new features but can improve usability or performance.Enhancements are not bugs or new features but can improve usability or performance.