#[macro_use]
extern crate tracing;
use std::path::PathBuf;
use hyperion::effects::EffectRegistry;
use structopt::StructOpt;
use tokio::runtime::Builder;
use tokio::signal;
use hyperion::models::backend::ConfigExt;
#[derive(Debug, StructOpt)]
struct Opts {
#[structopt(short, long, parse(from_occurrences))]
verbose: u32,
#[structopt(
short,
long = "db-path",
default_value = "$ROOT/hyperion.db",
env = "DATABASE_URL"
)]
database_path: PathBuf,
#[structopt(short, long = "config")]
config_path: Option<PathBuf>,
#[structopt(long)]
dump_config: bool,
#[structopt(long)]
user_root: Option<PathBuf>,
#[structopt(long)]
core_threads: Option<usize>,
}
async fn run(opts: Opts) -> color_eyre::eyre::Result<()> {
let paths = hyperion::global::Paths::new(opts.user_root.clone())?;
let mut backend: Box<dyn hyperion::models::backend::ConfigBackend> =
if let Some(config_path) = opts.config_path.as_deref() {
Box::new(hyperion::models::backend::FileBackend::new(config_path))
} else {
let db = hyperion::db::Db::open(&paths.resolve_path(opts.database_path)).await?;
Box::new(hyperion::models::backend::DbBackend::new(db))
};
let config = backend.load().await?;
if opts.dump_config {
print!("{}", config.to_string()?);
return Ok(());
}
let global = hyperion::global::GlobalData::new(&config).wrap();
let mut effects = EffectRegistry::new();
let providers = hyperion::effects::Providers::new();
for path in ["$SYSTEM/effects"] {
let path = paths.resolve_path(path);
let mut discovered = hyperion::effects::EffectDefinition::read_dir(&path).await?;
discovered.sort_by(|a, b| a.file.cmp(&b.file));
effects.add_definitions(&providers, discovered);
}
info!("discovered {} effects", effects.len());
global
.write_effects(|e| {
*e = effects;
})
.await;
tokio::spawn(
hyperion::global::HookRunner::new(
config.global.hooks.clone(),
global.subscribe_events().await,
)
.run(),
);
let mut instances = Vec::with_capacity(config.instances.len());
for (&id, inst) in &config.instances {
let (inst, handle) = hyperion::instance::Instance::new(global.clone(), inst.clone()).await;
global.register_instance(handle.clone()).await;
instances.push(handle);
tokio::spawn({
let global = global.clone();
let event_tx = global.get_event_tx().await;
async move {
event_tx
.send(hyperion::global::Event::instance(
id,
hyperion::global::InstanceEventKind::Start,
))
.map(|_| ())
.unwrap_or_else(|err| {
error!(error = %err, "event error");
});
let result = inst.run().await;
if let Err(error) = result {
error!(error = %error, "instance error");
}
global.unregister_instance(id).await;
event_tx
.send(hyperion::global::Event::instance(
id,
hyperion::global::InstanceEventKind::Stop,
))
.map(|_| ())
.unwrap_or_else(|err| {
error!(error = %err, "event error");
});
}
});
}
let _flatbuffers_server = if config.global.flatbuffers_server.enable {
Some(
hyperion::servers::bind(
"Flatbuffers",
config.global.flatbuffers_server.clone(),
global.clone(),
hyperion::servers::flat::handle_client,
)
.await?,
)
} else {
None
};
let _json_server = hyperion::servers::bind(
"JSON",
config.global.json_server,
global.clone(),
hyperion::servers::json::handle_client,
)
.await?;
let _proto_server = if config.global.proto_server.enable {
Some(
hyperion::servers::bind(
"Protobuf",
config.global.proto_server.clone(),
global.clone(),
hyperion::servers::proto::handle_client,
)
.await?,
)
} else {
None
};
let _webconfig_server = tokio::task::spawn(
hyperion::web::bind(global.clone(), &config.global.web_config, &paths).await?,
);
let event_tx = global.get_event_tx().await;
event_tx.send(hyperion::global::Event::Start)?;
let mut abort = false;
while !abort {
tokio::select! {
_ = signal::ctrl_c() => {
abort = true;
}
}
}
for instance in instances.into_iter() {
instance.stop().await.ok();
}
event_tx.send(hyperion::global::Event::Stop)?;
Ok(())
}
fn install_tracing(opts: &Opts) -> Result<(), tracing_subscriber::util::TryInitError> {
use tracing_error::ErrorLayer;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
let fmt_layer = fmt::layer();
let filter_layer = EnvFilter::try_from_env("HYPERION_LOG").unwrap_or_else(|_| {
EnvFilter::new(match opts.verbose {
0 => "hyperion=warn,hyperiond=warn",
1 => "hyperion=info,hyperiond=info",
2 => "hyperion=debug,hyperiond=debug",
_ => "hyperion=trace,hyperiond=trace",
})
});
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.with(ErrorLayer::default())
.try_init()
}
#[paw::main]
fn main(opts: Opts) -> color_eyre::eyre::Result<()> {
color_eyre::install()?;
install_tracing(&opts)?;
let thd_count = opts
.core_threads
.and_then(|n| if n > 0 { Some(n) } else { None })
.unwrap_or_else(|| num_cpus::get().max(2).min(4));
let rt = Builder::new_multi_thread()
.worker_threads(thd_count)
.enable_all()
.build()?;
rt.block_on(run(opts))
}