package connection import ( "fmt" "strings" ) // Address represents a parsed agent or rig address. // Format: [machine:]rig[/polecat] // // Examples: // - "gastown/rictus" -> local machine, gastown rig, rictus polecat // - "vm:gastown/rictus" -> vm machine, gastown rig, rictus polecat // - "gastown/" -> local machine, gastown rig, broadcast // - "vm:gastown/" -> vm machine, gastown rig, broadcast type Address struct { Machine string // Machine name (empty = local) Rig string // Rig name (required) Polecat string // Polecat name (empty = broadcast to rig) } // ParseAddress parses an address string into its components. // Valid formats: // - rig/polecat // - rig/ // - machine:rig/polecat // - machine:rig/ func ParseAddress(s string) (*Address, error) { if s == "" { return nil, fmt.Errorf("empty address") } addr := &Address{} // Check for machine prefix (machine:) if idx := strings.Index(s, ":"); idx >= 0 { addr.Machine = s[:idx] s = s[idx+1:] if addr.Machine == "" { return nil, fmt.Errorf("empty machine name before ':'") } } // Parse rig/polecat parts := strings.SplitN(s, "/", 2) if len(parts) < 1 || parts[0] == "" { return nil, fmt.Errorf("missing rig name in address") } addr.Rig = parts[0] if len(parts) == 2 { addr.Polecat = parts[1] // May be empty for broadcast } return addr, nil } // String returns the address in canonical form. func (a *Address) String() string { var sb strings.Builder if a.Machine != "" { sb.WriteString(a.Machine) sb.WriteString(":") } sb.WriteString(a.Rig) sb.WriteString("/") if a.Polecat != "" { sb.WriteString(a.Polecat) } return sb.String() } // IsLocal returns true if the address targets the local machine. func (a *Address) IsLocal() bool { return a.Machine == "" || a.Machine == "local" } // IsBroadcast returns true if the address targets a rig (no specific polecat). func (a *Address) IsBroadcast() bool { return a.Polecat == "" } // RigPath returns the rig/polecat portion without machine prefix. func (a *Address) RigPath() string { if a.Polecat != "" { return a.Rig + "/" + a.Polecat } return a.Rig + "/" } // Validate checks if the address is valid against the registry. // Returns nil if valid, otherwise an error describing the issue. func (a *Address) Validate(registry *MachineRegistry) error { // Check machine exists (if specified) if a.Machine != "" && a.Machine != "local" { if _, err := registry.Get(a.Machine); err != nil { return fmt.Errorf("unknown machine: %s", a.Machine) } } // Rig validation would require connection to target machine // to check if rig exists - defer to caller for now return nil } // Equal returns true if two addresses are equivalent. func (a *Address) Equal(other *Address) bool { if other == nil { return false } // Normalize local machine comparisons m1, m2 := a.Machine, other.Machine if m1 == "" || m1 == "local" { m1 = "local" } if m2 == "" || m2 == "local" { m2 = "local" } return m1 == m2 && a.Rig == other.Rig && a.Polecat == other.Polecat } // MustParseAddress parses an address and panics on error. // Only use for known-good addresses (e.g., constants). func MustParseAddress(s string) *Address { addr, err := ParseAddress(s) if err != nil { panic(fmt.Sprintf("invalid address %q: %v", s, err)) } return addr }