1use anyhow::{Context as _, Result};
12use clap::{
13 Parser as _,
14 error::{ContextKind, ContextValue},
15};
16use serde::Deserialize;
17use std::ffi::OsString;
18use std::path::Path;
19
20mod arg;
22mod settings;
24mod subcommand;
26mod version;
28
29use arg::{Cli, Commands};
30
31#[derive(Debug, Clone, Default, Deserialize)]
32struct Options {
36 #[expect(dead_code, reason = "not implemented yet")]
37 val1: String,
39}
40
41#[expect(clippy::single_call_fn, reason = "better readability")]
43fn read_file(path: &Path) -> Result<Options> {
44 let _content =
45 fs_err::read_to_string(path).context(format!("Failed to read file: {}", path.display()))?;
46 let options: Options = Options::default();
47 Ok(options)
48}
49
50#[derive(Debug, Clone)]
51#[expect(dead_code, reason = "not implemented yet")]
54struct FilesystemOptions(Options);
55
56impl FilesystemOptions {
57 #[expect(clippy::single_call_fn, reason = "better readability")]
59 pub fn from_file(file: &Path) -> Result<Self> {
60 let options: Options = read_file(file)?;
61 Ok(Self(options))
62 }
63}
64
65#[inline]
84pub fn run<I, T>(args: I) -> Result<()>
85where
86 I: IntoIterator<Item = T>,
87 T: Into<OsString> + Clone,
88{
89 let cli = match Cli::try_parse_from(args) {
90 Ok(cli) => cli,
91 Err(mut err) => {
92 #[expect(clippy::single_match, reason = "better readability")]
93 #[expect(clippy::pattern_type_mismatch, reason = "I don't know")]
94 match err.get(ContextKind::InvalidSubcommand) {
95 Some(ContextValue::String(subcommand)) => match subcommand.as_str() {
96 "help" => {
97 err.insert(
98 ContextKind::InvalidSubcommand,
99 ContextValue::String("help".to_owned()),
100 );
101 }
102 "module" => {
103 err.insert(
104 ContextKind::InvalidSubcommand,
105 ContextValue::String("module".to_owned()),
106 );
107 }
108 _ => {}
109 },
110 _ => {}
111 }
112 err.exit()
113 }
114 };
115
116 let filesystem = if let Some(config_file) = cli.top_level.config_file.as_ref() {
117 Some(FilesystemOptions::from_file(config_file)?)
118 } else {
119 None
120 };
121
122 #[expect(clippy::todo, reason = "implement later")]
123 if let Some(_filesystem) = filesystem {
124 todo!("TODO: read as global settings");
125 }
126
127 let _global_settings = settings::GlobalSettings::resolve(&cli.top_level.global_args);
129
130 let result = match *cli.command {
131 #[expect(clippy::todo, reason = "implement later")]
132 Commands::Help(_help_args) => {
133 todo!("TODO: implement help command");
134 }
135 Commands::Module(module_args) => subcommand::module::module(module_args),
136 };
137 result.context("Failed to run command")
138}