use std::time::Duration;
use ff_rdp_core::{
ActorId, ProtocolError, RdpConnection, RdpTransport, RootActor, TabActor, TargetInfo,
};
use crate::cli::args::Cli;
use crate::daemon::client::{ConnectionTarget, resolve_connection_target};
use crate::error::AppError;
pub struct ConnectedTab {
connection: RdpConnection,
pub(crate) target: TargetInfo,
tab_actor: ActorId,
pub(crate) via_daemon: bool,
}
pub fn connect_and_get_target(cli: &Cli) -> Result<ConnectedTab, AppError> {
let target = resolve_connection_target(&cli.host, cli.port, cli.daemon_timeout, cli.no_daemon);
let (connect_host, connect_port, via_daemon, deferred_warning) = match target {
ConnectionTarget::Daemon { port } => ("127.0.0.1".to_owned(), port, true, None),
ConnectionTarget::Direct { deferred_warning } => {
(cli.host.clone(), cli.port, false, deferred_warning)
}
};
let connection = connect_to_firefox(&connect_host, connect_port, cli, via_daemon)
.inspect_err(|_| {
if let Some(w) = &deferred_warning {
eprintln!("{w}");
}
})?;
handshake_and_resolve_tab(connection, cli, via_daemon)
}
pub fn connect_direct(cli: &Cli) -> Result<ConnectedTab, AppError> {
let connection = connect_to_firefox(&cli.host, cli.port, cli, false)?;
handshake_and_resolve_tab(connection, cli, false)
}
fn connect_to_firefox(
host: &str,
port: u16,
cli: &Cli,
via_daemon: bool,
) -> Result<RdpConnection, AppError> {
RdpConnection::connect(host, port, Duration::from_millis(cli.timeout)).map_err(|e| match e {
ProtocolError::ConnectionFailed(_) | ProtocolError::Timeout if !via_daemon => {
AppError::User(format!(
"could not connect to Firefox at {}:{} — is Firefox running with --start-debugger-server {}?\n\
hint: run `ff-rdp doctor` for a full diagnostic, or `ff-rdp launch` to start Firefox with debugging enabled.",
cli.host, cli.port, cli.port
))
}
ProtocolError::ConnectionFailed(_) | ProtocolError::Timeout if via_daemon => {
AppError::User(format!(
"could not connect to daemon on port {port} — try --no-daemon to connect directly to Firefox.\n\
hint: run `ff-rdp doctor` to inspect daemon health."
))
}
other => AppError::from(other),
})
}
fn handshake_and_resolve_tab(
mut connection: RdpConnection,
cli: &Cli,
via_daemon: bool,
) -> Result<ConnectedTab, AppError> {
connection.warn_if_version_unsupported();
crate::connection_meta::remember_version(connection.firefox_version());
let tabs = RootActor::list_tabs(connection.transport_mut()).map_err(AppError::from)?;
let tab = crate::tab_target::resolve_tab_with_context(
&tabs,
cli.tab.as_deref(),
cli.tab_id.as_deref(),
&cli.host,
cli.port,
)?;
let tab_actor = tab.actor.clone();
let target_info =
TabActor::get_target(connection.transport_mut(), &tab_actor).map_err(AppError::from)?;
Ok(ConnectedTab {
connection,
target: target_info,
tab_actor,
via_daemon,
})
}
impl ConnectedTab {
pub fn transport_mut(&mut self) -> &mut RdpTransport {
self.connection.transport_mut()
}
pub fn target_tab_actor(&self) -> &ActorId {
&self.tab_actor
}
}