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::fixture_manager_fixtures().map(|fixture| (fixture.name(), fixture)),
138 )
139 } else {
140 let fixtures = self
141 .fixtures
142 .iter()
143 .map(|name| {
144 let fixture = JsonFixture::by_name(name)
145 .ok_or_else(|| anyhow!("unknown fixture: {}", name))?;
146 Ok((name.as_str(), fixture))
147 })
148 .collect::<Result<Vec<_>>>()?;
149 Box::new(fixtures.into_iter())
150 };
151
152 let mut num_changed = 0;
153
154 for (name, fixture) in fixtures {
155 println!("generating outputs for {name}...");
156
157 let context: GenerateContext<'_, T> =
158 GenerateContext::new(fixture, &args, self.mode == GenerateMode::Force)?;
159 for item in context {
160 let item = item?;
161 let is_changed = item.is_changed();
162
163 if is_changed {
164 num_changed += 1;
165 }
166
167 if self.mode == GenerateMode::Check {
168 if is_changed {
169 println!("** {}:\n{}", item.path(), item.diff());
170 }
171
172 continue;
173 }
174
175 if is_changed || self.mode == GenerateMode::Force {
176 item.write_to_path()?;
177 }
178 }
179 }
180
181 if self.mode == GenerateMode::Check && num_changed > 0 {
182 bail!("{} outputs changed", num_changed);
183 }
184
185 println!("{num_changed} outputs changed");
186
187 Ok(())
188 }
189}