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 safety: SafetyContext,
26 pub sink: OutputSink,
27}
28
29impl RuntimeConfig {
30 pub fn from_cli(cli: &Cli) -> Result<Self> {
31 let safety_cfg = SafetyConfig::load().context("Failed to load safety.json")?;
32 anyhow::ensure!(
33 !(cli.dry_run && cli.simulate),
34 "--dry-run and --simulate are mutually exclusive"
35 );
36 Ok(Self {
37 mode: cli.mode,
38 output: cli.effective_output(),
39 yes: cli.yes,
40 dry_run: cli.dry_run,
41 simulate: cli.simulate,
42 trust: cli.trust,
43 suppress_tick_output: false,
44 safety: SafetyContext::new(safety_cfg),
45 sink: OutputSink::stdout(),
46 })
47 }
48
49 pub fn emit(&self, envelope: crate::output::ResponseEnvelope) {
50 self.sink.write(&envelope, self.output);
51 }
52
53 pub fn is_tty(&self) -> bool {
54 use std::io::{stdin, stdout, IsTerminal};
55 stdin().is_terminal() && stdout().is_terminal()
56 }
57
58 pub fn is_interactive(&self) -> bool {
60 use std::io::stdout;
61 use std::io::IsTerminal;
62 self.mode.is_human() && stdout().is_terminal()
63 }
64
65 pub fn build_api(&self) -> Result<Arc<TraderApi>> {
66 let config = ClientConfig::from_env().context("Failed to load Schwab client config")?;
67 let client = SchwabClient::new(config);
68 Ok(Arc::new(TraderApi::new(client)))
69 }
70
71 pub fn build_market_api(&self) -> Result<Arc<MarketDataApi>> {
72 let config = ClientConfig::from_env().context("Failed to load Schwab client config")?;
73 let client = SchwabClient::new(config);
74 Ok(Arc::new(MarketDataApi::new(client)))
75 }
76
77 pub fn for_agent_trading(
79 output: OutputFormat,
80 yes: bool,
81 dry_run: bool,
82 simulate: bool,
83 trust: bool,
84 suppress_tick_output: bool,
85 safety: SafetyContext,
86 sink: OutputSink,
87 ) -> Self {
88 Self {
89 mode: CliMode::Agent,
90 output,
91 yes,
92 dry_run,
93 simulate,
94 trust,
95 suppress_tick_output,
96 safety,
97 sink,
98 }
99 }
100}