Skip to content

Commit

Permalink
Add facility to recognize specific error messages.
Browse files Browse the repository at this point in the history
Also add the invalid user name and password message. This list should
eventually grow as more errors are recognized. It prevents a client from
having to either bubble up literally any text from RouterOS or have to
rebuild all the different error strings themselves.
  • Loading branch information
EliRibble committed Sep 20, 2024
1 parent c7c9b83 commit d30869a
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 1 deletion.
30 changes: 30 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ var (
errAsyncLoopEnded = errors.New("method Async(): loop has ended - probably read error")
)

// A list of sentences we recognize from the router
const (
MsgInvalidUserNameOrPassword = "invalid user name or password (6)"
)

// UnknownReplyError records the sentence whose Word is unknown.
type UnknownReplyError struct {
Sentence *proto.Sentence
Expand All @@ -27,6 +32,19 @@ type DeviceError struct {
Sentence *proto.Sentence
}

// Well-known errors. We could just dynamically create these out of the various
// strings we get back from RouterOS, but by listing them here we capture knowledge
// and experience recognizing the different messages. This makes it easier for clients
// to write good error-handling code.
var (
ErrInvalidAuthentication = errors.New(MsgInvalidUserNameOrPassword)
)

// A mapping between the recognized sentences and error types
var ErrorsByMessage = map[string]error{
MsgInvalidUserNameOrPassword: ErrInvalidAuthentication,
}

func (err *DeviceError) fetchMessage() string {
if m := err.Sentence.Map["message"]; m != "" {
return m
Expand All @@ -38,3 +56,15 @@ func (err *DeviceError) fetchMessage() string {
func (err *DeviceError) Error() string {
return fmt.Sprintf("from RouterOS device: %s", err.fetchMessage())
}

// Given a sentence see if it's a well-known error. If it is, return that error.
// Otherwise produce a new DeviceError with the details of the sentence.
// Users are encouraged to send pull-requests with new error types to make error
// handling by type easier.
func DeviceErrorFromSentence(sen* proto.Sentence) error {
m := sen.Map["message"]
if err, ok := ErrorsByMessage[m]; ok {
return err
}
return &DeviceError{sen}
}
3 changes: 2 additions & 1 deletion reply.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func (r *Reply) String() string {
return sb.String()
}

// Return whether or not we are done processing, and any error detected in the sentence
func (r *Reply) processSentence(sen *proto.Sentence) (bool, error) {
switch sen.Word {
case reSentence:
Expand All @@ -34,7 +35,7 @@ func (r *Reply) processSentence(sen *proto.Sentence) (bool, error) {
r.Done = sen
return true, nil
case trapSentence, fatalSentence:
return sen.Word == fatalSentence, &DeviceError{sen}
return sen.Word == fatalSentence, DeviceErrorFromSentence(sen)
case "":
// API docs say that empty sentences should be ignored
default:
Expand Down

0 comments on commit d30869a

Please sign in to comment.