cargo_mobile2/config/
mod.rs1pub mod app;
2pub mod metadata;
3mod raw;
4pub use raw::Raw;
5
6use self::{app::App, raw::*};
7#[cfg(target_os = "macos")]
8use crate::apple;
9use crate::{
10 android, bicycle, templating,
11 util::cli::{Report, Reportable, TextWrapper},
12};
13use serde::Serialize;
14use std::{
15 fmt::Debug,
16 io,
17 path::{Path, PathBuf},
18};
19use thiserror::Error;
20
21pub fn file_name() -> String {
22 format!("{}.toml", crate::NAME)
23}
24
25#[derive(Debug, Error)]
26pub enum FromRawError {
27 #[error(transparent)]
28 AppConfigInvalid(app::Error),
29 #[cfg(target_os = "macos")]
30 #[error(transparent)]
31 AppleConfigInvalid(apple::config::Error),
32 #[error(transparent)]
33 AndroidConfigInvalid(android::config::Error),
34}
35
36impl FromRawError {
37 pub fn report(&self, msg: &str) -> Report {
38 match self {
39 Self::AppConfigInvalid(err) => err.report(msg),
40 #[cfg(target_os = "macos")]
41 Self::AppleConfigInvalid(err) => err.report(msg),
42 Self::AndroidConfigInvalid(err) => err.report(msg),
43 }
44 }
45}
46
47#[derive(Debug, Error)]
48pub enum GenError {
49 #[error(transparent)]
50 PromptFailed(PromptError),
51 #[error(transparent)]
52 DetectFailed(DetectError),
53 #[error("Failed to canonicalize root dir: {0}")]
54 CanonicalizeFailed(io::Error),
55 #[error(transparent)]
56 FromRawFailed(FromRawError),
57 #[error(transparent)]
58 WriteFailed(WriteError),
59}
60
61impl Reportable for GenError {
62 fn report(&self) -> Report {
63 Report::error("Failed to generate config", self)
64 }
65}
66
67#[derive(Debug, Error)]
68pub enum LoadOrGenError {
69 #[error("Failed to load config: {0}")]
70 LoadFailed(LoadError),
71 #[error("Config file at {path} invalid: {cause}")]
72 FromRawFailed { path: PathBuf, cause: FromRawError },
73 #[error(transparent)]
74 GenFailed(GenError),
75}
76
77impl Reportable for LoadOrGenError {
78 fn report(&self) -> Report {
79 Report::error("Config error", self)
80 }
81}
82
83#[derive(Clone, Copy, Debug)]
84pub enum Origin {
85 FreshlyMinted,
86 Loaded,
87}
88
89impl Origin {
90 pub fn freshly_minted(self) -> bool {
91 matches!(self, Self::FreshlyMinted)
92 }
93}
94
95#[derive(Debug, Serialize)]
96#[serde(rename_all = "kebab-case")]
97pub struct Config {
98 app: App,
99 #[cfg(target_os = "macos")]
100 apple: apple::config::Config,
101 android: android::config::Config,
102}
103
104impl Config {
105 pub fn from_raw(root_dir: PathBuf, raw: Raw) -> Result<Self, FromRawError> {
106 let app = App::from_raw(root_dir, raw.app).map_err(FromRawError::AppConfigInvalid)?;
107 #[cfg(target_os = "macos")]
108 let apple = apple::config::Config::from_raw(app.clone(), raw.apple)
109 .map_err(FromRawError::AppleConfigInvalid)?;
110 let android = android::config::Config::from_raw(app.clone(), raw.android)
111 .map_err(FromRawError::AndroidConfigInvalid)?;
112 Ok(Self {
113 app,
114 #[cfg(target_os = "macos")]
115 apple,
116 android,
117 })
118 }
119
120 fn gen(
121 cwd: impl AsRef<Path>,
122 non_interactive: bool,
123 wrapper: &TextWrapper,
124 ) -> Result<Self, GenError> {
125 let raw = if !non_interactive {
126 Raw::prompt(wrapper).map_err(GenError::PromptFailed)
127 } else {
128 Raw::detect(wrapper).map_err(GenError::DetectFailed)
129 }?;
130 let root_dir = cwd
131 .as_ref()
132 .canonicalize()
133 .map_err(GenError::CanonicalizeFailed)?;
134 let config =
135 Self::from_raw(root_dir.clone(), raw.clone()).map_err(GenError::FromRawFailed)?;
136 log::info!("generated config: {:#?}", config);
137 raw.write(&root_dir).map_err(GenError::WriteFailed)?;
138 Ok(config)
139 }
140
141 pub fn load_or_gen(
142 cwd: impl AsRef<Path>,
143 non_interactive: bool,
144 wrapper: &TextWrapper,
145 ) -> Result<(Self, Origin), LoadOrGenError> {
146 let cwd = cwd.as_ref();
147 if let Some((root_dir, raw)) = Raw::load(cwd).map_err(LoadOrGenError::LoadFailed)? {
148 Self::from_raw(root_dir.clone(), raw)
149 .map(|config| (config, Origin::Loaded))
150 .map_err(|cause| LoadOrGenError::FromRawFailed {
151 path: root_dir,
152 cause,
153 })
154 } else {
155 Self::gen(cwd, non_interactive, wrapper)
156 .map(|config| (config, Origin::FreshlyMinted))
157 .map_err(LoadOrGenError::GenFailed)
158 }
159 }
160
161 pub fn path(&self) -> PathBuf {
162 self.app().root_dir().join(file_name())
163 }
164
165 pub fn app(&self) -> &App {
166 &self.app
167 }
168
169 #[cfg(target_os = "macos")]
170 pub fn apple(&self) -> &apple::config::Config {
171 &self.apple
172 }
173
174 pub fn android(&self) -> &android::config::Config {
175 &self.android
176 }
177
178 pub fn build_a_bike(&self) -> bicycle::Bicycle {
179 templating::init(Some(self))
180 }
181}