clap_interface.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. use microrm::{
  2. prelude::*,
  3. schema::{
  4. datum::{ConcreteDatumList, Datum, DatumDiscriminator, DatumDiscriminatorRef},
  5. entity::{Entity, EntityID, EntityPartList, EntityPartVisitor, EntityVisitor},
  6. },
  7. };
  8. use super::{CLIError, CLIObject};
  9. use clap::{FromArgMatches, Subcommand};
  10. /// iterate across the list of key parts (E::Keys) and add args for each
  11. fn add_keys<E: Entity, IC: InterfaceCustomization>(
  12. mut cmd: clap::Command,
  13. role: ValueRole,
  14. ) -> clap::Command {
  15. struct UVisitor<'a, IC: InterfaceCustomization, E: Entity>(
  16. &'a mut clap::Command,
  17. ValueRole,
  18. std::marker::PhantomData<(IC, E)>,
  19. );
  20. impl<'a, IC: InterfaceCustomization, E: Entity> EntityPartVisitor for UVisitor<'a, IC, E> {
  21. type Entity = E;
  22. fn visit<EP: microrm::schema::entity::EntityPart>(&mut self) {
  23. if !IC::has_value_for(EP::Entity::entity_name(), EP::part_name(), self.1) {
  24. let arg = clap::Arg::new(EP::part_name())
  25. .required(true)
  26. .help(EP::desc());
  27. *self.0 = self.0.clone().arg(arg);
  28. }
  29. }
  30. }
  31. <E::Keys as EntityPartList>::accept_part_visitor(&mut UVisitor::<IC, E>(
  32. &mut cmd,
  33. role,
  34. Default::default(),
  35. ));
  36. cmd
  37. }
  38. fn collect_keys<E: Entity, IC: InterfaceCustomization>(
  39. matches: &clap::ArgMatches,
  40. role: ValueRole,
  41. ) -> Vec<EntityKey> {
  42. struct UVisitor<'a, IC: InterfaceCustomization, E: Entity>(
  43. &'a clap::ArgMatches,
  44. &'a mut Vec<EntityKey>,
  45. ValueRole,
  46. std::marker::PhantomData<(IC, E)>,
  47. );
  48. impl<'a, IC: InterfaceCustomization, E: Entity> EntityPartVisitor for UVisitor<'a, IC, E> {
  49. type Entity = E;
  50. fn visit<EP: microrm::schema::entity::EntityPart>(&mut self) {
  51. if !IC::has_value_for(EP::Entity::entity_name(), EP::part_name(), self.2) {
  52. self.1.push(EntityKey::UserInput(
  53. self.0
  54. .get_one::<std::string::String>(EP::part_name())
  55. .unwrap()
  56. .clone(),
  57. ));
  58. } else {
  59. self.1.push(EntityKey::Placeholder(
  60. EP::Entity::entity_name(),
  61. EP::part_name(),
  62. self.2,
  63. ));
  64. }
  65. }
  66. }
  67. let mut key_values = vec![];
  68. <E::Keys as EntityPartList>::accept_part_visitor(&mut UVisitor::<IC, E>(
  69. matches,
  70. &mut key_values,
  71. role,
  72. Default::default(),
  73. ));
  74. key_values
  75. }
  76. #[derive(Debug, Clone)]
  77. pub enum EntityKey {
  78. Placeholder(&'static str, &'static str, ValueRole),
  79. UserInput(String),
  80. }
  81. impl EntityKey {
  82. fn to_string_vec(vec: &[Self], ic: &impl InterfaceCustomization) -> Vec<String> {
  83. vec.iter()
  84. .map(|v| match v {
  85. EntityKey::UserInput(s) => s.to_owned(),
  86. EntityKey::Placeholder(entity, field, role) => ic.value_for(entity, field, *role),
  87. })
  88. .collect()
  89. }
  90. }
  91. #[derive(Clone, Debug)]
  92. pub enum InterfaceVerb<O: CLIObject> {
  93. Attach {
  94. local_keys: Vec<EntityKey>,
  95. relation: String,
  96. remote_keys: Vec<EntityKey>,
  97. },
  98. Create(O::CreateParameters),
  99. Delete(Vec<EntityKey>),
  100. Detach {
  101. local_keys: Vec<EntityKey>,
  102. relation: String,
  103. remote_keys: Vec<EntityKey>,
  104. },
  105. ListAll,
  106. Inspect(Vec<EntityKey>),
  107. Extra(O::ExtraCommands),
  108. }
  109. // helper alias for later
  110. type UniqueList<E> = <<E as Entity>::Keys as EntityPartList>::DatumList;
  111. impl<O: CLIObject> InterfaceVerb<O> {
  112. fn parse_attachment<IC: InterfaceCustomization>(
  113. matches: &clap::ArgMatches,
  114. ) -> Result<(Vec<EntityKey>, String, Vec<EntityKey>), clap::Error> {
  115. let local_keys = collect_keys::<O, IC>(matches, ValueRole::BaseTarget);
  116. let (subcommand, submatches) = matches
  117. .subcommand()
  118. .ok_or(clap::Error::new(clap::error::ErrorKind::MissingSubcommand))?;
  119. // find the relevant relation
  120. struct RelationFinder<'l, IC: InterfaceCustomization, E: Entity> {
  121. subcommand: &'l str,
  122. submatches: &'l clap::ArgMatches,
  123. keys: &'l mut Vec<EntityKey>,
  124. _ghost: std::marker::PhantomData<(IC, E)>,
  125. }
  126. impl<'l, IC: InterfaceCustomization, E: Entity> EntityPartVisitor for RelationFinder<'l, IC, E> {
  127. type Entity = E;
  128. fn visit<EP: microrm::schema::entity::EntityPart>(&mut self) {
  129. if EP::part_name() != self.subcommand {
  130. return;
  131. }
  132. println!("EP: {}", std::any::type_name::<EP>());
  133. // println!("EP: {}", std::any::
  134. EP::Datum::accept_entity_visitor(self);
  135. }
  136. }
  137. impl<'l, IC: InterfaceCustomization, OE: Entity> EntityVisitor for RelationFinder<'l, IC, OE> {
  138. fn visit<E: Entity>(&mut self) {
  139. println!("\trelationfinder visiting entity {}", E::entity_name());
  140. *self.keys = collect_keys::<E, IC>(self.submatches, ValueRole::AttachmentTarget);
  141. }
  142. }
  143. let mut remote_keys = vec![];
  144. O::accept_part_visitor(&mut RelationFinder::<IC, O> {
  145. subcommand,
  146. submatches,
  147. keys: &mut remote_keys,
  148. _ghost: Default::default(),
  149. });
  150. Ok((local_keys, subcommand.into(), remote_keys))
  151. }
  152. fn from_matches<IC: InterfaceCustomization>(
  153. parent_matches: &clap::ArgMatches,
  154. ) -> Result<Self, clap::Error> {
  155. let (subcommand, matches) = parent_matches
  156. .subcommand()
  157. .ok_or(clap::Error::new(clap::error::ErrorKind::MissingSubcommand))?;
  158. Ok(match subcommand {
  159. "attach" => {
  160. let (local_keys, relation, remote_keys) = Self::parse_attachment::<IC>(matches)?;
  161. InterfaceVerb::Attach {
  162. local_keys,
  163. relation,
  164. remote_keys,
  165. }
  166. }
  167. "create" => InterfaceVerb::Create(
  168. <O::CreateParameters as clap::FromArgMatches>::from_arg_matches(matches)?,
  169. ),
  170. "delete" => {
  171. InterfaceVerb::Delete(collect_keys::<O, IC>(matches, ValueRole::BaseTarget))
  172. }
  173. "detach" => {
  174. let (local_keys, relation, remote_keys) = Self::parse_attachment::<IC>(matches)?;
  175. InterfaceVerb::Detach {
  176. local_keys,
  177. relation,
  178. remote_keys,
  179. }
  180. }
  181. "list" => InterfaceVerb::ListAll,
  182. "inspect" => {
  183. InterfaceVerb::Inspect(collect_keys::<O, IC>(matches, ValueRole::BaseTarget))
  184. }
  185. cmd => {
  186. if <O::ExtraCommands>::has_subcommand(cmd) {
  187. InterfaceVerb::Extra(<O::ExtraCommands>::from_arg_matches(parent_matches)?)
  188. } else {
  189. unreachable!()
  190. }
  191. }
  192. })
  193. }
  194. }
  195. /// helper type for attach and detach verbs
  196. struct Attacher<'l, Error: CLIError, E: Entity> {
  197. do_attach: bool,
  198. relation: &'l str,
  199. remote_keys: Vec<String>,
  200. err: Option<Error>,
  201. _ghost: std::marker::PhantomData<E>,
  202. }
  203. impl<'l, Error: CLIError, OE: Entity> Attacher<'l, Error, OE> {
  204. fn do_operation<E: Entity>(&mut self, map: &impl AssocInterface<RemoteEntity = E>) {
  205. match map
  206. .query_all()
  207. .keyed(
  208. UniqueList::<E>::build_equivalent(self.remote_keys.iter().map(String::as_str))
  209. .unwrap(),
  210. )
  211. .get()
  212. {
  213. Ok(Some(obj)) => {
  214. if self.do_attach {
  215. self.err = map.connect_to(obj.id()).err().map(Into::into);
  216. } else {
  217. self.err = map.disconnect_from(obj.id()).err().map(Into::into);
  218. }
  219. }
  220. Ok(None) => {
  221. self.err = Some(Error::no_such_entity(
  222. E::entity_name(),
  223. self.remote_keys
  224. .iter()
  225. .cloned()
  226. .reduce(|a, b| format!("{},{}", a, b))
  227. .unwrap()
  228. .to_string(),
  229. ));
  230. }
  231. Err(e) => {
  232. self.err = Some(e.into());
  233. }
  234. }
  235. }
  236. }
  237. impl<'l, Error: CLIError, E: Entity> EntityPartVisitor for Attacher<'l, Error, E> {
  238. type Entity = E;
  239. fn visit_datum<EP: microrm::schema::entity::EntityPart>(&mut self, datum: &EP::Datum) {
  240. if EP::part_name() != self.relation {
  241. return;
  242. }
  243. datum.accept_discriminator_ref(self);
  244. }
  245. }
  246. impl<'l, Error: CLIError, OE: Entity> DatumDiscriminatorRef for Attacher<'l, Error, OE> {
  247. fn visit_entity_id<E: Entity>(&mut self, _: &E::ID) {
  248. unreachable!()
  249. }
  250. fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self, _: &T) {
  251. unreachable!()
  252. }
  253. fn visit_bare_field<T: Datum>(&mut self, _: &T) {
  254. unreachable!()
  255. }
  256. fn visit_assoc_map<E: Entity>(&mut self, map: &AssocMap<E>) {
  257. self.do_operation(map);
  258. }
  259. fn visit_assoc_range<R: microrm::schema::Relation>(
  260. &mut self,
  261. map: &microrm::schema::AssocRange<R>,
  262. ) {
  263. self.do_operation(map);
  264. }
  265. fn visit_assoc_domain<R: microrm::schema::Relation>(
  266. &mut self,
  267. map: &microrm::schema::AssocDomain<R>,
  268. ) {
  269. self.do_operation(map);
  270. }
  271. }
  272. /// Enumeration that describes the role of a value in an [`InterfaceCustomization`] instance.
  273. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
  274. pub enum ValueRole {
  275. /// This value is for the 'base' object, i.e. the first object type specified in a command.
  276. BaseTarget,
  277. /// This value is for the 'attachment' object, i.e. the second object type specified in a command.
  278. AttachmentTarget,
  279. }
  280. /// Allows customization of the autogenerated commands by programmatically providing values so they
  281. /// don't have to be specified on the command-line.
  282. pub trait InterfaceCustomization {
  283. /// Returns true iff this customization will provide a value for this field.
  284. fn has_value_for(entity: &'static str, field: &'static str, role: ValueRole) -> bool;
  285. /// Returns a string representation of the value for this field, equivalent to a value entered
  286. /// on the command-line.
  287. fn value_for(&self, entity: &'static str, field: &'static str, role: ValueRole) -> String;
  288. }
  289. impl InterfaceCustomization for () {
  290. fn has_value_for(_entity: &'static str, _field: &'static str, _role: ValueRole) -> bool {
  291. false
  292. }
  293. fn value_for(&self, _: &'static str, _: &'static str, _: ValueRole) -> String {
  294. unreachable!()
  295. }
  296. }
  297. #[derive(Debug)]
  298. /// Type implementing `Subcommand` for a given CLIObject and specialization.
  299. ///
  300. ///
  301. pub struct ClapInterface<O: CLIObject, IC: InterfaceCustomization> {
  302. verb: InterfaceVerb<O>,
  303. _ghost: std::marker::PhantomData<IC>,
  304. }
  305. impl<O: CLIObject, IC: InterfaceCustomization> ClapInterface<O, IC> {
  306. /// Execute the action that this ClapInterface instance describes. Note that if `Spec` is
  307. /// chosen as [`EmptyList`](../schema/entity/struct.EmptyList.html), the value passed for the
  308. /// `spec` parameter should be the unit type, `()`.
  309. pub fn perform(
  310. &self,
  311. data: &O::CommandData,
  312. ic: &IC,
  313. query_ctx: impl microrm::prelude::Queryable<EntityOutput = O>,
  314. insert_ctx: &impl microrm::prelude::Insertable<O>,
  315. ) -> Result<(), O::Error> {
  316. match &self.verb {
  317. InterfaceVerb::Attach {
  318. local_keys,
  319. relation,
  320. remote_keys,
  321. } => {
  322. let local_keys = EntityKey::to_string_vec(local_keys, ic);
  323. let remote_keys = EntityKey::to_string_vec(remote_keys, ic);
  324. let outer_obj = query_ctx
  325. .keyed(
  326. UniqueList::<O>::build_equivalent(local_keys.iter().map(String::as_str))
  327. .unwrap(),
  328. )
  329. .get()?
  330. .ok_or(<O::Error>::no_such_entity(
  331. O::entity_name(),
  332. local_keys
  333. .iter()
  334. .cloned()
  335. .reduce(|a, b| format!("{},{}", a, b))
  336. .unwrap()
  337. .to_string(),
  338. ))?;
  339. let mut attacher = Attacher {
  340. do_attach: true,
  341. relation,
  342. remote_keys,
  343. err: None,
  344. _ghost: Default::default(),
  345. };
  346. outer_obj.accept_part_visitor_ref(&mut attacher);
  347. if let Some(err) = attacher.err {
  348. return Err(err);
  349. }
  350. }
  351. InterfaceVerb::Create(params) => {
  352. insert_ctx.insert(O::create_from_params(data, params)?)?;
  353. }
  354. InterfaceVerb::Delete(keys) => {
  355. let keys = EntityKey::to_string_vec(keys, ic);
  356. query_ctx
  357. .keyed(
  358. UniqueList::<O>::build_equivalent(keys.iter().map(String::as_str)).unwrap(),
  359. )
  360. .delete()?;
  361. }
  362. InterfaceVerb::Detach {
  363. local_keys,
  364. relation,
  365. remote_keys,
  366. } => {
  367. let local_keys = EntityKey::to_string_vec(local_keys, ic);
  368. let remote_keys = EntityKey::to_string_vec(remote_keys, ic);
  369. let outer_obj = query_ctx
  370. .keyed(
  371. UniqueList::<O>::build_equivalent(local_keys.iter().map(String::as_str))
  372. .unwrap(),
  373. )
  374. .get()?
  375. .ok_or(<O::Error>::no_such_entity(
  376. O::entity_name(),
  377. local_keys
  378. .iter()
  379. .cloned()
  380. .reduce(|a, b| format!("{},{}", a, b))
  381. .unwrap()
  382. .to_string(),
  383. ))?;
  384. let mut attacher = Attacher {
  385. do_attach: false,
  386. relation,
  387. remote_keys,
  388. err: None,
  389. _ghost: Default::default(),
  390. };
  391. outer_obj.accept_part_visitor_ref(&mut attacher);
  392. if let Some(err) = attacher.err {
  393. return Err(err);
  394. }
  395. }
  396. InterfaceVerb::ListAll => {
  397. println!(
  398. "Listing all {}(s): ({})",
  399. O::entity_name(),
  400. query_ctx.clone().count()?
  401. );
  402. for obj in query_ctx.get()?.into_iter() {
  403. println!(" - {}", obj.shortname());
  404. }
  405. }
  406. InterfaceVerb::Inspect(keys) => {
  407. let keys = EntityKey::to_string_vec(keys, ic);
  408. let obj = query_ctx
  409. .keyed(
  410. UniqueList::<O>::build_equivalent(keys.iter().map(String::as_str)).unwrap(),
  411. )
  412. .get()?
  413. .ok_or(<O::Error>::no_such_entity(
  414. O::entity_name(),
  415. keys.iter()
  416. .cloned()
  417. .reduce(|a, b| format!("{},{}", a, b))
  418. .unwrap()
  419. .to_string(),
  420. ))?;
  421. println!("{:#?}", obj.as_ref());
  422. fn inspect_ai<AI: AssocInterface>(name: &'static str, ai: &AI) {
  423. println!("{}: ({})", name, ai.count().unwrap());
  424. for a in ai.get().expect("couldn't get object associations") {
  425. println!("[#{:3}]: {:?}", a.id().into_raw(), a.wrapped());
  426. }
  427. }
  428. struct AssocFieldWalker<E: Entity>(std::marker::PhantomData<E>);
  429. impl<E: Entity> EntityPartVisitor for AssocFieldWalker<E> {
  430. type Entity = E;
  431. fn visit_datum<EP: microrm::schema::entity::EntityPart>(
  432. &mut self,
  433. datum: &EP::Datum,
  434. ) {
  435. struct Discriminator(&'static str);
  436. impl DatumDiscriminatorRef for Discriminator {
  437. fn visit_serialized<
  438. T: serde::Serialize + serde::de::DeserializeOwned,
  439. >(
  440. &mut self,
  441. _: &T,
  442. ) {
  443. }
  444. fn visit_bare_field<T: Datum>(&mut self, _: &T) {}
  445. fn visit_entity_id<E: Entity>(&mut self, _: &E::ID) {}
  446. fn visit_assoc_map<E: Entity>(&mut self, amap: &AssocMap<E>) {
  447. inspect_ai(self.0, amap);
  448. }
  449. fn visit_assoc_domain<R: microrm::schema::Relation>(
  450. &mut self,
  451. adomain: &microrm::schema::AssocDomain<R>,
  452. ) {
  453. inspect_ai(self.0, adomain);
  454. }
  455. fn visit_assoc_range<R: microrm::schema::Relation>(
  456. &mut self,
  457. arange: &microrm::schema::AssocRange<R>,
  458. ) {
  459. inspect_ai(self.0, arange);
  460. }
  461. }
  462. datum.accept_discriminator_ref(&mut Discriminator(EP::part_name()));
  463. }
  464. }
  465. obj.accept_part_visitor_ref(&mut AssocFieldWalker(Default::default()));
  466. }
  467. InterfaceVerb::Extra(extra) => {
  468. O::run_extra_command(data, extra, query_ctx, insert_ctx)?;
  469. }
  470. }
  471. Ok(())
  472. }
  473. fn make_relation_subcommands() -> impl Iterator<Item = clap::Command> {
  474. let mut out = vec![];
  475. struct PartVisitor<'l, IC: InterfaceCustomization, E: Entity>(
  476. &'l mut Vec<clap::Command>,
  477. std::marker::PhantomData<(IC, E)>,
  478. );
  479. impl<'l, IC: InterfaceCustomization, E: Entity> EntityPartVisitor for PartVisitor<'l, IC, E> {
  480. type Entity = E;
  481. fn visit<EP: microrm::schema::entity::EntityPart>(&mut self) {
  482. struct Discriminator<'l, IC: InterfaceCustomization>(
  483. &'l mut Vec<clap::Command>,
  484. &'static str,
  485. std::marker::PhantomData<IC>,
  486. );
  487. impl<'l, IC: InterfaceCustomization> DatumDiscriminator for Discriminator<'l, IC> {
  488. fn visit_entity_id<E: Entity>(&mut self) {}
  489. fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(
  490. &mut self,
  491. ) {
  492. }
  493. fn visit_bare_field<T: Datum>(&mut self) {}
  494. fn visit_assoc_map<E: Entity>(&mut self) {
  495. self.0.push(add_keys::<E, IC>(
  496. clap::Command::new(self.1),
  497. ValueRole::AttachmentTarget,
  498. ));
  499. }
  500. fn visit_assoc_domain<R: microrm::schema::Relation>(&mut self) {
  501. self.0.push(add_keys::<R::Range, IC>(
  502. clap::Command::new(self.1),
  503. ValueRole::AttachmentTarget,
  504. ));
  505. }
  506. fn visit_assoc_range<R: microrm::schema::Relation>(&mut self) {
  507. self.0.push(add_keys::<R::Domain, IC>(
  508. clap::Command::new(self.1),
  509. ValueRole::AttachmentTarget,
  510. ));
  511. }
  512. }
  513. <EP::Datum as Datum>::accept_discriminator(&mut Discriminator::<IC>(
  514. self.0,
  515. EP::part_name(),
  516. Default::default(),
  517. ));
  518. }
  519. }
  520. O::accept_part_visitor(&mut PartVisitor::<IC, O>(&mut out, Default::default()));
  521. out.into_iter()
  522. }
  523. }
  524. impl<O: CLIObject, IC: InterfaceCustomization> FromArgMatches for ClapInterface<O, IC> {
  525. fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
  526. let verb = InterfaceVerb::from_matches::<IC>(matches);
  527. Ok(Self {
  528. verb: verb?,
  529. _ghost: Default::default(),
  530. })
  531. }
  532. fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
  533. todo!()
  534. }
  535. }
  536. impl<O: CLIObject, IC: InterfaceCustomization> Subcommand for ClapInterface<O, IC> {
  537. fn has_subcommand(_name: &str) -> bool {
  538. todo!()
  539. }
  540. fn augment_subcommands(cmd: clap::Command) -> clap::Command {
  541. let cmd = cmd
  542. .subcommand(
  543. add_keys::<O, IC>(clap::Command::new("attach"), ValueRole::BaseTarget)
  544. .subcommands(Self::make_relation_subcommands())
  545. .subcommand_required(true),
  546. )
  547. .subcommand(
  548. add_keys::<O, IC>(clap::Command::new("detach"), ValueRole::BaseTarget)
  549. .subcommands(Self::make_relation_subcommands())
  550. .subcommand_required(true),
  551. )
  552. .subcommand(<O::CreateParameters as clap::CommandFactory>::command().name("create"))
  553. .subcommand(add_keys::<O, IC>(
  554. clap::Command::new("delete"),
  555. ValueRole::BaseTarget,
  556. ))
  557. .subcommand(add_keys::<O, IC>(
  558. clap::Command::new("inspect"),
  559. ValueRole::BaseTarget,
  560. ))
  561. .subcommand(clap::Command::new("list"));
  562. <O::ExtraCommands>::augment_subcommands(cmd)
  563. }
  564. fn augment_subcommands_for_update(_cmd: clap::Command) -> clap::Command {
  565. todo!()
  566. }
  567. }