package garth import ( "errors" "fmt" "time" ) // Unified Token Definitions // OAuth1Token represents OAuth1 credentials type OAuth1Token struct { Token string Secret string } // OAuth2Token represents OAuth2 credentials type OAuth2Token struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` Expiry time.Time `json:"-"` } // IsExpired checks if the token has expired func (t *OAuth2Token) IsExpired() bool { return time.Now().After(t.Expiry) } // Token represents unified authentication credentials type Token struct { OAuth1 *OAuth1Token OAuth2 *OAuth2Token UserProfile *UserProfile Domain string } // IsExpired checks if the OAuth2 token has expired func (t *Token) IsExpired() bool { if t.OAuth2 == nil { return true } return t.OAuth2.IsExpired() } // NeedsRefresh checks if token needs refresh (within 5 min expiry window) func (t *Token) NeedsRefresh() bool { if t.OAuth2 == nil { return true } return time.Now().Add(5 * time.Minute).After(t.OAuth2.Expiry) } // UserProfile represents Garmin user profile information type UserProfile struct { Username string ProfileID string DisplayName string } // ClientOptions contains configuration for the authenticator type ClientOptions struct { Storage TokenStorage TokenURL string Domain string // garmin.com or garmin.cn Timeout time.Duration } // TokenStorage defines the interface for token storage type TokenStorage interface { StoreToken(token *Token) error GetToken() (*Token, error) ClearToken() error } // Error interface defines common error behavior for Garth type Error interface { error GetStatusCode() int GetType() string GetCause() error Unwrap() error } // ErrTokenNotFound is returned when a token is not available in storage var ErrTokenNotFound = errors.New("token not found") // AuthError represents Garmin authentication errors type AuthError struct { StatusCode int `json:"status_code"` // HTTP status code Message string `json:"message"` // Human-readable error message Type string `json:"type"` // Garmin error type identifier Cause error `json:"cause"` // Underlying error } // GetStatusCode returns the HTTP status code func (e *AuthError) GetStatusCode() int { return e.StatusCode } // GetType returns the error category func (e *AuthError) GetType() string { return e.Type } // Error implements the error interface for AuthError func (e *AuthError) Error() string { msg := fmt.Sprintf("garmin auth error %d: %s", e.StatusCode, e.Message) if e.Cause != nil { msg += " (" + e.Cause.Error() + ")" } return msg } // Unwrap returns the underlying error func (e *AuthError) Unwrap() error { return e.Cause } // GetCause returns the underlying error (implements Error interface) func (e *AuthError) GetCause() error { return e.Cause } // APIError represents errors from API operations type APIError struct { StatusCode int // HTTP status code Message string // Error description Cause error // Underlying error ErrorType string // Specific error category } // GetStatusCode returns the HTTP status code func (e *APIError) GetStatusCode() int { return e.StatusCode } // GetType returns the error category func (e *APIError) GetType() string { if e.ErrorType == "" { return "api_error" } return e.ErrorType } // Error implements the error interface for APIError func (e *APIError) Error() string { if e.Cause != nil { return fmt.Sprintf("API error (%d): %s: %v", e.StatusCode, e.Message, e.Cause) } return fmt.Sprintf("API error (%d): %s", e.StatusCode, e.Message) } // Unwrap returns the underlying error func (e *APIError) Unwrap() error { return e.Cause } // GetCause returns the underlying error (implements Error interface) func (e *APIError) GetCause() error { return e.Cause } // AuthenticatorSetter interface for storage that needs authenticator reference type AuthenticatorSetter interface { SetAuthenticator(a Authenticator) }