Skip to content

Commit 5a2b9d1

Browse files
committed
transaction: Define TransactionError struct to provide more detailed error
If the transaction fails during start, there's no way to get the error detail in a programmatic way, so let's inherit from error to allow more per-type checks.
1 parent 854bf03 commit 5a2b9d1

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

transaction.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,10 @@ func StartFunc(service, user string, handler func(Style, string) (string, error)
238238
// transaction provides an interface to the remainder of the API.
239239
func StartConfDir(service, user string, handler ConversationHandler, confDir string) (*Transaction, error) {
240240
if !CheckPamHasStartConfdir() {
241-
return nil, errors.New("StartConfDir() was used, but the pam version on the system is not recent enough")
241+
return nil, &TransactionError{
242+
errors.New("StartConfDir() was used, but the pam version on the system is not recent enough"),
243+
SystemErr,
244+
}
242245
}
243246

244247
return start(service, user, handler, confDir)
@@ -248,7 +251,10 @@ func start(service, user string, handler ConversationHandler, confDir string) (*
248251
switch handler.(type) {
249252
case BinaryConversationHandler:
250253
if !CheckPamHasBinaryProtocol() {
251-
return nil, errors.New("BinaryConversationHandler() was used, but it is not supported by this platform")
254+
return nil, &TransactionError{
255+
errors.New("BinaryConversationHandler() was used, but it is not supported by this platform"),
256+
SystemErr,
257+
}
252258
}
253259
}
254260
t := &Transaction{
@@ -272,11 +278,34 @@ func start(service, user string, handler ConversationHandler, confDir string) (*
272278
t.status = C.pam_start_confdir(s, u, t.conv, c, &t.handle)
273279
}
274280
if t.status != Success {
275-
return nil, t
281+
return nil, &TransactionError{t, ReturnType(t.status)}
276282
}
277283
return t, nil
278284
}
279285

286+
// transactionError is a private interface that is implemented by both
287+
// TransactionError and Transaction
288+
type transactionError interface {
289+
error
290+
Status() ReturnType
291+
}
292+
293+
// TransactionError extends error to provide more detailed information
294+
type TransactionError struct {
295+
error
296+
status ReturnType
297+
}
298+
299+
// Status exposes the ReturnType for the error
300+
func (e *TransactionError) Status() ReturnType {
301+
return ReturnType(e.status)
302+
}
303+
304+
// Error pretty prints the error from the status message
305+
func (e *TransactionError) Error() string {
306+
return errors.Join(e.error, ReturnType(e.status)).Error()
307+
}
308+
280309
func (t *Transaction) Error() string {
281310
return t.Status().Error()
282311
}

transaction_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ func TestPAM_ConfDir_FailNoServiceOrUnsupported(t *testing.T) {
222222
if len(s) == 0 {
223223
t.Fatalf("error #expected an error message")
224224
}
225+
switch et := err.(type) {
226+
case transactionError:
227+
if et.Status() != Abort {
228+
t.Fatalf("error #unexpected status: %v", et.Status())
229+
}
230+
default:
231+
t.Fatalf("error #unexpected type: %v", et)
232+
}
225233
}
226234

227235
func TestPAM_ConfDir_InfoMessage(t *testing.T) {

0 commit comments

Comments
 (0)