1use crate as eg;
3use eg::common::auth;
4use eg::date;
5use eg::db::DatabaseConnection;
6use eg::init;
7use eg::Editor;
8use eg::EgResult;
9
10const HELP_TEXT: &str = "
11Runner Additions:
12
13 --help
14 Show help text
15
16 --staff-account
17 ID of the account to use where a staff account would typically
18 be used, e.g. setting the last editor on a bib record.
19
20 --staff-workstation
21 Name of the staff login workstation. See --staff-acount. Optional.
22
23 --announce
24 Log calls to announce() to STDOUT in addition to log::info!().
25
26Database Connector Additions:
27
28 Parameters supported when Runner is started with a database connection.
29
30 --db-host
31 --db-port
32 --db-user
33 --db-name
34";
35
36const DEFAULT_STAFF_ACCOUNT: i64 = 1;
38
39pub struct Options {
40 pub options: Option<getopts::Options>,
42
43 pub with_evergreen: bool,
46
47 pub with_database: bool,
50
51 pub help_text: Option<String>,
53
54 pub extra_params: Option<Vec<String>>,
58}
59
60#[derive(Debug, Clone)]
65pub struct RunnerCore {
66 staff_account: i64,
67 staff_workstation: Option<String>,
68 authtoken: Option<String>,
69 params: getopts::Matches,
70 log_prefix: Option<String>,
71 announce: bool,
72}
73
74pub struct Runner {
76 core: RunnerCore,
77 editor: Option<Editor>,
78 db: Option<DatabaseConnection>,
79}
80
81impl From<RunnerCore> for Runner {
82 fn from(core: RunnerCore) -> Self {
83 Runner {
84 core,
85 editor: None,
86 db: None,
87 }
88 }
89}
90
91impl Runner {
92 pub fn init(mut options: Options) -> EgResult<Option<Runner>> {
99 let mut ops_binding = None;
100
101 let ops = options.options.as_mut().unwrap_or_else(|| {
102 ops_binding = Some(getopts::Options::new());
103 ops_binding.as_mut().unwrap()
104 });
105
106 ops.optflag("h", "help", "");
107 ops.optflag("", "announce", "");
108 ops.optopt("", "staff-account", "", "");
109 ops.optopt("", "staff-workstation", "", "");
110
111 if options.with_database {
112 DatabaseConnection::append_options(ops);
114 }
115
116 let mut args: Vec<String> = std::env::args().collect();
117
118 if let Some(extras) = options.extra_params.as_mut() {
119 args.append(extras);
120 }
121
122 let params = ops
123 .parse(&args[1..])
124 .map_err(|e| format!("Error parsing options: {e}"))?;
125
126 if params.opt_present("help") {
127 println!(
128 "{}\n{}",
129 options
130 .help_text
131 .unwrap_or("No Application Help Text Provided".to_string()),
132 HELP_TEXT
133 );
134 return Ok(None);
135 }
136
137 let sa = DEFAULT_STAFF_ACCOUNT.to_string();
138 let staff_account = params.opt_get_default("staff-account", sa).unwrap();
139 let staff_account = staff_account
140 .parse::<i64>()
141 .map_err(|e| format!("Error parsing staff-account value: {e}"))?;
142
143 let staff_workstation = params.opt_str("staff-workstation").map(|v| v.to_string());
144 let announce = params.opt_present("announce");
145
146 let mut runner = Runner {
147 db: None,
148 editor: None,
149 core: RunnerCore {
150 params,
151 staff_account,
152 staff_workstation,
153 announce,
154 authtoken: None,
155 log_prefix: None,
156 },
157 };
158
159 if options.with_database {
160 runner.connect_db()?;
161 }
162
163 if options.with_evergreen {
164 let client = init::init()?;
168 runner.editor = Some(eg::Editor::new(&client));
169 }
170
171 Ok(Some(runner))
172 }
173
174 pub fn connect_db(&mut self) -> EgResult<()> {
176 let mut db = DatabaseConnection::new_from_options(self.params());
177 db.connect()?;
178 self.db = Some(db);
179 Ok(())
180 }
181
182 pub fn connect_evergreen(&mut self) -> EgResult<()> {
187 let client = eg::Client::connect()?;
188 self.editor = Some(eg::Editor::new(&client));
189 Ok(())
190 }
191
192 pub fn core(&self) -> &RunnerCore {
194 &self.core
195 }
196
197 pub fn announce(&self, msg: &str) {
202 let pfx = self.core.log_prefix.as_deref().unwrap_or("");
203 if self.core.announce {
204 println!("{} {pfx}{msg}", date::now().format("%F %T%.3f"));
205 }
206 log::info!("{pfx}{msg}");
207 }
208
209 pub fn set_log_prefix(&mut self, p: &str) {
213 self.core.log_prefix = Some(p.to_string() + " ");
214 }
215
216 pub fn set_editor(&mut self, e: Editor) {
222 self.editor = Some(e);
223 }
224
225 pub fn staff_account(&self) -> i64 {
227 self.core.staff_account
228 }
229
230 pub fn editor_mut(&mut self) -> &mut Editor {
237 self.editor.as_mut().unwrap()
238 }
239
240 pub fn editor(&self) -> &Editor {
247 self.editor.as_ref().unwrap()
248 }
249
250 pub fn params(&self) -> &getopts::Matches {
252 &self.core.params
253 }
254
255 pub fn authtoken(&self) -> &str {
261 self.core.authtoken.as_deref().unwrap()
262 }
263
264 pub fn db(&mut self) -> &mut DatabaseConnection {
271 self.db
272 .as_mut()
273 .expect("database connection should be established")
274 }
275
276 pub fn login_staff(&mut self) -> EgResult<String> {
282 let mut args =
283 auth::InternalLoginArgs::new(self.core.staff_account, auth::LoginType::Staff);
284
285 if let Some(ws) = self.core.staff_workstation.as_ref() {
286 args.set_workstation(ws);
287 }
288
289 let ses = auth::Session::internal_session_api(self.editor_mut().client_mut(), &args)?;
290
291 if let Some(s) = ses {
292 self.editor_mut().apply_authtoken(s.token())?;
293 self.core.authtoken = Some(s.token().to_string());
294 Ok(s.token().to_string())
295 } else {
296 Err("Could not retrieve auth session".into())
297 }
298 }
299}