Files
MSE-PI-E2EEDA-Plein-de-eeee…/db/src/mapping.go
Klagarge c34ec94a6b feat(db): get mapping dynamically from file
Assisted-by: Junie:claude-sonnet-4.6
Signed-off-by: Klagarge <remi@heredero.ch>
2026-06-04 14:46:34 +02:00

153 lines
3.9 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
)
// mappingFile is the structure of the JSON config file.
// Campus names map to the list of gateway IDs that belong to them.
// Room names map to the list of node IDs that belong to them.
type mappingFile struct {
Campus map[string][]string `json:"campus"`
Room map[string][]string `json:"room"`
}
// MappingConfig holds the reverse lookup maps built from the config file:
//
// gateway_id -> campus name
// node_id -> room name
type MappingConfig struct {
gatewayToCampus map[string]string
nodeToRoom map[string]string
}
// DynamicMapping reloads the mapping file on every access so that changes
// to the JSON file take effect without restarting the programme.
type DynamicMapping struct {
path string
}
// NewDynamicMapping creates a DynamicMapping that reads from the given path.
func NewDynamicMapping(path string) *DynamicMapping {
return &DynamicMapping{path: path}
}
func (d *DynamicMapping) load() *MappingConfig {
cfg, err := LoadMapping(d.path)
if err != nil {
return EmptyMapping()
}
return cfg
}
func (d *DynamicMapping) GetCampus(gatewayID string) (string, bool) {
return d.load().GetCampus(gatewayID)
}
func (d *DynamicMapping) GetRoom(nodeID string) (string, bool) {
return d.load().GetRoom(nodeID)
}
func (d *DynamicMapping) NodesForRoom(room string) []string {
return d.load().NodesForRoom(room)
}
func (d *DynamicMapping) AllNodes() []string {
return d.load().AllNodes()
}
func (d *DynamicMapping) Rooms() []string {
return d.load().Rooms()
}
// EmptyMapping returns a MappingConfig with no entries.
// GetCampus and GetRoom will return their "unknown_*" fallback values.
func EmptyMapping() *MappingConfig {
return &MappingConfig{
gatewayToCampus: make(map[string]string),
nodeToRoom: make(map[string]string),
}
}
// LoadMapping reads the mapping configuration from a JSON file and builds
// the internal reverse-lookup tables.
func LoadMapping(path string) (*MappingConfig, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read mapping file %q: %w", path, err)
}
var raw mappingFile
if err := json.Unmarshal(data, &raw); err != nil {
return nil, fmt.Errorf("failed to parse mapping JSON: %w", err)
}
cfg := &MappingConfig{
gatewayToCampus: make(map[string]string),
nodeToRoom: make(map[string]string),
}
for campus, gateways := range raw.Campus {
for _, gwID := range gateways {
cfg.gatewayToCampus[gwID] = campus
}
}
for room, nodes := range raw.Room {
for _, nodeID := range nodes {
cfg.nodeToRoom[nodeID] = room
}
}
return cfg, nil
}
// GetCampus returns the campus name for a given gateway ID.
// The boolean is false if the gateway ID has no mapping.
func (c *MappingConfig) GetCampus(gatewayID string) (string, bool) {
campus, ok := c.gatewayToCampus[gatewayID]
return campus, ok
}
// GetRoom returns the room name for a given node ID.
// The boolean is false if the node ID has no mapping.
func (c *MappingConfig) GetRoom(nodeID string) (string, bool) {
room, ok := c.nodeToRoom[nodeID]
return room, ok
}
// NodesForRoom returns the list of node IDs that belong to the given room.
func (c *MappingConfig) NodesForRoom(room string) []string {
var nodes []string
for nodeID, r := range c.nodeToRoom {
if r == room {
nodes = append(nodes, nodeID)
}
}
return nodes
}
// AllNodes returns all node IDs defined in the mapping.
func (c *MappingConfig) AllNodes() []string {
nodes := make([]string, 0, len(c.nodeToRoom))
for nodeID := range c.nodeToRoom {
nodes = append(nodes, nodeID)
}
return nodes
}
// Rooms returns the list of all room names defined in the mapping.
func (c *MappingConfig) Rooms() []string {
seen := make(map[string]struct{})
for _, room := range c.nodeToRoom {
seen[room] = struct{}{}
}
rooms := make([]string, 0, len(seen))
for room := range seen {
rooms = append(rooms, room)
}
return rooms
}