use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DiscoverError {
#[error("html parse failed: {0}. Fix: verify the target URL is returning well-formed HTML.")]
Parse(String),
#[error("no login form in page. Fix: ensure the page contains a `<form>` with standard login fields.")]
NoForm,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LoginFlowError {
#[error(
"form discovery failed: {0}. Fix: verify the target URL hosts a supported login form."
)]
Discover(#[from] DiscoverError),
#[error(
"browser login drive failed: {0}. Fix: verify credentials and form selectors are correct."
)]
Drive(#[from] DriveError),
#[error("session capture failed: {0}. Fix: check if the site issues cookies or custom headers for auth.")]
Capture(#[from] CaptureError),
#[error("login verification failed: {0}. Fix: ensure the canary URL is protected and returns expected markers.")]
Verify(#[from] VerifyError),
#[error("http login failed: {0}. Fix: verify HTTP-only form submission parameters.")]
Http(#[from] HttpLoginError),
#[error("no login form discovered at {url}. Fix: ensure the URL is the exact sign-in page.")]
NoLoginForm {
url: String,
},
#[error("loginflow builder: {0}. Fix: correct the builder configuration.")]
Build(#[from] BuildError),
#[error("target URL has no host. Fix: use an absolute URL with a host, e.g. https://example.com/login.")]
MissingHost,
#[error("mfa required but credentials have no mfa_source. Fix: provide an MFA source in credentials.")]
MfaRequired,
#[error("mfa: {0}. Fix: verify the MFA source is accessible and correct.")]
Mfa(#[from] MfaError),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DriveError {
#[error("headless: {0}. Fix: verify chromium is installed and launchable.")]
Headless(String),
#[error("oauth redirect: {0}. Fix: verify the OAuth chain is not broken or looping.")]
OAuth(String),
#[error("field fill failed for {field}: {details}. Fix: verify the field is visible and not disabled.")]
FieldFill {
field: String,
details: String,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CaptureError {
#[error("authjar: {0}. Fix: verify the captured tokens are well-formed.")]
AuthJar(#[from] authjar::AuthJarError),
#[error("empty session after login. Fix: check if the site uses a non-standard session storage mechanism.")]
EmptySession,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VerifyError {
#[error("canary request failed: {0}. Fix: verify the canary URL is reachable.")]
Request(String),
#[error("canary status {actual}, expected one of {expected:?}. Fix: verify the canary status criteria.")]
Status {
actual: u16,
expected: Vec<u16>,
},
#[error("canary body missing markers: {missing:?}. Fix: verify the markers are correct for a logged-in state.")]
BodyMarkers {
missing: Vec<String>,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum HttpLoginError {
#[error("{0}. Fix: verify the network is reachable and URL is valid.")]
Transport(String),
#[error("target URL has no host. Fix: use an absolute URL with a host.")]
MissingHost,
#[error("login succeeded but no session cookies were set. Fix: the endpoint may not support HTTP-only login without JS.")]
NoCookies,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum MfaError {
#[error("totp: {0}. Fix: verify the TOTP secret is base32 encoded.")]
Totp(String),
#[error("email: {0}. Fix: verify the email polling configuration.")]
Email(String),
#[error("empty mfa response. Fix: ensure the MFA source is supplying a valid code.")]
Empty,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum BuildError {
#[error("browser feature disabled; enable `browser` or use HTTP-only targets. Fix: build with `--features browser`.")]
BrowserUnavailable,
}