cat_gateway/cli.rs
1//! CLI interpreter for the service
2use std::{io::Write, path::PathBuf, time::Duration};
3
4use clap::Parser;
5use tracing::{error, info};
6
7use crate::{
8 cardano::start_followers,
9 db::{self, index::session::CassandraSession},
10 service::{
11 self,
12 utilities::health::{
13 condition_for_started, is_live, live_counter_reset, service_has_started, set_to_started,
14 },
15 },
16 settings::{ServiceSettings, Settings},
17};
18
19#[derive(Parser)]
20#[clap(rename_all = "kebab-case")]
21/// Simple service CLI options
22pub(crate) enum Cli {
23 /// Run the service
24 Run(ServiceSettings),
25 /// Build API docs of the service in the JSON format
26 Docs {
27 /// The output path to the generated docs file, if omitted prints to stdout.
28 output: Option<PathBuf>,
29 },
30}
31
32impl Cli {
33 /// Execute the specified operation.
34 ///
35 /// This method is asynchronous and returns a `Result` indicating whether the
36 /// operation was successful or if an error occurred.
37 ///
38 /// # Errors
39 ///
40 /// This method can return an error if:
41 ///
42 /// - Failed to initialize the logger with the specified log level.
43 /// - Failed to create a new `State` with the provided database URL.
44 /// - Failed to run the service on the specified address.
45 pub(crate) async fn exec(self) -> anyhow::Result<()> {
46 match self {
47 Self::Run(settings) => {
48 Settings::init(settings)?;
49
50 let mut tasks = Vec::new();
51
52 info!("Catalyst Gateway - Starting");
53
54 // Start the DB's.
55 CassandraSession::init();
56
57 // Initialize Event DB connection pool
58 db::event::establish_connection_pool();
59
60 // Start the chain indexing follower.
61 start_followers().await?;
62
63 // Start the API service.
64 let handle = tokio::spawn(async move {
65 match service::run().await {
66 Ok(()) => info!("Endpoints started ok"),
67 Err(err) => {
68 error!("Error starting endpoints {err}");
69 },
70 }
71 });
72 tasks.push(handle);
73
74 // Start task to reset the service 'live counter' at a regular interval.
75 let handle = tokio::spawn(async move {
76 while is_live() {
77 tokio::time::sleep(Settings::service_live_timeout_interval()).await;
78 live_counter_reset();
79 }
80 });
81 tasks.push(handle);
82
83 // Start task to wait for the service 'started' flag to be `true`.
84 let handle = tokio::spawn(async move {
85 while !service_has_started() {
86 tokio::time::sleep(Duration::from_secs(1)).await;
87 if condition_for_started() {
88 set_to_started();
89 }
90 }
91 });
92 tasks.push(handle);
93
94 // Run all asynchronous tasks to completion.
95 for task in tasks {
96 task.await?;
97 }
98
99 info!("Catalyst Gateway - Shut Down");
100 },
101 Self::Docs { output } => {
102 let docs = service::get_app_docs();
103 match output {
104 Some(path) => {
105 let mut docs_file = std::fs::File::create(path)?;
106 docs_file.write_all(docs.as_bytes())?;
107 },
108 None => println!("{docs}"),
109 }
110 },
111 }
112
113 Ok(())
114 }
115}