use crate::{
config::{Config, DbType},
database::Database,
};
use anyhow::Error as AnyError;
use modules::Modules;
use std::{net::SocketAddr, panic};
use thiserror::Error;
use tokio::task::JoinHandle;
use tracing::log::warn;
pub(crate) mod genesis;
pub mod graph_api;
pub mod metrics;
pub mod modules;
pub struct FuelService {
tasks: Vec<JoinHandle<Result<(), AnyError>>>,
modules: Modules,
pub bound_address: SocketAddr,
}
impl FuelService {
#[tracing::instrument(skip(config))]
pub async fn new_node(config: Config) -> Result<Self, AnyError> {
let database = match config.database_type {
#[cfg(feature = "rocksdb")]
DbType::RocksDb => Database::open(&config.database_path)?,
DbType::InMemory => Database::in_memory(),
#[cfg(not(feature = "rocksdb"))]
_ => Database::in_memory(),
};
Self::init_service(database, config).await
}
#[cfg(any(test, feature = "test-helpers"))]
pub async fn from_database(database: Database, config: Config) -> Result<Self, AnyError> {
Self::init_service(database, config).await
}
async fn init_service(database: Database, config: Config) -> Result<Self, AnyError> {
if config.predicates {
warn!("Predicates are currently an unstable feature!");
}
Self::initialize_state(&config, &database)?;
let modules = modules::start_modules(&config, &database).await?;
let mut tasks = vec![];
let (bound_address, api_server) =
graph_api::start_server(config.clone(), database, &modules).await?;
tasks.push(api_server);
Ok(FuelService {
tasks,
bound_address,
modules,
})
}
pub async fn run(self) {
for task in self.tasks {
match task.await {
Err(err) => {
if err.is_panic() {
panic::resume_unwind(err.into_panic());
}
}
Ok(Err(e)) => {
eprintln!("server error: {:?}", e);
}
Ok(Ok(_)) => {}
}
}
}
pub async fn stop(&self) {
for task in &self.tasks {
task.abort();
}
self.modules.stop().await;
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("An api server error occurred {0}")]
ApiServer(#[from] hyper::Error),
}