1pub mod context;
5pub mod hakari_toml;
6pub mod summaries;
7
8use crate::{
9 context::{ContextImpl, GenerateContext},
10 hakari_toml::HakariTomlContext,
11 summaries::*,
12};
13use anyhow::{Result, anyhow, bail};
14use clap::{Parser, ValueEnum};
15use fixtures::json::JsonFixture;
16
17#[derive(Debug, Parser)]
18pub struct FixtureManager {
19 #[clap(subcommand)]
21 cmd: Command,
22}
23
24impl FixtureManager {
25 pub fn exec(self) -> Result<()> {
26 match self.cmd {
27 Command::List => list(),
28 Command::GenerateSummaries(opts) => opts.exec(),
29 Command::GenerateHakari(opts) => opts.exec(),
30 }
31 }
32}
33
34#[derive(Debug, Parser)]
35enum Command {
36 #[clap(name = "list")]
37 List,
39 GenerateSummaries(GenerateSummariesOpts),
41 GenerateHakari(GenerateHakariOpts),
43}
44
45pub fn list() -> Result<()> {
46 for (name, fixture) in JsonFixture::all_fixtures().iter() {
47 println!("{}: {}", name, fixture.workspace_path());
48 }
49
50 Ok(())
51}
52
53#[derive(Debug, Parser)]
54pub struct GenerateSummariesOpts {
55 #[clap(long, default_value = Self::DEFAULT_COUNT_STR)]
57 pub count: usize,
58
59 #[clap(flatten)]
60 pub generate_opts: GenerateOpts,
61}
62
63impl GenerateSummariesOpts {
64 pub const DEFAULT_COUNT_STR: &'static str = "8";
66
67 pub fn default_count() -> usize {
69 Self::DEFAULT_COUNT_STR
70 .parse()
71 .expect("DEFAULT_COUNT_STR should parse as a usize")
72 }
73}
74
75#[derive(Debug, Parser)]
76pub struct GenerateHakariOpts {
77 #[clap(long, default_value = Self::DEFAULT_COUNT_STR)]
79 pub count: usize,
80
81 #[clap(flatten)]
82 pub generate_opts: GenerateOpts,
83}
84
85impl GenerateHakariOpts {
86 pub const DEFAULT_COUNT_STR: &'static str = "4";
88
89 pub fn default_count() -> usize {
91 Self::DEFAULT_COUNT_STR
92 .parse()
93 .expect("DEFAULT_COUNT_STR should parse as a usize")
94 }
95}
96
97#[derive(Debug, Parser)]
98pub struct GenerateOpts {
99 #[clap(
101 long,
102 short,
103 value_enum,
104 ignore_case = true,
105 default_value = "generate"
106 )]
107 pub mode: GenerateMode,
108
109 #[clap(long)]
111 pub fixtures: Vec<String>,
112}
113
114#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, ValueEnum)]
115pub enum GenerateMode {
116 Generate,
117 Check,
118 Force,
119}
120
121impl GenerateSummariesOpts {
122 pub fn exec(self) -> Result<()> {
123 self.generate_opts.exec::<SummaryContext>(self.count)
124 }
125}
126
127impl GenerateHakariOpts {
128 pub fn exec(self) -> Result<()> {
129 self.generate_opts.exec::<HakariTomlContext>(self.count)
130 }
131}
132
133impl GenerateOpts {
134 pub fn exec<'g, T: ContextImpl<'g>>(self, args: T::IterArgs) -> Result<()> {
135 let fixtures: Box<dyn Iterator<Item = (&str, &JsonFixture)>> = if self.fixtures.is_empty() {
136 Box::new(
137 JsonFixture::all_fixtures()
138 .iter()
139 .map(|(name, fixture)| (*name, fixture)),
140 )
141 } else {
142 let fixtures = self
143 .fixtures
144 .iter()
145 .map(|name| {
146 let fixture = JsonFixture::by_name(name)
147 .ok_or_else(|| anyhow!("unknown fixture: {}", name))?;
148 Ok((name.as_str(), fixture))
149 })
150 .collect::<Result<Vec<_>>>()?;
151 Box::new(fixtures.into_iter())
152 };
153
154 let mut num_changed = 0;
155
156 for (name, fixture) in fixtures {
157 println!("generating outputs for {name}...");
158
159 let context: GenerateContext<'_, T> =
160 GenerateContext::new(fixture, &args, self.mode == GenerateMode::Force)?;
161 for item in context {
162 let item = item?;
163 let is_changed = item.is_changed();
164
165 if is_changed {
166 num_changed += 1;
167 }
168
169 if self.mode == GenerateMode::Check {
170 if is_changed {
171 println!("** {}:\n{}", item.path(), item.diff());
172 }
173
174 continue;
175 }
176
177 if is_changed || self.mode == GenerateMode::Force {
178 item.write_to_path()?;
179 }
180 }
181 }
182
183 if self.mode == GenerateMode::Check && num_changed > 0 {
184 bail!("{} outputs changed", num_changed);
185 }
186
187 println!("{num_changed} outputs changed");
188
189 Ok(())
190 }
191}