1use std::{
2 path::PathBuf,
3 sync::{Arc, LazyLock},
4};
5
6use clap::{Parser, Subcommand};
7use haste_config::{ConfigType, get_config};
8use haste_fhir_operation_error::OperationOutcomeError;
9use haste_server::auth_n::oidc::routes::discovery::WellKnownDiscoveryDocument;
10use tokio::sync::Mutex;
11
12use crate::commands::config::{CLIConfiguration, load_config};
13
14mod client;
15mod commands;
16
17#[derive(Parser)]
18#[command(version, about, long_about = None)] struct Cli {
20 #[command(subcommand)]
21 command: CLICommand,
22}
23
24#[derive(Subcommand)]
25enum CLICommand {
26 FHIRPath {
28 fhirpath: String,
30 },
31 Generate {
32 #[command(subcommand)]
34 command: commands::codegen::CodeGen,
35 },
36 Server {
37 #[command(subcommand)]
38 command: commands::server::ServerCommands,
39 },
40 Api {
41 #[command(subcommand)]
42 command: commands::api::ApiCommands,
43 },
44 Config {
45 #[command(subcommand)]
46 command: commands::config::ConfigCommands,
47 },
48 Worker {
49 #[command(subcommand)]
50 command: Option<commands::worker::WorkerCommands>,
51 },
52 Testscript {
53 #[command(subcommand)]
54 command: commands::testscript::TestScriptCommands,
55 },
56 Admin {
57 #[command(subcommand)]
58 command: commands::admin::AdminCommands,
59 },
60}
61
62static CONFIG_LOCATION: LazyLock<PathBuf> = LazyLock::new(|| {
63 let config_dir = std::env::home_dir()
64 .unwrap_or_else(|| std::path::PathBuf::from("."))
65 .join(".haste_health");
66
67 std::fs::create_dir_all(&config_dir).expect("Failed to create config directory");
68
69 config_dir.join("config.toml")
70});
71
72pub struct CLIState {
73 config: CLIConfiguration,
74 access_token: Option<String>,
75 well_known_document: Option<WellKnownDiscoveryDocument>,
76}
77
78impl CLIState {
79 pub fn new(config: CLIConfiguration) -> Self {
80 CLIState {
81 config,
82 access_token: None,
83 well_known_document: None,
84 }
85 }
86}
87
88static CLI_STATE: LazyLock<Arc<Mutex<CLIState>>> = LazyLock::new(|| {
89 let config = load_config(&CONFIG_LOCATION);
90
91 Arc::new(Mutex::new(CLIState::new(config)))
92});
93
94enum CLIEnvironmentVariables {
95 SentryDSN,
96}
97
98impl From<CLIEnvironmentVariables> for String {
99 fn from(value: CLIEnvironmentVariables) -> Self {
100 match value {
101 CLIEnvironmentVariables::SentryDSN => "SENTRY_DSN".to_string(),
102 }
103 }
104}
105
106fn main() -> Result<(), OperationOutcomeError> {
107 tracing_subscriber::fmt::init();
108 let cli = Cli::parse();
109 let config = CLI_STATE.clone();
110 let env = get_config(ConfigType::Environment);
111 let sentry_location = env.get(CLIEnvironmentVariables::SentryDSN);
112
113 let _guard = sentry::init((
117 sentry_location.unwrap_or_default(),
118 sentry::ClientOptions {
119 release: sentry::release_name!(),
120 send_default_pii: true,
123 ..Default::default()
124 },
125 ));
126
127 tokio::runtime::Builder::new_multi_thread()
128 .enable_all()
129 .build()
130 .unwrap()
131 .block_on(async {
132 match &cli.command {
133 CLICommand::FHIRPath { fhirpath } => commands::fhirpath::fhirpath(fhirpath).await,
134 CLICommand::Generate { command } => commands::codegen::codegen(command).await,
135 CLICommand::Server { command } => commands::server::server(command).await,
136 CLICommand::Worker { command } => commands::worker::worker(command).await,
137 CLICommand::Config { command } => commands::config::config(&config, command).await,
138 CLICommand::Api { command } => commands::api::api_commands(config, command).await,
139 CLICommand::Testscript { command } => {
140 commands::testscript::testscript_commands(config, command).await
141 }
142 CLICommand::Admin { command } => commands::admin::admin(command).await,
143 }
144 })
145}