use crate::error::Result;
use crate::protocol::page::Page;
use crate::server::channel_owner::ChannelOwner;
use std::path::PathBuf;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ScreencastFrame {
pub data: bytes::Bytes,
pub timestamp: Option<f64>,
}
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct ScreencastStartOptions {
pub size: Option<ScreencastSize>,
pub quality: Option<u8>,
pub path: Option<PathBuf>,
}
impl ScreencastStartOptions {
pub fn size(mut self, size: ScreencastSize) -> Self {
self.size = Some(size);
self
}
pub fn quality(mut self, quality: u8) -> Self {
self.quality = Some(quality);
self
}
pub fn path(mut self, path: PathBuf) -> Self {
self.path = Some(path);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScreencastSize {
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ActionPosition {
TopLeft,
Top,
TopRight,
BottomLeft,
Bottom,
BottomRight,
}
impl ActionPosition {
pub(crate) fn as_str(self) -> &'static str {
match self {
ActionPosition::TopLeft => "top-left",
ActionPosition::Top => "top",
ActionPosition::TopRight => "top-right",
ActionPosition::BottomLeft => "bottom-left",
ActionPosition::Bottom => "bottom",
ActionPosition::BottomRight => "bottom-right",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ActionCursor {
None,
Pointer,
}
impl ActionCursor {
pub(crate) fn as_str(self) -> &'static str {
match self {
ActionCursor::None => "none",
ActionCursor::Pointer => "pointer",
}
}
}
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct ShowActionsOptions {
pub duration: Option<f64>,
pub position: Option<ActionPosition>,
pub font_size: Option<i32>,
pub cursor: Option<ActionCursor>,
}
impl ShowActionsOptions {
pub fn duration(mut self, duration: f64) -> Self {
self.duration = Some(duration);
self
}
pub fn position(mut self, position: ActionPosition) -> Self {
self.position = Some(position);
self
}
pub fn font_size(mut self, font_size: i32) -> Self {
self.font_size = Some(font_size);
self
}
pub fn cursor(mut self, cursor: ActionCursor) -> Self {
self.cursor = Some(cursor);
self
}
}
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct ChapterOptions {
pub description: Option<String>,
pub duration: Option<f64>,
}
impl ChapterOptions {
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn duration(mut self, duration: f64) -> Self {
self.duration = Some(duration);
self
}
}
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct ShowOverlayOptions {
pub duration: Option<f64>,
}
impl ShowOverlayOptions {
pub fn duration(mut self, duration: f64) -> Self {
self.duration = Some(duration);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OverlayId(pub String);
#[derive(Clone)]
pub struct Screencast {
page: Page,
}
impl Screencast {
pub(crate) fn new(page: Page) -> Self {
Self { page }
}
#[tracing::instrument(level = "info", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn start(&self, options: ScreencastStartOptions) -> Result<()> {
self.page.screencast_start(options).await
}
#[tracing::instrument(level = "info", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn stop(&self) -> Result<()> {
self.page.screencast_stop().await
}
pub fn on_frame<F, Fut>(&self, handler: F)
where
F: Fn(ScreencastFrame) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = Result<()>> + Send + 'static,
{
self.page.screencast_on_frame(handler);
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn show_actions(&self, options: ShowActionsOptions) -> Result<()> {
self.page.screencast_show_actions(options).await
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn hide_actions(&self) -> Result<()> {
self.page.screencast_hide_actions().await
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid(), title = %title))]
pub async fn show_chapter(&self, title: &str, options: ChapterOptions) -> Result<()> {
self.page.screencast_chapter(title, options).await
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn show_overlay(&self, html: &str, options: ShowOverlayOptions) -> Result<OverlayId> {
self.page.screencast_show_overlay(html, options).await
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid()))]
pub async fn remove_overlay(&self, id: OverlayId) -> Result<()> {
self.page.screencast_remove_overlay(id).await
}
#[tracing::instrument(level = "debug", skip_all, fields(page_guid = %self.page.guid(), visible))]
pub async fn set_overlay_visible(&self, visible: bool) -> Result<()> {
self.page.screencast_set_overlay_visible(visible).await
}
}
#[cfg(test)]
mod options_tests {
use super::{
ActionCursor, ActionPosition, ScreencastSize, ScreencastStartOptions, ShowActionsOptions,
};
#[test]
fn start_builder_sets_quality_and_size() {
let o = ScreencastStartOptions::default()
.quality(80)
.size(ScreencastSize {
width: 1280,
height: 720,
});
assert_eq!(o.quality, Some(80));
assert_eq!(o.size.map(|s| (s.width, s.height)), Some((1280, 720)));
}
#[test]
fn action_cursor_as_str_maps_each_variant() {
assert_eq!(ActionCursor::None.as_str(), "none");
assert_eq!(ActionCursor::Pointer.as_str(), "pointer");
}
#[test]
fn action_position_as_str_maps_each_variant() {
assert_eq!(ActionPosition::TopLeft.as_str(), "top-left");
assert_eq!(ActionPosition::Top.as_str(), "top");
assert_eq!(ActionPosition::TopRight.as_str(), "top-right");
assert_eq!(ActionPosition::BottomLeft.as_str(), "bottom-left");
assert_eq!(ActionPosition::Bottom.as_str(), "bottom");
assert_eq!(ActionPosition::BottomRight.as_str(), "bottom-right");
}
#[test]
fn show_actions_builder_sets_cursor() {
let o = ShowActionsOptions::default().cursor(ActionCursor::Pointer);
assert_eq!(o.cursor, Some(ActionCursor::Pointer));
}
}