Release 1.0

This commit is contained in:
Breadway 2026-05-11 11:56:03 +08:00
parent 009ea6da0e
commit 730a8b61d7
32 changed files with 6629 additions and 0 deletions

128
breadd/src/main.rs Normal file
View file

@ -0,0 +1,128 @@
mod adapters;
mod core;
mod ipc;
mod lua;
use std::sync::Arc;
use anyhow::Result;
use bread_shared::{AdapterSource, BreadEvent, RawEvent};
use tokio::sync::{broadcast, mpsc, watch, RwLock};
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
use crate::core::config::Config;
use crate::core::normalizer::EventNormalizer;
use crate::core::state_engine::{run_state_engine, StateHandle};
use crate::core::types::RuntimeState;
#[tokio::main]
async fn main() -> Result<()> {
let config = Config::load()?;
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::new(config.daemon.log_level.clone()))
.init();
info!("starting breadd");
let state = Arc::new(RwLock::new(RuntimeState::default()));
let (raw_tx, mut raw_rx) = mpsc::channel::<RawEvent>(2048);
let (normalized_tx, normalized_rx) = mpsc::unbounded_channel::<BreadEvent>();
let (state_cmd_tx, state_cmd_rx) = mpsc::unbounded_channel();
let (event_stream_tx, _) = broadcast::channel(2048);
let (shutdown_tx, shutdown_rx) = watch::channel(false);
let state_handle = StateHandle::new(state.clone(), state_cmd_tx);
let lua_runtime = lua::spawn_runtime(config.clone(), state_handle.clone(), normalized_tx.clone())?;
let lua_tx = lua_runtime.sender();
tokio::spawn(run_state_engine(
normalized_rx,
state_cmd_rx,
state.clone(),
lua_tx,
event_stream_tx.clone(),
shutdown_rx.clone(),
));
let normalizer = Arc::new(EventNormalizer::new(config.events.dedup_window_ms));
{
let normalizer = normalizer.clone();
let normalized_tx = normalized_tx.clone();
let mut shutdown_rx = shutdown_rx.clone();
tokio::spawn(async move {
loop {
tokio::select! {
_ = shutdown_rx.changed() => {
if *shutdown_rx.borrow() {
break;
}
}
maybe_raw = raw_rx.recv() => {
let Some(raw) = maybe_raw else {
break;
};
for event in normalizer.normalize(&raw) {
if normalized_tx.send(event).is_err() {
break;
}
}
}
}
}
});
}
let adapter_manager = adapters::Manager::new(raw_tx, config.clone(), shutdown_rx.clone());
adapter_manager.start_all().await?;
let _ = normalized_tx.send(BreadEvent::new(
"bread.system.startup",
AdapterSource::System,
serde_json::json!({}),
));
let ipc_server = ipc::Server::new(
config.socket_path(),
state_handle,
event_stream_tx,
lua_runtime.clone(),
normalized_tx,
);
info!("breadd fully started");
tokio::select! {
result = ipc_server.serve(shutdown_rx.clone()) => {
if let Err(err) = result {
error!(error = %err, "ipc server failed");
}
}
_ = wait_for_shutdown() => {
info!("shutdown signal received");
}
}
let _ = shutdown_tx.send(true);
lua_runtime.shutdown();
Ok(())
}
async fn wait_for_shutdown() {
let ctrl_c = tokio::signal::ctrl_c();
#[cfg(unix)]
{
use tokio::signal::unix::{signal, SignalKind};
let mut sigterm = signal(SignalKind::terminate()).expect("failed to install SIGTERM handler");
tokio::select! {
_ = ctrl_c => {},
_ = sigterm.recv() => {},
}
}
#[cfg(not(unix))]
{
let _ = ctrl_c.await;
}
}