142 lines
4.4 KiB
Rust
142 lines
4.4 KiB
Rust
use std::time::Duration;
|
|
|
|
use tracing::{debug, error, info, warn};
|
|
|
|
use crate::browser::{Browser, BrowserError};
|
|
use crate::config::Configuration;
|
|
use crate::marionette::error::ConnectionError;
|
|
use crate::marionette::Marionette;
|
|
use crate::mqtt::{Message, MqttClient, MqttPublisher};
|
|
|
|
#[derive(Debug)]
|
|
pub enum SessionError {
|
|
Browser(BrowserError),
|
|
Io(std::io::Error),
|
|
Connection(ConnectionError),
|
|
InvalidState(String),
|
|
}
|
|
|
|
impl From<BrowserError> for SessionError {
|
|
fn from(e: BrowserError) -> Self {
|
|
Self::Browser(e)
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for SessionError {
|
|
fn from(e: std::io::Error) -> Self {
|
|
Self::Io(e)
|
|
}
|
|
}
|
|
|
|
impl From<ConnectionError> for SessionError {
|
|
fn from(e: ConnectionError) -> Self {
|
|
Self::Connection(e)
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for SessionError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self {
|
|
Self::Browser(e) => write!(f, "Error launching browser: {}", e),
|
|
Self::Io(e) => write!(f, "I/O error: {}", e),
|
|
Self::Connection(e) => write!(f, "Connection error: {}", e),
|
|
Self::InvalidState(e) => write!(f, "Invalid state: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for SessionError {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match self {
|
|
Self::Browser(e) => Some(e),
|
|
Self::Io(e) => Some(e),
|
|
Self::Connection(e) => Some(e),
|
|
Self::InvalidState(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Session {
|
|
config: Configuration,
|
|
browser: Browser,
|
|
marionette: Marionette,
|
|
}
|
|
|
|
impl Session {
|
|
pub async fn begin(config: Configuration) -> Result<Self, SessionError> {
|
|
debug!("Launching Firefox");
|
|
let browser = Browser::launch()?;
|
|
browser.wait_ready().await?;
|
|
debug!("Firefox Marionette is now ready");
|
|
let Some(port) = browser.marionette_port() else {
|
|
return Err(SessionError::InvalidState("No active Marionette port".into()));
|
|
};
|
|
debug!("Connecting to Firefox Marionette on port {}", port);
|
|
let mut marionette = Marionette::connect(("127.0.0.1", port)).await?;
|
|
info!("Successfully connected to Firefox Marionette");
|
|
let ses = marionette.new_session().await?;
|
|
debug!("Started Marionette session {}", ses.session_id);
|
|
Ok(Self {
|
|
config,
|
|
browser,
|
|
marionette,
|
|
})
|
|
}
|
|
|
|
pub async fn run(mut self) {
|
|
let mut client = MqttClient::new(&self.config).unwrap();
|
|
loop {
|
|
if let Err(e) = client.connect().await {
|
|
warn!("Failed to connect to MQTT server: {}", e);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
continue;
|
|
}
|
|
if let Err(e) = client.subscribe().await {
|
|
warn!("Error subscribing to MQTT topics: {}", e);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
let handler = MessageHandler {
|
|
marionette: &mut self.marionette,
|
|
};
|
|
client.run(handler).await;
|
|
}
|
|
}
|
|
|
|
pub struct MessageHandler<'a> {
|
|
marionette: &'a mut Marionette,
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl<'a> crate::mqtt::MessageHandler for MessageHandler<'a> {
|
|
async fn navigate(&mut self, publisher: &MqttPublisher, msg: &Message) {
|
|
let url = msg.payload_str();
|
|
let parts: Vec<&str> = msg.topic().split('/').rev().collect();
|
|
let screen = match parts.get(1) {
|
|
Some(&"") | None => {
|
|
warn!("Invalid navigate request: no screen");
|
|
return;
|
|
}
|
|
Some(s) => s,
|
|
};
|
|
debug!("Handling navigate request: {}", url);
|
|
info!("Navigate screen {} to {}", screen, url);
|
|
if let Err(e) = self.marionette.navigate(url.to_string()).await {
|
|
error!("Failed to navigate: {}", e);
|
|
}
|
|
if let Err(e) = publisher.publish_url(screen, &url).await {
|
|
error!("Failed to publish title: {}", e);
|
|
}
|
|
match self.marionette.get_title().await {
|
|
Ok(t) => {
|
|
if let Err(e) = publisher.publish_title(screen, &t).await {
|
|
error!("Failed to publish title: {}", e);
|
|
}
|
|
}
|
|
Err(e) => error!("Error getting title: {}", e),
|
|
}
|
|
}
|
|
}
|