init v.1.0.0
All checks were successful
CI / Check / Test (push) Successful in 3m40s

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-08-06 20:11:22 +02:00
commit 15fdace324
38 changed files with 2883 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
[package]
name = "application"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
[dependencies]
domain = { workspace = true }
heapless = { version = "0.9.3", default-features = false }
thiserror = { workspace = true }

View File

@@ -0,0 +1,55 @@
use core::fmt::{self, Display, Write};
pub type Message = heapless::String<104, u8>;
#[derive(Debug, thiserror::Error)]
pub enum ApplicationError {
#[error("Failed to initialize display: {0}")]
DisplayInitialization(Message),
#[error("Display event loop failure: {0}")]
EventLoop(Message),
#[error("Render pipeline failure: {0}")]
RenderingFailed(Message),
#[error("Failed to load logo: {0}")]
LogoSource(Message),
#[error("Domain configuration error: {0}")]
DomainError(#[from] domain::DomainError),
}
impl ApplicationError {
pub fn display_initialization(reason: impl Display) -> Self {
Self::DisplayInitialization(message(reason))
}
pub fn event_loop(reason: impl Display) -> Self {
Self::EventLoop(message(reason))
}
pub fn rendering_failed(reason: impl Display) -> Self {
Self::RenderingFailed(message(reason))
}
pub fn logo_source(reason: impl Display) -> Self {
Self::LogoSource(message(reason))
}
}
fn message(reason: impl Display) -> Message {
let mut buffer = Message::new();
let _ = write!(Truncating(&mut buffer), "{reason}");
buffer
}
struct Truncating<'a>(&'a mut Message);
impl Write for Truncating<'_> {
fn write_str(&mut self, text: &str) -> fmt::Result {
for character in text.chars() {
if self.0.push(character).is_err() {
break;
}
}
Ok(())
}
}

View File

@@ -0,0 +1,9 @@
#![no_std]
mod errors;
mod ports;
mod use_cases;
pub use errors::{ApplicationError, Message};
pub use ports::{DisplayOverlayPort, LogoSourcePort, RendererPort};
pub use use_cases::{run, step};

View File

@@ -0,0 +1,20 @@
use domain::{Dimensions, ScreensaverScene};
use crate::ApplicationError;
pub trait DisplayOverlayPort {
fn screen_dimensions(&self) -> Dimensions;
fn wait_for_frame(&mut self) -> Result<bool, ApplicationError>;
}
pub trait RendererPort {
fn render_frame(
&mut self,
scene: &ScreensaverScene,
screen_size: Dimensions,
) -> Result<(), ApplicationError>;
}
pub trait LogoSourcePort {
fn load(&self) -> Result<Option<&[u8]>, ApplicationError>;
}

View File

@@ -0,0 +1,37 @@
use domain::ScreensaverScene;
use crate::{
errors::ApplicationError,
ports::{DisplayOverlayPort, RendererPort},
};
pub fn step<D, R>(
display: &mut D,
renderer: &mut R,
scene: &mut ScreensaverScene,
) -> Result<(), ApplicationError>
where
D: DisplayOverlayPort,
R: RendererPort,
{
let bounds = display.screen_dimensions();
scene.tick(bounds);
renderer.render_frame(scene, bounds)
}
pub fn run<D, R>(
display: &mut D,
renderer: &mut R,
scene: &mut ScreensaverScene,
) -> Result<(), ApplicationError>
where
D: DisplayOverlayPort,
R: RendererPort,
{
while display.wait_for_frame()? {
step(display, renderer, scene)?;
}
Ok(())
}