1use std::sync::Arc;
2
3use anyhow::{Context, Result};
4use schwab_api::{ClientConfig, SchwabClient, TraderApi};
5use schwab_market_data::MarketDataApi;
6
7use crate::cli::Cli;
8use crate::mode::CliMode;
9use crate::output::{OutputFormat, OutputSink};
10use crate::safety::SafetyContext;
11use crate::safety_config::SafetyConfig;
12
13#[derive(Debug, Clone)]
14pub struct RuntimeConfig {
15 pub mode: CliMode,
16 pub output: OutputFormat,
17 pub yes: bool,
18 pub dry_run: bool,
19 pub simulate: bool,
21 pub trust: bool,
23 pub suppress_tick_output: bool,
25 pub no_audio: bool,
27 pub safety: SafetyContext,
28 pub sink: OutputSink,
29}
30
31impl RuntimeConfig {
32 pub fn from_cli(cli: &Cli) -> Result<Self> {
33 let safety_cfg = SafetyConfig::load().context("Failed to load safety.json")?;
34 anyhow::ensure!(
35 !(cli.dry_run && cli.simulate),
36 "--dry-run and --simulate are mutually exclusive"
37 );
38 Ok(Self {
39 mode: cli.mode,
40 output: cli.effective_output(),
41 yes: cli.yes,
42 dry_run: cli.dry_run,
43 simulate: cli.simulate,
44 trust: cli.trust,
45 suppress_tick_output: false,
46 no_audio: cli.no_audio,
47 safety: SafetyContext::new(safety_cfg),
48 sink: OutputSink::stdout(),
49 })
50 }
51
52 pub fn emit(&self, envelope: crate::output::ResponseEnvelope) {
53 self.sink.write(&envelope, self.output);
54 }
55
56 pub fn is_tty(&self) -> bool {
57 use std::io::{stdin, stdout, IsTerminal};
58 stdin().is_terminal() && stdout().is_terminal()
59 }
60
61 pub fn is_interactive(&self) -> bool {
63 use std::io::stdout;
64 use std::io::IsTerminal;
65 self.mode.is_human() && stdout().is_terminal()
66 }
67
68 pub fn build_api(&self) -> Result<Arc<TraderApi>> {
69 let config = ClientConfig::from_env().context("Failed to load Schwab client config")?;
70 let client = SchwabClient::new(config);
71 Ok(Arc::new(TraderApi::new(client)))
72 }
73
74 pub fn build_market_api(&self) -> Result<Arc<MarketDataApi>> {
75 let config = ClientConfig::from_env().context("Failed to load Schwab client config")?;
76 let client = SchwabClient::new(config);
77 Ok(Arc::new(MarketDataApi::new(client)))
78 }
79
80 pub fn for_agent_trading(
82 output: OutputFormat,
83 yes: bool,
84 dry_run: bool,
85 simulate: bool,
86 trust: bool,
87 suppress_tick_output: bool,
88 no_audio: bool,
89 safety: SafetyContext,
90 sink: OutputSink,
91 ) -> Self {
92 Self {
93 mode: CliMode::Agent,
94 output,
95 yes,
96 dry_run,
97 simulate,
98 trust,
99 suppress_tick_output,
100 no_audio,
101 safety,
102 sink,
103 }
104 }
105}