Skip to main content

dropshot_api_manager/cmd/
dispatch.rs

1// Copyright 2026 Oxide Computer Company
2
3use crate::{
4    apis::ManagedApis,
5    cmd::{
6        check::check_impl, debug::debug_impl, generate::generate_impl,
7        list::list_impl,
8    },
9    environment::{BlessedSource, Environment, GeneratedSource, ResolvedEnv},
10    output::OutputOpts,
11    vcs::VcsRevision,
12};
13use anyhow::Result;
14use camino::Utf8PathBuf;
15use clap::{Args, Parser, Subcommand};
16use std::process::ExitCode;
17
18/// Manage OpenAPI documents for this repository.
19///
20/// For more information, see <https://crates.io/crates/dropshot-api-manager>.
21#[derive(Debug, Parser)]
22pub struct App {
23    #[clap(flatten)]
24    output_opts: OutputOpts,
25
26    #[clap(subcommand)]
27    command: Command,
28}
29
30impl App {
31    /// Executes the application under the given environment, and with the
32    /// provided list of managed APIs.
33    pub fn exec(self, env: &Environment, apis: &ManagedApis) -> ExitCode {
34        let result = match self.command {
35            Command::Debug(args) => args.exec(env, apis, &self.output_opts),
36            Command::List(args) => args.exec(apis, &self.output_opts),
37            Command::Generate(args) => args.exec(env, apis, &self.output_opts),
38            Command::Check(args) => args.exec(env, apis, &self.output_opts),
39        };
40
41        match result {
42            Ok(exit_code) => exit_code,
43            Err(error) => {
44                eprintln!("failure: {:#}", error);
45                ExitCode::FAILURE
46            }
47        }
48    }
49}
50
51#[derive(Debug, Subcommand)]
52pub enum Command {
53    /// Dump debug information about everything the tool knows
54    Debug(DebugArgs),
55
56    /// List managed APIs.
57    ///
58    /// Returns information purely from code without consulting JSON files on
59    /// disk. To compare against files on disk, use the `check` command.
60    List(ListArgs),
61
62    /// Generate latest OpenAPI documents and validate the results.
63    Generate(GenerateArgs),
64
65    /// Check that OpenAPI documents are up-to-date and valid.
66    Check(CheckArgs),
67}
68
69#[derive(Debug, Args)]
70pub struct BlessedSourceArgs {
71    /// Loads blessed OpenAPI documents from the given VCS REVISION.
72    ///
73    /// The REVISION is not used as-is; instead, the tool always looks at
74    /// the merge-base between the current working state and REVISION.
75    /// So if you provide `main`, then it will look at the merge-base
76    /// of the working copy with `main`.
77    ///
78    /// REVISION is optional and defaults to the `default_blessed_branch`
79    /// provided by the OpenAPI manager binary (typically `origin/main`
80    /// for Git, `trunk()` for Jujutsu).
81    ///
82    /// The path within the revision defaults to `default_openapi_dir`
83    /// provided by the OpenAPI manager binary. To override it, use
84    /// `--blessed-from-vcs-path`.
85    ///
86    /// As a fallback, the `OPENAPI_MGR_BLESSED_FROM_GIT` environment
87    /// variable can also be used.
88    // Environment variable handling is done manually in
89    // `resolve_blessed_from_vcs` because clap's `env()` only supports a
90    // single variable, and we need two.
91    #[clap(
92        long = "blessed-from-vcs",
93        alias = "blessed-from-git",
94        env(BLESSED_FROM_VCS_ENV),
95        value_name("REVISION")
96    )]
97    pub blessed_from_vcs: Option<String>,
98
99    /// Overrides the path within the VCS revision to load blessed
100    /// OpenAPI documents from.
101    #[clap(long, env(BLESSED_FROM_VCS_PATH_ENV), value_name("PATH"))]
102    pub blessed_from_vcs_path: Option<Utf8PathBuf>,
103
104    /// Loads blessed OpenAPI documents from a local directory (instead of
105    /// the default, from VCS).
106    ///
107    /// This is intended for testing and debugging this tool.
108    #[clap(
109        long,
110        conflicts_with("blessed_from_vcs"),
111        env("OPENAPI_MGR_BLESSED_FROM_DIR"),
112        value_name("DIRECTORY")
113    )]
114    pub blessed_from_dir: Option<Utf8PathBuf>,
115}
116
117/// Environment variable for the blessed VCS revision.
118const BLESSED_FROM_VCS_ENV: &str = "OPENAPI_MGR_BLESSED_FROM_VCS";
119
120/// Environment variable for the blessed VCS path within a revision.
121const BLESSED_FROM_VCS_PATH_ENV: &str = "OPENAPI_MGR_BLESSED_FROM_VCS_PATH";
122
123/// Environment variable for the blessed VCS revision (legacy fallback).
124const BLESSED_FROM_GIT_ENV: &str = "OPENAPI_MGR_BLESSED_FROM_GIT";
125
126impl BlessedSourceArgs {
127    pub(crate) fn to_blessed_source(
128        &self,
129        env: &ResolvedEnv,
130    ) -> Result<BlessedSource, anyhow::Error> {
131        assert!(
132            self.blessed_from_dir.is_none() || self.blessed_from_vcs.is_none()
133        );
134
135        if let Some(local_directory) = &self.blessed_from_dir {
136            return Ok(BlessedSource::Directory {
137                local_directory: local_directory.clone(),
138            });
139        }
140
141        let resolved =
142            resolve_blessed_from_vcs(self.blessed_from_vcs.as_deref());
143        let revision_str = match &resolved {
144            Some(revision) => revision.as_str(),
145            None => env.default_blessed_branch.as_str(),
146        };
147        let revision = VcsRevision::from(String::from(revision_str));
148        let directory = match &self.blessed_from_vcs_path {
149            Some(path) => path.clone(),
150            // We must use the relative directory path for VCS
151            // commands.
152            None => Utf8PathBuf::from(env.openapi_rel_dir()),
153        };
154        Ok(BlessedSource::VcsRevisionMergeBase { revision, directory })
155    }
156}
157
158/// Resolve the blessed-from-vcs value from the CLI flag or environment
159/// variables.
160///
161/// Returns `Some` if a value was provided via the CLI flag or an
162/// environment variable. The priority is:
163///
164/// 1. CLI flag (`cli_value`)
165/// 2. `OPENAPI_MGR_BLESSED_FROM_VCS` (done automatically by clap and
166///    stored in `cli_value`)
167/// 3. `OPENAPI_MGR_BLESSED_FROM_GIT` (legacy fallback)
168///
169/// Returns `None` if none of these are set, meaning the caller should
170/// use the environment's default blessed branch or revset.
171fn resolve_blessed_from_vcs(cli_value: Option<&str>) -> Option<String> {
172    if let Some(v) = cli_value {
173        return Some(v.to_owned());
174    }
175
176    if let Ok(v) = std::env::var(BLESSED_FROM_GIT_ENV) {
177        return Some(v);
178    }
179
180    None
181}
182
183#[derive(Debug, Args)]
184pub struct GeneratedSourceArgs {
185    /// Instead of generating OpenAPI documents directly from the API
186    /// implementation, load OpenAPI documents from this directory.
187    #[clap(long, value_name("DIRECTORY"))]
188    pub generated_from_dir: Option<Utf8PathBuf>,
189}
190
191impl From<GeneratedSourceArgs> for GeneratedSource {
192    fn from(value: GeneratedSourceArgs) -> Self {
193        match value.generated_from_dir {
194            Some(local_directory) => {
195                GeneratedSource::Directory { local_directory }
196            }
197            None => GeneratedSource::Generated,
198        }
199    }
200}
201
202#[derive(Debug, Args)]
203pub struct LocalSourceArgs {
204    /// Loads this workspace's OpenAPI documents from local path DIRECTORY.
205    #[clap(long, env("OPENAPI_MGR_DIR"), value_name("DIRECTORY"))]
206    dir: Option<Utf8PathBuf>,
207}
208
209#[derive(Debug, Args)]
210pub struct DebugArgs {
211    #[clap(flatten)]
212    local: LocalSourceArgs,
213    #[clap(flatten)]
214    blessed: BlessedSourceArgs,
215    #[clap(flatten)]
216    generated: GeneratedSourceArgs,
217}
218
219impl DebugArgs {
220    fn exec(
221        self,
222        env: &Environment,
223        apis: &ManagedApis,
224        output: &OutputOpts,
225    ) -> anyhow::Result<ExitCode> {
226        let env = env.resolve(self.local.dir)?;
227        let blessed_source = self.blessed.to_blessed_source(&env)?;
228        let generated_source = GeneratedSource::from(self.generated);
229        debug_impl(apis, &env, &blessed_source, &generated_source, output)?;
230        Ok(ExitCode::SUCCESS)
231    }
232}
233
234#[derive(Debug, Args)]
235pub struct ListArgs {
236    /// Show verbose output including descriptions.
237    #[clap(long, short)]
238    verbose: bool,
239}
240
241impl ListArgs {
242    fn exec(
243        self,
244        apis: &ManagedApis,
245        output: &OutputOpts,
246    ) -> anyhow::Result<ExitCode> {
247        list_impl(apis, self.verbose, output)?;
248        Ok(ExitCode::SUCCESS)
249    }
250}
251
252#[derive(Debug, Args)]
253pub struct GenerateArgs {
254    #[clap(flatten)]
255    local: LocalSourceArgs,
256    #[clap(flatten)]
257    blessed: BlessedSourceArgs,
258    #[clap(flatten)]
259    generated: GeneratedSourceArgs,
260}
261
262impl GenerateArgs {
263    fn exec(
264        self,
265        env: &Environment,
266        apis: &ManagedApis,
267        output: &OutputOpts,
268    ) -> anyhow::Result<ExitCode> {
269        let env = env.resolve(self.local.dir)?;
270        let blessed_source = self.blessed.to_blessed_source(&env)?;
271        let generated_source = GeneratedSource::from(self.generated);
272        Ok(generate_impl(
273            apis,
274            &env,
275            &blessed_source,
276            &generated_source,
277            output,
278        )?
279        .to_exit_code())
280    }
281}
282
283#[derive(Debug, Args)]
284pub struct CheckArgs {
285    #[clap(flatten)]
286    local: LocalSourceArgs,
287    #[clap(flatten)]
288    blessed: BlessedSourceArgs,
289    #[clap(flatten)]
290    generated: GeneratedSourceArgs,
291}
292
293impl CheckArgs {
294    fn exec(
295        self,
296        env: &Environment,
297        apis: &ManagedApis,
298        output: &OutputOpts,
299    ) -> anyhow::Result<ExitCode> {
300        let env = env.resolve(self.local.dir)?;
301        let blessed_source = self.blessed.to_blessed_source(&env)?;
302        let generated_source = GeneratedSource::from(self.generated);
303        Ok(check_impl(apis, &env, &blessed_source, &generated_source, output)?
304            .to_exit_code())
305    }
306}
307
308/// Exit code which indicates that local files are out-of-date.
309///
310/// This is chosen to be 4 so that the exit code is not 0 or 1 (general anyhow
311/// errors).
312pub const NEEDS_UPDATE_EXIT_CODE: u8 = 4;
313
314/// Exit code which indicates that one or more failures occurred.
315///
316/// This exit code is returned for issues like validation errors, or blessed
317/// files being updated in an incompatible way.
318pub const FAILURE_EXIT_CODE: u8 = 100;
319
320#[cfg(test)]
321mod test {
322    use super::*;
323    use crate::{
324        environment::{
325            BlessedSource, Environment, GeneratedSource, ResolvedEnv,
326        },
327        vcs::VcsRevision,
328    };
329    use assert_matches::assert_matches;
330    use camino::{Utf8Path, Utf8PathBuf};
331    use clap::Parser;
332
333    #[test]
334    fn test_arg_parsing() {
335        // Default case
336        let app = App::parse_from(["dummy", "check"]);
337        assert_matches!(
338            app.command,
339            Command::Check(CheckArgs {
340                local: LocalSourceArgs { dir: None },
341                blessed: BlessedSourceArgs {
342                    blessed_from_vcs: None,
343                    blessed_from_vcs_path: None,
344                    blessed_from_dir: None
345                },
346                generated: GeneratedSourceArgs { generated_from_dir: None },
347            })
348        );
349
350        // Override local dir
351        let app = App::parse_from(["dummy", "check", "--dir", "foo"]);
352        assert_matches!(app.command, Command::Check(CheckArgs {
353            local: LocalSourceArgs { dir: Some(local_dir) },
354            blessed:
355                BlessedSourceArgs { blessed_from_vcs: None, blessed_from_vcs_path: None, blessed_from_dir: None },
356            generated: GeneratedSourceArgs { generated_from_dir: None },
357        }) if local_dir == "foo");
358
359        // Override generated dir differently
360        let app = App::parse_from([
361            "dummy",
362            "check",
363            "--dir",
364            "foo",
365            "--generated-from-dir",
366            "bar",
367        ]);
368        assert_matches!(app.command, Command::Check(CheckArgs {
369            local: LocalSourceArgs { dir: Some(local_dir) },
370            blessed:
371                BlessedSourceArgs { blessed_from_vcs: None, blessed_from_vcs_path: None, blessed_from_dir: None },
372            generated: GeneratedSourceArgs { generated_from_dir: Some(generated_dir) },
373        }) if local_dir == "foo" && generated_dir == "bar");
374
375        // Override blessed with a local directory.
376        let app = App::parse_from([
377            "dummy",
378            "check",
379            "--dir",
380            "foo",
381            "--generated-from-dir",
382            "bar",
383            "--blessed-from-dir",
384            "baz",
385        ]);
386        assert_matches!(app.command, Command::Check(CheckArgs {
387            local: LocalSourceArgs { dir: Some(local_dir) },
388            blessed:
389                BlessedSourceArgs { blessed_from_vcs: None, blessed_from_vcs_path: None, blessed_from_dir: Some(blessed_dir) },
390            generated: GeneratedSourceArgs { generated_from_dir: Some(generated_dir) },
391        }) if local_dir == "foo" && generated_dir == "bar" && blessed_dir == "baz");
392
393        // Override blessed from Git.
394        let app = App::parse_from([
395            "dummy",
396            "check",
397            "--blessed-from-git",
398            "some/other/upstream",
399        ]);
400        assert_matches!(app.command, Command::Check(CheckArgs {
401            local: LocalSourceArgs { dir: None },
402            blessed:
403                BlessedSourceArgs { blessed_from_vcs: Some(git), blessed_from_vcs_path: None, blessed_from_dir: None },
404            generated: GeneratedSourceArgs { generated_from_dir: None },
405        }) if git == "some/other/upstream");
406
407        // Error case: specifying both --blessed-from-vcs and --blessed-from-dir
408        let error = App::try_parse_from([
409            "dummy",
410            "check",
411            "--blessed-from-vcs",
412            "vcs_revision",
413            "--blessed-from-dir",
414            "dir",
415        ])
416        .unwrap_err();
417        assert_eq!(error.kind(), clap::error::ErrorKind::ArgumentConflict);
418        assert!(error.to_string().contains(
419            "error: the argument '--blessed-from-vcs <REVISION>' \
420             cannot be used with '--blessed-from-dir <DIRECTORY>"
421        ));
422    }
423
424    // Test how we turn `LocalSourceArgs` into `Environment`.
425    #[test]
426    fn test_local_args() {
427        #[cfg(unix)]
428        const ABS_DIR: &str = "/tmp";
429        #[cfg(windows)]
430        const ABS_DIR: &str = "C:\\tmp";
431
432        {
433            let env = Environment::new_for_test(
434                "cargo openapi".to_owned(),
435                Utf8PathBuf::from(ABS_DIR),
436                Utf8PathBuf::from("foo"),
437            )
438            .expect("loading environment");
439            let env = env.resolve(None).expect("resolving environment");
440            assert_eq!(
441                env.openapi_abs_dir(),
442                Utf8Path::new(ABS_DIR).join("foo")
443            );
444        }
445
446        {
447            let error = Environment::new_for_test(
448                "cargo openapi".to_owned(),
449                Utf8PathBuf::from(ABS_DIR),
450                Utf8PathBuf::from(ABS_DIR),
451            )
452            .unwrap_err();
453            assert_eq!(
454                error.to_string(),
455                format!(
456                    "default_openapi_dir must be a relative path with \
457                     normal components, found: {}",
458                    ABS_DIR
459                )
460            );
461        }
462
463        {
464            let current_dir =
465                Utf8PathBuf::try_from(std::env::current_dir().unwrap())
466                    .unwrap();
467            let env = Environment::new_for_test(
468                "cargo openapi".to_owned(),
469                current_dir.clone(),
470                Utf8PathBuf::from("foo"),
471            )
472            .expect("loading environment");
473            let env = env
474                .resolve(Some(Utf8PathBuf::from("bar")))
475                .expect("resolving environment");
476            assert_eq!(env.openapi_abs_dir(), current_dir.join("bar"));
477        }
478    }
479
480    // Test how we convert `GeneratedSourceArgs` into `GeneratedSource`.
481    #[test]
482    fn test_generated_args() {
483        let source = GeneratedSource::from(GeneratedSourceArgs {
484            generated_from_dir: None,
485        });
486        assert_matches!(source, GeneratedSource::Generated);
487
488        let source = GeneratedSource::from(GeneratedSourceArgs {
489            generated_from_dir: Some(Utf8PathBuf::from("/tmp")),
490        });
491        assert_matches!(
492            source,
493            GeneratedSource::Directory { local_directory }
494                if local_directory == "/tmp"
495        );
496    }
497
498    // Test how we convert `BlessedSourceArgs` into `BlessedSource`.
499    #[test]
500    fn test_blessed_args() {
501        #[cfg(unix)]
502        const ABS_DIR: &str = "/tmp";
503        #[cfg(windows)]
504        const ABS_DIR: &str = "C:\\tmp";
505
506        // Clear env vars so they don't interfere with these tests.
507        //
508        // SAFETY:
509        // https://nexte.st/docs/configuration/env-vars/#altering-the-environment-within-tests
510        unsafe {
511            std::env::remove_var(BLESSED_FROM_VCS_ENV);
512            std::env::remove_var(BLESSED_FROM_VCS_PATH_ENV);
513            std::env::remove_var(BLESSED_FROM_GIT_ENV);
514        }
515
516        let env =
517            Environment::new_for_test("cargo openapi", ABS_DIR, "foo-openapi")
518                .unwrap()
519                .with_default_git_branch("upstream/dev".to_owned());
520        let env = env.resolve(None).unwrap();
521
522        let source = BlessedSourceArgs {
523            blessed_from_vcs: None,
524            blessed_from_vcs_path: None,
525            blessed_from_dir: None,
526        }
527        .to_blessed_source(&env)
528        .unwrap();
529        assert_matches!(
530            source,
531            BlessedSource::VcsRevisionMergeBase { revision, directory }
532                if *revision == "upstream/dev" && directory == "foo-openapi"
533        );
534
535        // Override branch only.
536        let source = BlessedSourceArgs {
537            blessed_from_vcs: Some(String::from("my/other/main")),
538            blessed_from_vcs_path: None,
539            blessed_from_dir: None,
540        }
541        .to_blessed_source(&env)
542        .unwrap();
543        assert_matches!(
544            source,
545            BlessedSource::VcsRevisionMergeBase { revision, directory}
546                if *revision == "my/other/main" && directory == "foo-openapi"
547        );
548
549        // Override branch and directory.
550        let source = BlessedSourceArgs {
551            blessed_from_vcs: Some(String::from("my/other/main")),
552            blessed_from_vcs_path: Some(Utf8PathBuf::from("other_openapi/bar")),
553            blessed_from_dir: None,
554        }
555        .to_blessed_source(&env)
556        .unwrap();
557        assert_matches!(
558            source,
559            BlessedSource::VcsRevisionMergeBase { revision, directory}
560                if *revision == "my/other/main" &&
561                     directory == "other_openapi/bar"
562        );
563
564        // Override with a local directory.
565        let source = BlessedSourceArgs {
566            blessed_from_vcs: None,
567            blessed_from_vcs_path: None,
568            blessed_from_dir: Some(Utf8PathBuf::from("/tmp")),
569        }
570        .to_blessed_source(&env)
571        .unwrap();
572        assert_matches!(
573            source,
574            BlessedSource::Directory { local_directory }
575                if local_directory == "/tmp"
576        );
577    }
578
579    /// Helper: parse CLI args through clap and resolve the blessed
580    /// source.
581    ///
582    /// This exercises the full env var resolution path, including
583    /// clap's `env()` attribute on `blessed_from_vcs`.
584    fn parse_blessed_source(
585        env: &ResolvedEnv,
586        extra_args: &[&str],
587    ) -> BlessedSource {
588        let mut args = vec!["dummy", "check"];
589        args.extend_from_slice(extra_args);
590        let app = App::parse_from(args);
591        match app.command {
592            Command::Check(check_args) => {
593                check_args.blessed.to_blessed_source(env).unwrap()
594            }
595            _ => panic!("expected Check command"),
596        }
597    }
598
599    // Test that env vars flow through `to_blessed_source` correctly.
600    //
601    // Uses `parse_blessed_source` to route through clap parsing, so
602    // that clap's `env()` attribute on `blessed_from_vcs` is
603    // exercised. Constructing `BlessedSourceArgs` manually would
604    // bypass this and miss the `OPENAPI_MGR_BLESSED_FROM_VCS` env
605    // var.
606    #[test]
607    fn test_blessed_args_from_env_vars() {
608        #[cfg(unix)]
609        const ABS_DIR: &str = "/tmp";
610        #[cfg(windows)]
611        const ABS_DIR: &str = "C:\\tmp";
612
613        let env =
614            Environment::new_for_test("cargo openapi", ABS_DIR, "foo-openapi")
615                .unwrap()
616                .with_default_git_branch("upstream/dev".to_owned());
617        let env = env.resolve(None).unwrap();
618
619        // SAFETY:
620        // https://nexte.st/docs/configuration/env-vars/#altering-the-environment-within-tests
621        unsafe {
622            std::env::remove_var(BLESSED_FROM_VCS_ENV);
623            std::env::remove_var(BLESSED_FROM_VCS_PATH_ENV);
624            std::env::remove_var(BLESSED_FROM_GIT_ENV);
625        }
626
627        // OPENAPI_MGR_BLESSED_FROM_VCS overrides the default.
628        unsafe {
629            std::env::set_var(BLESSED_FROM_VCS_ENV, "env-trunk");
630        }
631        assert_eq!(
632            parse_blessed_source(&env, &[]),
633            BlessedSource::VcsRevisionMergeBase {
634                revision: VcsRevision::from("env-trunk".to_owned()),
635                directory: Utf8PathBuf::from("foo-openapi"),
636            },
637        );
638
639        // OPENAPI_MGR_BLESSED_FROM_VCS_PATH overrides the path.
640        unsafe {
641            std::env::set_var(BLESSED_FROM_VCS_ENV, "env-trunk");
642            std::env::set_var(BLESSED_FROM_VCS_PATH_ENV, "custom-dir");
643        }
644        assert_eq!(
645            parse_blessed_source(&env, &[]),
646            BlessedSource::VcsRevisionMergeBase {
647                revision: VcsRevision::from("env-trunk".to_owned()),
648                directory: Utf8PathBuf::from("custom-dir"),
649            },
650        );
651
652        // Clean up path env var for remaining tests.
653        unsafe {
654            std::env::remove_var(BLESSED_FROM_VCS_PATH_ENV);
655        }
656
657        // OPENAPI_MGR_BLESSED_FROM_GIT as legacy fallback.
658        unsafe {
659            std::env::remove_var(BLESSED_FROM_VCS_ENV);
660            std::env::set_var(BLESSED_FROM_GIT_ENV, "origin/dev");
661        }
662        assert_eq!(
663            parse_blessed_source(&env, &[]),
664            BlessedSource::VcsRevisionMergeBase {
665                revision: VcsRevision::from("origin/dev".to_owned()),
666                directory: Utf8PathBuf::from("foo-openapi"),
667            },
668        );
669
670        // Both env vars set: OPENAPI_MGR_BLESSED_FROM_VCS is preferred over
671        // OPENAPI_MGR_BLESSED_FROM_GIT.
672        unsafe {
673            std::env::set_var(BLESSED_FROM_VCS_ENV, "env-vcs");
674            std::env::set_var(BLESSED_FROM_GIT_ENV, "env-git");
675        }
676        assert_eq!(
677            parse_blessed_source(&env, &[]),
678            BlessedSource::VcsRevisionMergeBase {
679                revision: VcsRevision::from("env-vcs".to_owned()),
680                directory: Utf8PathBuf::from("foo-openapi"),
681            },
682        );
683
684        // CLI flag overrides both env vars.
685        assert_eq!(
686            parse_blessed_source(&env, &["--blessed-from-vcs", "cli-override"]),
687            BlessedSource::VcsRevisionMergeBase {
688                revision: VcsRevision::from("cli-override".to_owned()),
689                directory: Utf8PathBuf::from("foo-openapi"),
690            },
691        );
692    }
693}